여러 페이지에서 모든 주문 유지하기

MySQLBeginner
지금 연습하기

소개

동료가 이미 매장의 첫 페이지 조회 두 개를 튜닝했습니다. 픽업 대기열은 오래된 순서이고 고객 이력은 최신순입니다. 이제 호출자는 첫 주문 일곱 건을 넘어 계속 조회해야 합니다. 실제 주문 여러 건의 타임스탬프가 같으므로 타임스탬프만의 커서는 레코드를 잃거나 반복할 수 있습니다.

이 독립 단원에는 자체 MySQL 스냅샷, 동작하는 첫 페이지 queue.sql과 history.sql, 적용된 인덱스와 indexes.sql이 제공됩니다. 이전 VM의 파일은 필요하지 않습니다. 효율적인 인덱스 접근을 유지하면서 모든 원래 주문이 완전한 오름차순·내림차순 페이지 순회에 남도록 두 저장 쿼리를 편집합니다.

첫 단원의 MySQL 매개변수, 반개구간 경계, 두 열 정렬, 진단 계획이 선수 지식입니다. NULL일 수 있는 커서, 완전한 비교 조건, 깊은 페이지 검사는 여기서 배웁니다. Terminal과 /home/labex/project/order-desk를 사용하세요. queue.sql과 history.sql만 변경하세요. 제공된 인덱스/인덱스 원본, 모든 주문 사실, 열, 기본 키, 실행 도구를 보존하세요.

각 페이지에는 여전히 실제 주문이 최대 일곱 건 들어가고 MySQL 자체 진단 행 요청을 최대 64회 사용합니다. 준비된 연결은 이 통제된 비교를 위해 인덱스 조건 푸시다운을 끈 상태를 유지합니다. 그 설정을 유지하고 옵티마이저 힌트는 사용하지 마세요. 요청은 고정된 데이터 스냅샷을 사용합니다. 동시 갱신 스냅샷, 물리적 I/O 횟수, 운영 지연 시간 보장은 주장하지 않습니다. 실행 환경의 표시 이름과 출력은 영어로 유지됩니다.

두 키를 다음 픽업 페이지로 전달하기

이 단계에서는 같은 타임스탬프에 남은 주문을 잃지 않으면서 오래된 순서의 대기열을 진행합니다.

일반 셸의 Terminal에서 시작하세요. 제공된 db와 desk는 첫 단원에서 연습한 것과 같은 인터페이스입니다. 경로나 파일 이름이 틀렸다면 ls로 실제 이름을 확인하고 원래 행을 바꾸지 않은 채 다시 시도하세요. 여기서 cat은 저장된 쿼리 텍스트를 출력합니다. 다음 page 요청은 실제 출력을 읽으며 기준 쿼리가 누락된 행을 고쳐 주지 않습니다.

cd /home/labex/project/order-desk
cat queue.sql
./desk page queue

저장 쿼리에는 유용한 매장/상태/시간 조건과 (placed_at,order_id) 정렬이 있지만 커서 조건은 없습니다. 첫 페이지는 올바르게 900001부터 900007까지 반환합니다. 데스크의 cursor_for_next_request에는 실제 마지막 행의 시간과 ID가 들어 있습니다. 다음 요청을 어디서 시작할 수 있는지 알려 주며, 다음 페이지가 비어 있지 않다고 약속하지는 않습니다.

--after는 실제 마지막 타임스탬프와 실제 마지막 주문 ID라는 두 인수를 받습니다. 따옴표는 타임스탬프의 공백을 셸 인수 하나 안에 유지합니다. 방금 본 커서로 다음 페이지를 요청하세요:

./desk page queue --after "2025-05-10 09:15:00.000000" 900007

변경하지 않은 시작 쿼리는 커서를 무시하므로 첫 ID 일곱 개를 다시 반환합니다. 모든 명령은 성공적으로 실행되었지만 잘못된 다음 페이지입니다. 문제를 숨기려고 원본 타임스탬프를 바꾸거나 표시 리스트를 중복 제거하지 마세요.

데스크는 MySQL 자체 세션 변수 두 개인 @after_at과 @after_id를 추가로 제공합니다. 첫 페이지에서 둘 다 NULL이며 이전 커서가 없다는 뜻입니다. SQL은 이 부재를 = NULL이 아니라 IS NULL로 검사합니다. 그 외에는 오래된 순서의 다음 행이 더 늦은 타임스탬프를 가지거나, 또는 같은 타임스탬프에 더 큰 ID를 가져야 합니다. placed_at > @after_at만 사용하면 반복된 시각의 ID 900008과 900009를 잃습니다.

