시퀀스
고친 사람 github-actions[bot]
시퀀스는 여럿을 한 줄로 세워 앞뒤를 정해 줍니다. 그런데 무엇을 줄 세우느냐에 따라 전혀 다른 물건을 가리킵니다. 파이썬에서는 번호로 값을 꺼내 쓰는 모음입니다. 데이터베이스에서는 겹치지 않는 번호를 내주는 장치입니다.
쉽고 빠른 이해
무슨 일을 하는 물건인가 — 차례를 뜻으로 삼는 이름입니다. 사과 · 포도 · 귤을 이 차례로 담은 리스트도 시퀀스입니다. 주문 번호를 1 · 2 · 3 으로 내주는 데이터베이스 장치도 시퀀스입니다. 네트워크로 보내는 데이터에 붙는 번호도 시퀀스라고 부릅니다. 리눅스 커널이 쓰기 횟수를 세어 두는 숫자도 시퀀스입니다. 앞뒤가 정해져 있다는 점만 같습니다.
왜 이렇게 하나 — 값의 모음은 몇 번째인지로 값을 꺼내야 해서입니다. 번호를 내주는 장치는 여러 곳에서 동시에 행을 넣어도 번호가 겹치지 않아야 해서입니다. 데이터에 붙는 번호는 받는 쪽이 뒤바뀐 것과 빠진 것을 알아채야 해서입니다.
어떻게 도나
- 값의 모음은 맨 앞을 0번으로 세어 번호를 붙여 둡니다. 번호를 대면 그 값을 건넵니다
- 번호를 내주는 장치는 부를 때마다 다음 수를 하나 건넵니다. 여러 곳이 같은 순간에 불러도 서로 다른 수를 받습니다
- 데이터에 붙는 번호는 보낸 만큼 올라갑니다. 받는 쪽은 번호를 보고 차례를 다시 맞춥니다
대가 — 번호를 내주는 장치는 서로 다른 번호만 약속합니다. 취소된 작업이 받아 간 번호는 돌아오지 않아 중간에 빈 번호가 생깁니다. 번호가 끊기면 안 되는 곳에는 쓰지 않습니다.
상세
「시퀀스」라는 한 낱말이 서로 다른 물건 넷을 가리킵니다. 넷을 나란히 놓고 같은 점과 갈리는 점을 봅니다.
같은 것은 차례입니다. 넷 모두 앞뒤가 정해져 있습니다. 그 차례 자체가 뜻을 집니다.
갈리는 것은 줄을 서는 물건입니다. 명사의 종류부터 다릅니다.
| 맥락 | 시퀀스는 무엇인가 | 무엇이 줄을 서나 |
|---|---|---|
| 파이썬 | 값을 담아 두는 모음 | 값 |
| 관계형 데이터베이스 | 부를 때마다 다음 정수를 내주는 장치 | 장치가 내준 번호 |
| 네트워크 프로토콜 | 데이터에 하나씩 올려 붙이는 번호 | 바이트 · 패킷 |
| 리눅스 커널 | 쓰기가 있을 때마다 올리는 카운터 값 | 쓰기 |
이 차이를 모르고 읽으면 엉뚱한 성질을 기대하게 됩니다. 값의 모음에는 「번호가 빈다」는 성질이 없습니다. 번호를 내주는 장치에는 「3번째 값을 꺼낸다」는 동작이 없습니다.
이름만 빌린 두 용어
「시퀀스」가 붙었지만 위 넷 어느 것도 아닌 용어가 둘 있습니다.
시퀀스 다이어그램은 UML(Unified Modeling Language, 통합 모델링 언어)이 정한 그림의 한 갈래입니다. UML 의 상호작용 그림에서 가장 눈에 띄는 것은 참여자 사이에 오가는 메시지입니다. 그 메시지의 차례가 상황을 이해하는 데 중요하다고 봅니다.
시퀀스 다이어그램은 그 상호작용 그림 가운데 가장 흔한 갈래입니다. 참여자 여럿 사이의 메시지 주고받기에 초점을 둡니다. 줄을 서는 것은 값도 번호도 아닌 메시지입니다.
시퀀셜 스캔은 데이터베이스가 테이블을 읽는 방법 하나입니다. 테이블의 행을 전부 훑습니다. 실행 계획에는 Seq Scan 으로 찍힙니다.
조건절이 없어 모든 행을 봐야 하는 조회에서 이 방법이 골라집니다. 인덱스 스캔과 나란히 놓이는 읽기 방법의 이름입니다. 위 네 물건 어느 것도 아닙니다.
맥락별 뜻
네 맥락에서 시퀀스가 무엇을 가리키는지와 그 뜻을 정한 문서를 나란히 놓습니다.
| 맥락 | 뜻 | 출처 |
|---|---|---|
| 파이썬 | 정수 번호로 원소를 효율적으로 꺼낼 수 있고 길이를 잴 수 있는 값의 모음 | 파이썬 공식 용어집 sequence · 파이썬 Built-in Types 「Sequence Types」 |
| 관계형 데이터베이스 | 테이블과 따로 서는 개체. 부를 때마다 다음 정수를 하나 내주는 번호 생성 장치. 동시에 불러도 세션마다 서로 다른 값을 받는다 | PostgreSQL 공식 문서 CREATE SEQUENCE · 9.17 Sequence Manipulation Functions |
| 네트워크 프로토콜 | 연결로 보내는 바이트 하나하나, 또는 보내는 패킷 하나하나에 붙는 번호 | RFC(Request for Comments, 인터넷 표준 문서) 9293 §3.4 · RFC 4303 §2.2 |
| 리눅스 커널 | 쓰는 측이 쓰기 앞뒤로 하나씩 올리는 카운터 값. 읽는 측은 이 값을 견주어 다시 읽을지 정한다 | 리눅스 커널 문서 「Sequence counters and sequential locks」 |
아래에서 맥락마다 하나씩 봅니다.
파이썬의 시퀀스
파이썬에서 시퀀스는 값을 정해진 차례로 담아 두는 모음입니다. 몇 번째인지를 가리키는 정수 번호로 원소를 꺼냅니다. 이 번호를 인덱스라고 부릅니다. 길이도 잴 수 있습니다.
리스트 · 튜플 · range 가 기본 시퀀스입니다. 글자를 늘어놓은 문자열과 바이트를 늘어놓은 bytes 도 시퀀스입니다. 시퀀스라면 대개 아래 연산을 받습니다.
s = ["사과", "포도", "귤"]
s[1] # '포도' — 맨 앞이 0번이다
s[0:2] # ['사과', '포도'] — 0번부터 2번 앞까지 잘라 낸다
len(s) # 3
정수 번호로 꺼낸다는 점이 핵심입니다. 같은 값을 두 번 담아도 각각 제 번호를 갖습니다. 값을 담은 차례가 다르면 다른 시퀀스입니다.
collections.abc.Sequence 는 시퀀스 연산을 모아 둔 추상 기반 클래스입니다. 추상 기반 클래스는 「이런 연산을 갖춰라」는 약속만 적어 두는 클래스입니다. 이 클래스가 정하는 인터페이스는 번호로 꺼내기와 길이 재기보다 넓습니다. count() · index() · in 검사 · 거꾸로 돌기를 더 얹습니다.
직접 만든 자료형이 이 넓은 인터페이스를 구현하면 register() 로 시퀀스로 등록할 수 있습니다. 이 클래스는 그 연산들을 제대로 구현하기 쉽게 도우려고 딸려 있습니다.
관계형 데이터베이스의 시퀀스
관계형 데이터베이스에서 시퀀스는 값의 모음이 아닙니다. 부를 때마다 다음 정수를 하나 건네주는 장치입니다. 주로 테이블의 행마다 겹치지 않는 식별자를 매기는 데 씁니다.
이 장치가 왜 따로 있는지는 번호를 직접 매겨 보면 드러납니다. 「지금 가장 큰 번호에 하나를 더한다」로 매기면, 두 요청이 같은 순간에 가장 큰 번호를 읽어 같은 값을 쓸 수 있습니다. 동시에 일어난 일의 차례에 따라 결과가 달라지는 이런 상황을 경쟁 상태라고 부릅니다.
sequenceDiagram
participant A as 요청 A
participant B as 요청 B
participant T as 주문 테이블
A->>T: 가장 큰 번호를 읽는다
B->>T: 가장 큰 번호를 읽는다
Note over A,B: 둘 다 3 을 받았다
A->>T: 4 번 행을 넣는다
B->>T: 4 번 행을 넣는다
Note over A,T: 같은 번호가 두 번 쓰였다
시퀀스는 다음 값으로 넘기는 일을 원자적으로 합니다. 원자적이라는 말은 중간에 쪼개지지 않는 한 동작으로 일어난다는 뜻입니다. 여러 세션이 같은 순간에 다음 값을 달라고 해도 각자 다른 값을 받습니다.
CREATE SEQUENCE order_seq;
SELECT nextval('order_seq'); -- 1
SELECT nextval('order_seq'); -- 2
SELECT nextval('order_seq'); -- 3
nextval 은 시퀀스를 다음 값으로 넘깁니다. 그 값을 그대로 돌려줍니다. 기본 설정으로 만들면 1부터 하나씩 올라갑니다. 시퀀스를 다루는 함수에는 setval 도 있습니다. setval 은 시퀀스의 지금 값을 바꿉니다.
PostgreSQL 에서 시퀀스는 행이 하나뿐인 특별한 테이블로 만들어집니다. 이름도 같은 스키마의 테이블 · 인덱스 · 뷰와 겹치면 안 됩니다. 기본으로는 어느 열에도 매이지 않습니다. OWNED BY 로 열 하나에 묶어 두면 그 열이나 테이블을 지울 때 시퀀스도 함께 지워집니다.
MySQL 은 같은 일을 열에 붙이는 속성으로 합니다. 열에 AUTO_INCREMENT 를 붙이면 값을 안 넣은 새 행마다 고유한 번호가 자동으로 매겨집니다. 이 번호도 시퀀스 번호라고 부릅니다. 따로 이름을 가진 개체가 아니라 그 열에 매인 성질이라는 점이 PostgreSQL 의 시퀀스와 갈립니다.
번호를 내주는 것이 어디에 매달려 있나를 둘로 갈라 보면 이렇습니다.
flowchart TD
subgraph PG["PostgreSQL 스키마"]
S1["시퀀스 order_seq · 따로 선 개체"]
T1["주문 테이블 · id 열"]
S1 -.->|OWNED BY 로만 묶인다| T1
end
subgraph MY["MySQL 테이블"]
T2["주문 테이블"]
C2["id 열 · AUTO_INCREMENT 속성"]
T2 --> C2
end
CREATE SEQUENCE 는 SQL(Structured Query Language) 표준에 있는 문장입니다. 표준은 다음 값을 NEXT VALUE FOR 식으로 받습니다. PostgreSQL 은 이것을 nextval() 함수로 받습니다.
네트워크 프로토콜의 시퀀스 번호
TCP(Transmission Control Protocol, 전송 제어 프로토콜)는 연결로 보내는 데이터의 바이트 하나하나에 번호를 붙입니다. 이 번호가 시퀀스 번호입니다. 데이터를 세그먼트라는 조각으로 나눠 보낼 때, 세그먼트 안의 첫 바이트가 가장 작은 번호를 갖습니다. 뒤따르는 바이트는 하나씩 이어진 번호를 갖습니다.
번호가 세그먼트가 아니라 바이트에 붙으므로 세그먼트마다 1 · 2 · 3 으로 오르지 않습니다. 100바이트를 실은 세그먼트 다음 세그먼트는 번호가 100 늘어납니다.
수신 측은 이 번호로 세 가지를 합니다.
| 하는 일 | 어떻게 |
|---|---|
| 차례 맞추기 | 들어온 세그먼트를 대개 번호 차례로 놓고 처리합니다 |
| 받았다고 알리기 | 확인 응답에 번호 X 를 담습니다. X 바로 앞 바이트까지 전부 받았다는 뜻입니다. 이렇게 앞을 다 아우르는 방식을 누적 확인 응답이라고 부릅니다 |
| 겹친 것 가려내기 | 다시 보낸 조각이 겹쳐 와도 번호로 이미 받은 것을 알아봅니다 |
송신 측도 번호로 잃어버린 조각을 알아챕니다. 그 조각을 다시 보내 바로잡습니다. 아래는 바이트 300개를 세 조각으로 보내다 가운데 하나를 잃은 모습입니다. 번호는 보기로 고른 값입니다.
sequenceDiagram
participant S as 송신 측
participant R as 수신 측
S->>R: 1~100 번 바이트
R-->>S: 확인 응답 101
S-xR: 101~200 번 바이트 · 도중에 잃었다
S->>R: 201~300 번 바이트
Note over R: 101 번부터가 비어 확인 응답을 301 로 올리지 못한다
S->>R: 101~200 번 바이트를 다시 보낸다
R-->>S: 확인 응답 301
번호를 패킷마다 붙이는 프로토콜도 있습니다. IPsec(Internet Protocol Security, IP 보안)의 ESP(Encapsulating Security Payload, 캡슐화 보안 페이로드)는 패킷을 하나 보낼 때마다 32비트 번호를 하나씩 올립니다. 이 번호로 재전송 공격(replay attack)을 막습니다. 재전송 공격은 남이 가로챈 패킷을 손대지 않고 다시 보내는 공격입니다. TCP 가 잃은 조각을 다시 보내는 재전송과는 다른 말입니다.
이 방어는 보안 연관(SA, Security Association)마다 수신 측이 켜거나 끕니다. 보안 연관은 양 끝이 키를 정해 맺은 한 벌입니다. 기본은 켜짐입니다. 켜져 있으면 수신 측은 그 보안 연관이 이어지는 동안 이미 받은 번호와 겹치는 패킷을 걸러 냅니다.
32비트 번호는 담을 수 있는 가장 큰 값에 닿으면 0으로 돌아갑니다. 이것을 번호가 한 바퀴 돈다고 합니다. 한 바퀴 돌면 새 패킷이 이미 받은 번호를 달게 됩니다.
방어가 켜져 있으면 이 번호는 한 바퀴 돌면 안 됩니다. 그래서 2^32 번째 패킷을 보내기 전에 새 보안 연관과 새 키를 세웁니다. 새 보안 연관에서는 번호를 0부터 다시 셉니다.
리눅스 커널의 시퀀스 카운터
리눅스 커널의 시퀀스 카운터는 여럿이 읽고 가끔 하나가 고치는 데이터를 지키는 장치입니다. 시스템 시각처럼 드물게 고치는 데이터에 씁니다. 읽는 측이 일관된 데이터 한 벌을 얻게 해 줍니다. 대신 읽는 측은 데이터가 바뀌었으면 다시 읽을 각오를 합니다.
이 장치의 몸은 정수 하나입니다. 그 정수를 카운터 값이라고 부릅니다. 커널 코드에서 이 기본형의 이름은 seqcount_t 입니다.
쓰는 측은 쓰기를 시작할 때 카운터 값을 하나 올립니다. 쓰기를 마칠 때 하나 더 올립니다. 쓰는 동안에는 값이 홀수입니다. 다 쓰면 짝수로 돌아옵니다.
쓰는 측이 카운터 값을 올리고 데이터를 고치는 동안을 쓰는 구간이라고 부릅니다. 읽는 측이 카운터 값을 보고 데이터를 복사하는 동안이 읽는 구간입니다.
읽는 측은 데이터를 복사하기 전과 후에 카운터 값을 봅니다. 시작할 때 짝수였고 끝날 때도 같은 값이면 그 사이에 아무도 고치지 않은 것입니다. 값이 달라졌으면 처음부터 다시 읽습니다.
flowchart TD
A["읽기 전에 카운터 값을 본다"] --> B{"짝수인가"}
B -->|홀수 · 쓰는 중이다| A
B -->|짝수| C["데이터를 복사해 둔다"]
C --> D["복사한 뒤 카운터 값을 다시 본다"]
D --> E{"처음 본 값과 같나"}
E -->|같다| F["복사본을 쓴다"]
E -->|다르다| A
아래는 읽는 측이 복사하는 도중에 쓰는 측이 끼어드는 모습입니다. 4 · 5 · 6 은 차이가 보이게 고른 보기 값입니다.
sequenceDiagram
participant W as 쓰는 측
participant C as 카운터 값
participant R as 읽는 측
R->>C: 복사하기 전에 본다 · 4
Note over R: 짝수라 복사를 시작한다
W->>C: 쓰기를 시작하며 올린다 · 5
Note over W: 데이터를 고친다
W->>C: 쓰기를 마치며 올린다 · 6
R->>C: 복사한 뒤 다시 본다 · 6
Note over R: 끝값은 짝수지만 처음 본 4 와 달라 처음부터 다시 읽는다
이 방식에서 읽는 측은 잠금을 잡지 않습니다. 쓰는 측도 읽는 측에 밀려 굶지 않습니다. 대가는 고치는 일이 잦으면 읽기가 계속 다시 돈다는 것입니다. 데이터를 복사하는 일은 읽는 구간 안에서 끝내야 합니다.
seqcount_t 는 쓰는 측이 여럿 들어오는 것을 혼자 막지 못합니다. 쓰는 측을 한 번에 하나만 들이는 일까지 해 주는 장치가 seqlock_t 입니다. 시퀀스 카운터에 스핀락 하나를 붙여 둔 것입니다. 스핀락은 잠금이 풀릴 때까지 제자리에서 돌며 기다리는 잠금입니다.
어느 뜻인지 가르는 단서
글에서 「시퀀스」를 만나면 곁에 놓인 말을 봅니다. 대개 그것만으로 어느 뜻인지 갈립니다.
| 곁에 놓인 말 | 뜻 |
|---|---|
몇 번째 · 원소 · 슬라이싱 · len · 리스트 · 튜플 |
값의 모음 |
CREATE SEQUENCE · nextval · setval · 기본 키 · AUTO_INCREMENT |
번호를 내주는 장치 |
| 세그먼트 · 확인 응답 · 재전송 · 바이트 · 패킷 | 데이터에 붙는 번호 |
seqcount_t · seqlock_t · 읽기 재시도 · 홀수 짝수 |
커널이 쓰기마다 올리는 카운터 값 |
보장과 가정
번호를 다루는 세 뜻 — 데이터베이스 시퀀스 · TCP 시퀀스 번호 · 커널 시퀀스 카운터 — 이 각각 무엇을 약속하는지 봅니다. 약속마다 그것이 서 있는 전제와, 전제가 깨질 때 무너지는 것을 짝지어 둡니다.
값의 모음은 번호를 내주지 않으므로 이 절에서 빠집니다.
데이터베이스 시퀀스
보장 — 여러 세션이 동시에 nextval 을 불러도 서로 다른 값을 받습니다.
가정 — 시퀀스가 끝값에 닿으면 멈춥니다. 기본 설정이 NO CYCLE 이라 끝값을 넘어 nextval 을 부르면 오류가 납니다. 그리고 누구도 setval 로 값을 되감지 않습니다.
가정이 깨지면 — CYCLE 로 만들면 올라가는 시퀀스는 끝값 다음에 가장 작은 값(minvalue)으로 돌아갑니다. 이미 내준 번호가 다시 나옵니다. setval 로 이미 내준 값 아래로 되감아도 같은 번호가 다시 나옵니다. setval 은 트랜잭션을 롤백해도 되돌려지지 않습니다.
MySQL 의 AUTO_INCREMENT 열도 끝이 있습니다. 열의 정수 타입이 담을 수 있는 가장 큰 값에 닿으면 다음 번호를 만들지 못하고 실패합니다.
약속하지 않는 것 — 시퀀스는 빈틈없는 번호를 약속하지 않습니다. 번호를 받아 간 트랜잭션이 나중에 취소돼도 그 번호는 돌아오지 않습니다. 같은 시퀀스에서 번호를 받는 다른 트랜잭션을 기다리게 하지 않으려고 이렇게 만들었습니다.
데이터베이스가 죽어도 번호가 빌 수 있습니다. 충돌하면 다른 처리로 넘어가는 INSERT ... ON CONFLICT 도 번호를 씁니다. 충돌을 알아채기 전에 필요한 nextval 을 먼저 부르기 때문입니다.
sequenceDiagram
participant A as 트랜잭션 A
participant S as 시퀀스
participant B as 트랜잭션 B
A->>S: 번호를 달라
S-->>A: 1
B->>S: 번호를 달라
S-->>B: 2
Note over B: 취소됐다 · 2 는 돌아가지 않는다
A->>S: 번호를 달라
S-->>A: 3
Note over A: 테이블에는 1 과 3 만 남는다
주문 번호가 1 · 3 으로 남아도 2번 행이 지워졌다고 단정할 수 없습니다. 몇 건이 들어왔는지는 마지막 번호로 짐작할 수 없습니다. 행을 세어야 합니다.
빈틈없는 번호가 꼭 필요하면 다음 번호를 적은 테이블 하나에 배타 잠금을 걸어 매기는 방법이 있습니다. 시퀀스보다 훨씬 비쌉니다. 많은 트랜잭션이 동시에 번호를 받으려 할 때 특히 그렇습니다.
CACHE 를 1보다 크게 두면 시퀀스는 여러 세션 사이의 오름차순도 약속하지 않습니다. 세션마다 번호 여러 개를 한 번에 미리 받아 두고 하나씩 쓰기 때문입니다. 그러면 세션 사이에서 번호가 엇갈립니다.
sequenceDiagram
participant A as 세션 A
participant S as 시퀀스 · CACHE 10
participant B as 세션 B
A->>S: 번호를 달라
S-->>A: 1~10 을 미리 받아 두고 1 을 쓴다
B->>S: 번호를 달라
S-->>B: 11~20 을 미리 받아 두고 11 을 쓴다
Note over A: 11 이 나간 뒤에야 2 를 쓴다
CACHE 가 1이면 번호가 차례대로 나온다고 봐도 됩니다. 1보다 크면 서로 다르다는 것만 믿어야 합니다. 미리 받아 두고 못 쓴 번호는 그 세션이 끝날 때 사라져 구멍이 됩니다.
TCP 시퀀스 번호
보장 — 수신 측이 바이트를 차례대로 맞춥니다. 이미 받은 것과 겹친 조각도 가려냅니다.
가정 — 번호 공간은 0부터 2^32 - 1 까지로 유한합니다. 끝에 닿으면 0으로 돌아가 다시 셉니다.
전제는 이렇습니다. 번호가 한 바퀴 다 돌기 전에 그 번호를 단 데이터는 전달과 확인 응답을 마칩니다. 망에 떠도는 그 데이터의 중복본도 모두 사라집니다.
이 전제는 세그먼트가 망에 머무는 시간에 상한이 있다는 데 기댑니다. 그 상한이 MSL(Maximum Segment Lifetime, 세그먼트 최대 수명)입니다. 임의로 2분으로 정해 둡니다.
가정이 깨지면 — 회선이 빠르면 번호가 한 바퀴 도는 시간이 짧아집니다.
| 회선 속도 | 번호가 한 바퀴 도는 시간 |
|---|---|
| 1 Gbps | 34초 |
| 10 Gbps | 3초 |
| 100 Gbps | 약 3분의 1초 |
1 Gbps 면 34초입니다. MSL 2분보다 짧습니다. 옛 세그먼트가 망에서 사라지기를 기다리는 시간이 다 가기 전에 번호가 한 바퀴 돌아 버립니다. 가정이 여기서 깨집니다.
번호가 바이트마다 붙으므로 한 바퀴는 2^32 바이트, 곧 4 GiB 입니다. 한 연결에서 4 GiB 이상을 빠르게 보내면 큐에 붙들린 세그먼트 하나가 늦게 도착하는 사이에 번호가 한 바퀴 돌 수 있습니다. 그러면 서로 다른 두 세그먼트가 같은 번호를 달 수 있습니다. 수신 측은 어느 것이 새 데이터이고 어느 것이 옛 데이터인지 헷갈릴 수 있습니다.
이것을 막는 장치가 PAWS(Protection Against Wrapped Sequences, 번호 되감김 보호)입니다. 세그먼트마다 TCP 타임스탬프 옵션으로 시간이 지날수록 줄지 않는 값을 싣습니다. 최근에 받은 타임스탬프보다 작은 값을 단 세그먼트는 옛 중복으로 보고 버릴 수 있습니다.
아래는 번호가 겹치는 장면과 PAWS 가 옛 것을 가려내는 장면입니다. 번호 N 과 타임스탬프 t1 · t2 는 기호로 두었습니다. t1 이 t2 보다 작습니다.
sequenceDiagram
participant S as 송신 측
participant Q as 망의 큐
participant R as 수신 측
S->>Q: 번호 N · 타임스탬프 t1 세그먼트
Note over Q: 큐에 붙들려 늦어진다
Note over S,R: 4 GiB 이상을 보내는 사이 번호가 한 바퀴 돈다
S->>R: 번호 N · 타임스탬프 t2 새 세그먼트
Q->>R: 번호 N · 타임스탬프 t1 옛 세그먼트가 늦게 도착
Note over R: 번호만으로는 어느 것이 새것인지 헷갈릴 수 있다
Note over R: t1 이 최근 받은 t2 보다 작아 PAWS 가 옛 중복으로 버릴 수 있다
커널 시퀀스 카운터
보장 — 읽는 측이 쓰는 도중의 반쯤 바뀐 값을 받아 쓰지 않습니다.
가정 — 쓰는 측은 한 번에 하나씩만 들어옵니다. seqcount_t 는 쓰는 측이 여럿 들어오는 것을 막지 못합니다. 그래서 바깥 잠금으로 쓰는 측을 한 번에 하나만 들여야 합니다. seqlock_t 가 그 잠금을 품은 장치입니다.
쓰는 구간은 도중에 선점되면 안 됩니다. 선점은 실행 중인 작업을 운영체제가 중간에 멈추고 CPU(Central Processing Unit, 중앙 처리 장치)를 다른 작업에 넘기는 일입니다. 읽는 구간이 쓰는 구간을 끊고 들어오는 것도 안 됩니다.
보호하는 데이터에 포인터가 없어야 합니다. 포인터가 있으면 읽는 측이 따라가던 포인터를 쓰는 측이 무효로 만들 수 있습니다.
가정이 깨지면 — 쓰는 측이 쓰는 구간 도중에 멈추면 카운터 값이 홀수로 남습니다. 읽는 측은 홀수를 보고 계속 다시 돕니다. 이 되돌기가 끝나려면 멈춘 쓰는 측이 다시 CPU 를 받아 카운터 값을 짝수로 되돌려야 합니다.
스케줄러 틱은 운영체제가 어느 작업에 CPU 를 줄지 다시 따지는 주기입니다. 읽는 측이 일반 작업이면 틱 하나가 다 지나며 차례가 넘어갑니다. 그동안 읽는 측은 헛돕니다.
읽는 측이 일반 작업보다 앞서 CPU 를 받는 실시간 스케줄링 클래스면 끝없이 돌 수 있습니다. 멈춘 쓰는 측이 CPU 를 되찾지 못해 카운터 값이 홀수로 남아 있기 때문입니다. 그러면 커널이 라이브락에 빠집니다. 라이브락은 작업이 계속 움직여도 아무 일도 앞으로 나가지 않는 상태입니다.
경계
값의 모음으로서의 시퀀스와 헷갈리는 이웃 하나, 파이썬 dict 를 판정합니다.
파이썬 dict
파이썬 dict 도 d[키] 로 값을 꺼내고 len(d) 로 길이를 잽니다. 그러면 dict 도 시퀀스일까요? 아닙니다. 매핑입니다.
가르는 것은 무엇으로 꺼내느냐입니다. 시퀀스는 정수 번호로 꺼냅니다. dict 는 해시할 수 있는 값이면 무엇이든 키로 삼아 꺼냅니다. 꺼내는 연산과 길이 재는 연산을 둘 다 갖췄어도 키가 정수 번호가 아니면 시퀀스로 치지 않습니다.
dict 와 같은 갈래로 꼽히는 collections.defaultdict · collections.OrderedDict · collections.Counter 도 매핑입니다. 셋 다 정수 번호가 아니라 키로 값을 꺼냅니다.
관련 항목
값의 시퀀스로 드는 파이썬 자료형
리스트 · 튜플 · range · 문자열 · bytes
값을 차례로 담는 자료구조
시퀀스와 맞세워지는 다른 모음 종류
집합 · 매핑 · 맵 · 해시테이블 · 다중집합 · OrderedDict
값의 시퀀스가 속하는 상위 분류
자료구조 · 추상 자료형 · 추상 기반 클래스 · 컬렉션 · 컨테이너 · 이터러블
값의 시퀀스에서 값을 꺼내는 수단
인덱스 · 이터레이터 · 슬라이싱 · 반복문 · 제너레이터 · 스트림
데이터베이스 시퀀스를 만들고 다루는 질의 언어와 함수
SQL · CREATE SEQUENCE · nextval · currval · setval
시퀀스 번호가 비게 되는 SQL 구문과 작업 단위
INSERT · ON CONFLICT · 롤백 · 트랜잭션
데이터베이스에서 겹치지 않는 번호를 만드는 방법
자동 증가 · 식별 열 · 식별자 · 기본 키 · 대리 키 · UUID · 스노우플레이크
겹치지 않는 번호를 내주는 기능을 가진 데이터베이스
관계형 데이터베이스 · PostgreSQL · MySQL
차례를 표시하는 다른 번호와 시계
시퀀스 번호 · 초기 시퀀스 번호 · 시간과 시계 · 타임스탬프 · 논리 시계 · 벡터 시계 · 단조 시계 · 에포크 · 리비전
시퀀스 번호를 쓰는 프로토콜과 그 상위 분류
TCP · IPsec · ESP · 프로토콜
TCP 가 번호를 붙이고 주고받는 단위와 절차
TCP 시퀀스 번호의 되감김에 걸린 시간 조건과 막는 장치
MSL · PAWS · TCP 타임스탬프 옵션
시퀀스 번호로 막는 공격과 그 수단
재전송 공격 · 논스 · 안티 리플레이
커널 시퀀스 카운터를 이루는 장치와 그것이 거는 조건
seqlock · seqcount · 스핀락 · 선점 · 라이브락 · 실시간 스케줄링
번호를 견주어 다시 읽게 하는 동시성 수단
동시 접근에서 터지는 문제와 번호가 기대는 규약
시퀀스 다이어그램이 속한 표기법과 나란히 선 그림
시퀀스 다이어그램 · 상호작용 다이어그램 · UML
시퀀셜 스캔이 찍히는 실행 계획과 나란히 선 읽기 방법
시퀀셜 스캔 · 실행 계획 · 인덱스 스캔 · 비트맵 인덱스 스캔
다른 이름: sequence