기존 그룹, 상태, 반개구간 경계를 유지하세요. 커서 선택 조건을 괄호 안에 넣고 다른 필터에 AND로 연결하세요. OR 때문에 다른 업무 조건을 벗어난 행이 허용되어서는 안 됩니다. 두 키 모두 오름차순을 유지합니다. cat > queue.sql은 아래 전체 쿼리로 덮어씁니다. 따옴표로 지정한 SQL here-document 표시는 셸 확장을 막으며 별도 한 줄에서 닫힙니다.

cat > queue.sql <<'SQL'
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE shop_id=@group_id AND state=@state
  AND placed_at>=@from_at AND placed_at<@until_at
  AND (@after_at IS NULL
       OR placed_at>@after_at
       OR (placed_at=@after_at AND order_id>@after_id))
ORDER BY placed_at ASC,order_id ASC
LIMIT 7;
SQL
./desk page queue
./desk page queue --after "2025-05-10 09:15:00.000000" 900007
./desk plan queue --after "2025-05-10 09:15:00.000000" 900007

첫 페이지는 그대로입니다. 이제 다음 페이지에는 원래 시간과 센트를 가진 900008, 900009, 900021이 들어 있습니다. 정확한 다음 자정은 여전히 요청일 밖이며 마지막 마이크로초는 안에 남습니다. 계획은 모든 이전 주문을 읽고 버리는 대신 커서의 인덱스 범위에 도달해야 합니다. 실제 행과 진단 횟수를 함께 살펴보세요.

walk 동작은 실제 마지막 행의 커서를 전달하며 진짜 빈 페이지가 나타날 때까지 같은 저장 쿼리를 반복 실행합니다. 반환 순서와 필드를 그대로 유지하며 결과를 정렬, 중복 제거, 보충하지 않습니다. 쿼리가 마지막 커서를 반복하면 도구는 부분 순회를 완료로 보여 주는 대신 오류를 보고합니다.

./desk walk queue
./desk page queue --after "2025-05-10 23:59:59.999999" 900021

순회는 주문 열 건을 정확히 한 번씩 전달합니다. 일곱 건, 세 건, 빈 페이지 순서입니다. 마지막 행 커서를 명시해도 빈 행 리스트를 반환합니다. 이는 Order desk error와 달리 유효한 끝점입니다. 파일이나 SQL이 틀렸다면 ls나 cat으로 확인하고 저장 쿼리를 수정한 뒤 실제 요청을 다시 실행하세요. 제공된 인덱스나 원래 행을 변경하지 마세요. 전체 대기열을 확인한 뒤 단계 검사를 실행하세요.

제한된 작업량으로 최신순 이력 이어가기

이 단계에서는 반대 커서 방향을 완성하고 깊은 페이지를 같은 데이터의 오프셋 기반 읽기와 비교합니다.

계속 Terminal과 /home/labex/project/order-desk를 사용하세요. 수정한 픽업 쿼리와 준비된 인덱스/인덱스 원본을 유지하세요. 고객 이력은 시간과 ID 모두 DESC를 사용하므로 다음 행은 더 이른 시간, 또는 같은 시간에 더 작은 ID를 가집니다. 고객 필터와 반개구간 범위도 계속 적용됩니다. 첫 페이지 NULL 경우는 그대로입니다.

전체 이력 쿼리를 덮어쓰세요. 준비 상태 제한은 추가하지 마세요. 고객 이력에는 그 고객의 모든 원래 상태와 매장이 포함됩니다.

cat > history.sql <<'SQL'
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE customer_id=@actor_id
  AND placed_at>=@from_at AND placed_at<@until_at
  AND (@after_at IS NULL
       OR placed_at<@after_at
       OR (placed_at=@after_at AND order_id<@after_id))
ORDER BY placed_at DESC,order_id DESC
LIMIT 7;
SQL
./desk page history
./desk page history --after "2025-05-10 09:15:00.000000" 900004
./desk walk history

첫 페이지는 900021로 시작하고 900009부터 900004까지 이어집니다. 다음 페이지에는 900003, 900002, 900001이 들어 있습니다. 전체 순회는 주문 열 건을 모두 유지한 다음 실제 빈 페이지에 도달합니다. 0센트 주문은 첫 페이지에 남습니다. 시간이나 가격이 같아도 식별 정보를 합치지 않습니다.

깊은 OFFSET은 MySQL에 정렬 후 앞선 행들을 버리도록 요청합니다. 올바른 일곱 행을 반환하면서 훨씬 많은 행을 읽을 수 있습니다. 대신 커서는 실제 마지막 키 근처에서 시작할 수 있습니다. 제외할 끝점이 5월 1일인 고객 77의 1월부터 4월 이력에서 둘을 비교하세요.

아래 MySQL 클라이언트에서 LIMIT 1 OFFSET 199는 정렬된 199행을 건너뛰고 200번째 행을 반환합니다. OFFSET 200 페이지 바로 앞의 마지막 행입니다. EXPLAIN ANALYZE는 오프셋 쿼리를 실제로 실행하고 반복자 작업량을 보고합니다. 마지막 SELECT는 그 페이지의 실제 행을 표시합니다. here-document와 두 정렬 방향은 이미 연습한 것과 같습니다.

./db <<'SQL'
SELECT order_id,placed_at
FROM orders
WHERE customer_id=77
  AND placed_at>='2025-01-01' AND placed_at<'2025-05-01'
ORDER BY placed_at DESC,order_id DESC
LIMIT 1 OFFSET 199;
EXPLAIN ANALYZE FORMAT=TREE
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE customer_id=77
  AND placed_at>='2025-01-01' AND placed_at<'2025-05-01'
ORDER BY placed_at DESC,order_id DESC
LIMIT 7 OFFSET 200;
SELECT order_id,shop_id AS group_id,customer_id AS actor_id,
       state,placed_at AS event_at,total_cents AS amount_cents
FROM orders
WHERE customer_id=77
  AND placed_at>='2025-01-01' AND placed_at<'2025-05-01'
ORDER BY placed_at DESC,order_id DESC
LIMIT 7 OFFSET 200;
SQL

200번째 행은 2025-02-25 02:30:00.000000의 주문 94263입니다. 하위 반복자의 실제 행을 읽으세요. 오프셋 요청은 일곱 행을 전달하기 전에 앞의 200행을 지나야 합니다. 이제 같은 고객과 경계에서 그 실제 행을 저장 쿼리의 커서로 사용하세요. --from과 --until은 이번 호출에만 적용됩니다.

./desk page history --from 2025-01-01 --until 2025-05-01 --after "2025-02-25 02:30:00.000000" 94263
./desk plan history --from 2025-01-01 --until 2025-05-01 --after "2025-02-25 02:30:00.000000" 94263

3542와 87067로 시작하는 같은 ID 일곱 개가 나타납니다. 커서 쿼리는 이전 200행을 버리는 대신 적은 실제 요청 횟수로 인덱스 범위에 도달해야 합니다. 무관한 두 요청이 아니라 같은 페이지의 작업량을 비교하세요. 경과 밀리초만으로는 여전히 완료 기준을 입증하지 못합니다.

마지막으로 다른 고객, 유효한 빈 범위, 변경되지 않은 픽업 순회를 확인하세요:

./desk page history --actor 78
./desk page history --from 2027-01-01 --until 2027-01-02
./desk walk queue

재사용 가능한 SQL 파일 두 개를 유지하세요. 제공된 인덱스와 indexes.sql은 그대로 남아야 합니다. 쿼리의 SQL/파일/연결 오류는 오류로 남으며 읽기 도구가 빠진 주문을 만들어 내지 않습니다. 이는 준비된 고정 스냅샷의 안정적인 순회이며 동시 변경 중의 트랜잭션 스냅샷이 아닙니다. 최종 검사는 원본과 도구의 보존과 함께 정확한 원래 필드/순서, 짧은 전체 순회, 변경된 식별자/범위/커서, 진단 작업량을 검증합니다.

요약

완전한 타임스탬프·ID 커서로 인덱스를 사용하는 두 주문 조회를 계속 진행하고, 같은 시각의 모든 주문과 0센트 값을 보존하며, 실제 빈 끝 페이지를 검증했습니다. 밀리초를 증거로 삼지 않으면서 같은 깊은 페이지를 OFFSET과 인덱스 커서 요청으로 비교했습니다. 원본 데이터, 도구, 준비된 인덱스를 그대로 유지한 고정 스냅샷에서 두 방향이 모두 동작합니다.