벽시계
고친 사람 github-actions[bot]
벽시계는 지금이 몇 시인지 알려 주는 시계입니다. 사람이 읽는 시각이 이 시계에서 나옵니다. 다른 기기와 맞대어 보는 시각도 같습니다. 대신 세상의 시각을 따라가느라 값이 앞으로 뛰거나 뒤로 돌아가기도 합니다.
쉽고 빠른 이해
벽시계는 사무실 벽에 걸린 시계와 같은 일을 합니다. 로그 한 줄에 찍히는 시각이 이 시계에서 나옵니다.
기기가 혼자 세는 시각은 놔두면 조금씩 틀어집니다. 여러 기기의 기록을 나란히 놓고 보려면 모두가 같은 기준 시각을 따라가야 합니다.
- 기기 안의 시계가 자기 박자로 시각을 셉니다
- 바깥의 기준 시각과 견주어 얼마나 어긋났는지 봅니다
- 그만큼 값을 옮기거나 박자를 조절해 따라잡습니다
대가는 값이 한 방향으로만 흐른다고 믿을 수 없다는 것입니다. 두 번 읽어 뺀 차이가 음수로 나오기도 해서, 걸린 시간을 재는 데는 못 씁니다.
상세
사무실 벽에 걸린 시계를 떠올려 보십시오. 모두가 그 시계를 보고 회의 시각을 맞춥니다. 조금 느려졌다 싶으면 누군가 바늘을 돌려 맞춰 둡니다. 컴퓨터가 들고 있는 벽시계도 똑같습니다.
벽시계는 세상이 함께 쓰는 시각을 따라가는 시계입니다. 이 시계는 맞고 틀림이 있습니다. 바깥의 기준 시각과 견주어 얼마나 어긋났는지 알아냅니다. 알아내면 맞춰 줍니다. 로그에 찍는 시각, 데이터베이스에 넣는 만료 시각, 인증서의 유효 기간이 전부 벽시계 값입니다.
이 시계가 없으면 기기마다 제각각인 시각을 세게 됩니다. 그러면 두 서버의 기록을 나란히 놓고 어느 것이 먼저인지 가릴 수 없습니다. 벽시계는 그 값을 세상의 시각에 묶어 두어, 남의 기록이나 사람이 보는 달력과 맞댈 수 있게 만듭니다.
이 절은 차례로 봅니다. 벽시계가 들고 있는 값, 그 값이 오는 곳, 값을 맞추는 두 방법과 값이 뒤로 가는 까닭입니다. 성능을 말할 때의 벽시계 시간과 벽시계를 고르는 기준도 뒤에 붙습니다.
벽시계가 들고 있는 값
컴퓨터는 시각을 날짜와 시·분으로 들고 있지 않습니다. 기준으로 삼은 한 시점부터 몇 초가 지났는지를 수 하나로 셉니다. 그 기준 시점을 에포크라고 합니다.
유닉스 계열이 널리 쓰는 에포크는 1970-01-01 0시입니다. 이 기준으로 센 값을 유닉스 시간이라고 부릅니다. 기준이 되는 시각은 온 세상이 함께 쓰는 UTC(Coordinated Universal Time, 협정 세계시)로 잡습니다.
나라마다 다른 시간대는 이 값에 안 들어갑니다. 값은 어디서 읽어도 같습니다. 사람에게 보여 줄 때만 그 나라 몫을 더하고 뺍니다. 그래서 서울에서 찍은 시각과 런던에서 찍은 시각을 저장한 값끼리 바로 견줄 수 있습니다.
flowchart TD
E["에포크 · 1970-01-01 0시"] --> V["경과 초 하나 · UTC 기준"]
V --> S["서울에 보일 때 +9시간"]
V --> L["런던에 보일 때 +0"]
저장하는 칸은 하나입니다. 갈림은 사람에게 보여 주기 직전에만 생깁니다.
값이 오는 곳
기기 안에서 시계의 박자를 만드는 부품은 수정 진동자입니다. 전기를 주면 일정한 횟수로 떱니다. 그 떨림을 세어 시각을 셉니다.
전원이 꺼지면 이 셈이 멈춥니다. 그래서 메인보드에는 작은 배터리로 따로 도는 실시간 시계가 있습니다. 부팅할 때 커널이 그 값을 읽어 벽시계의 출발점으로 삼습니다.
수정 진동자는 온도에 따라 미세하게 빨라지고 느려집니다. 놔두면 기준 시각에서 조금씩 멀어진다는 뜻입니다. 이 벌어짐을 클록 드리프트라고 합니다.
벌어진 것을 메우는 일이 시각 동기화입니다. 바깥의 기준 시각 서버에 물어 자기 시각이 얼마나 어긋났는지 잽니다. 그만큼 되돌립니다.
이 일을 맡는 대표 프로토콜이 NTP(Network Time Protocol, 네트워크 시각 프로토콜)입니다.
flowchart TD
subgraph inside["기기 안"]
X["수정 진동자 · 박자를 만든다"] --> K["커널이 들고 있는 벽시계 값"]
R["실시간 시계 · 전원이 꺼져도 시각을 지킨다"] --> K
end
subgraph outside["바깥"]
S["기준 시각 서버"] --> N["시각 동기화 · 어긋난 만큼 되돌린다"]
end
N --> K
K --> A["프로그램이 읽는 벽시계 시각"]
프로그램이 읽는 값은 맨 아래 칸입니다. 바깥에서 들어오는 화살표가 하나 있다는 것이 벽시계의 성격입니다. 값이 기기 안에서만 자라지 않고 남의 손을 탑니다.
시각을 맞추는 두 방법
어긋난 시각을 되돌리는 방법은 둘입니다. 어느 쪽을 쓰느냐에 따라 값이 뒤로 가기도 하고 안 가기도 합니다.
건너뛰기는 값을 목표 시각으로 곧장 옮깁니다. 한순간에 끝나는 대신 그 순간 시각이 뜁니다. 시계가 앞서 있었다면 값을 뒤로 돌려야 합니다. 그때 벽시계는 지나온 시각을 다시 지나갑니다.
늦추기는 시계의 박자를 잠깐 느리거나 빠르게 해서 서서히 따라잡습니다. 값이 뒤로 가지 않는 대신 다 맞추는 데 오래 걸립니다.
| 건너뛰기 | 늦추기 | |
|---|---|---|
| 하는 일 | 값을 곧장 옮긴다 | 박자를 조절해 서서히 맞춘다 |
| 맞춰지는 때 | 한순간 | 오래 걸린다 |
| 값이 뒤로 가나 | 간다 | 안 간다 |
| 주로 쓰는 때 | 많이 어긋났을 때 | 조금 어긋났을 때 |
표에 안 담기는 것이 하나 있습니다. 시계가 3초 앞서 있을 때 값이 밟고 지나가는 시각입니다.
flowchart TD
subgraph skip["건너뛰기 · 값을 곧장 옮긴다"]
A1["10:00:05"] --> A2["10:00:02 · 뒤로 돌아갔다"] --> A3["10:00:05 · 같은 시각을 다시 지난다"]
end
subgraph slew["늦추기 · 박자를 늦춘다"]
B1["10:00:05"] --> B2["10:00:06 · 한 칸이 좁아진다"] --> B3["10:00:07 · 값은 늘 커진다"]
end
skip ~~~ slew
건너뛰기는 벽시계가 같은 시각을 두 번 지나가게 만듭니다. 늦추기는 값을 늘 키우되 한 칸의 폭만 줄입니다.
벽시계가 뒤로 갈 수 있다는 것은 건너뛰기를 허용한 대가입니다.
값이 뒤로 가면 생기는 일
두 시점 사이의 거리를 벽시계로 재면 어떻게 되는지 보겠습니다.
t1 = 벽시계_읽기() // 10:00:05
// 그 사이 시각 동기화가 값을 뒤로 돌린다
t2 = 벽시계_읽기() // 10:00:02
경과 = t2 - t1 // -3초
일은 앞으로 흘렀는데 잰 값은 음수입니다. 두 번 읽는 사이에 시각 동기화가 값을 뒤로 돌렸기 때문입니다. 반대로 시계를 앞으로 크게 옮기면 짧은 일이 몇 시간짜리로 잡힙니다.
벽시계를 흔드는 것이 시각 동기화만은 아닙니다. 관리자가 손으로 시각을 고칠 때, 윤초 때문에 1초를 넣거나 뺄 때, 멈춰 두었던 가상 머신을 되살릴 때도 값이 튑니다. 이렇게 벽시계 값을 옮기는 일을 통틀어 시각 보정이라 부릅니다.
기기가 여럿이면 어긋남이 하나 더 있습니다. 같은 순간에 서버 둘이 벽시계를 읽어도 값이 서로 조금 다릅니다. 이 차이를 클록 스큐라고 합니다. 그 때문에 나중에 난 사건이 더 이른 타임스탬프를 달고 나타나기도 합니다.
sequenceDiagram
participant 가 as 서버 가 · 벽시계가 앞선다
participant 나 as 서버 나 · 벽시계가 뒤처진다
Note over 가,나: 두 벽시계는 클록 스큐만큼 어긋나 있다
가->>가: 사건 하나가 먼저 난다
Note over 가: 타임스탬프 10:00:05
나->>나: 사건 둘이 나중에 난다
Note over 나: 타임스탬프 10:00:02
세로로 내려가는 것이 실제 시간입니다. 나중에 난 사건 둘이 더 이른 타임스탬프를 달고 나왔습니다.
그래서 걸린 시간을 재는 데는 값이 뒤로 가지 않는 단조 시계를 씁니다. 여러 기기 사이의 앞뒤를 가려야 하면 시각 대신 순서만 매기는 논리 시계로 갑니다.
성능을 볼 때 쓰는 벽시계 시간
성능을 볼 때는 벽시계라는 말이 시계가 아니라 시간의 종류를 가리킵니다. 벽시계 시간은 일이 시작해서 끝날 때까지 바깥에서 흐른 시간 전부입니다. 기다린 시간도 여기 들어갑니다.
맞세워지는 것은 CPU 시간입니다. CPU(Central Processing Unit, 중앙처리장치)가 그 일에 매달려 있던 시간만 셉니다. 응답을 기다리며 놀고 있는 동안 CPU 시간은 안 늘고 벽시계 시간만 흐릅니다.
sequenceDiagram
participant 프로그램
participant 데이터베이스
Note over 프로그램: 계산한다 · 벽시계 시간과 CPU 시간이 함께 흐른다
프로그램->>데이터베이스: 질의를 보낸다
Note over 프로그램,데이터베이스: 기다린다 · CPU 시간은 안 늘고 벽시계 시간만 흐른다
데이터베이스-->>프로그램: 결과를 돌려준다
Note over 프로그램: 다시 계산한다 · 둘 다 흐른다
한 구간 안에서 두 시간이 서로 다른 속도로 흐릅니다. 기다리는 구간에서 벌어진 만큼이 두 값의 차이입니다.
이름은 벽시계지만 잴 때는 단조 시계를 쓰는 것이 보통입니다. 두 번 읽는 사이에 시각 보정이 끼면 잰 값이 어긋나기 때문입니다.
벽시계와 단조 시계를 가르는 기준
기준은 한 줄입니다. 사람이나 다른 기기에 보여 줄 시각이면 벽시계입니다. 두 시점 사이의 거리를 재는 것이면 단조 시계입니다.
| 하려는 일 | 고를 시계 |
|---|---|
| 로그 한 줄에 시각을 찍는다 | 벽시계 |
| 쿠폰 만료 시각을 데이터베이스에 넣는다 | 벽시계 |
| 두 서버의 기록을 시간순으로 맞춘다 | 벽시계 |
| 요청 하나가 얼마나 걸렸는지 잰다 | 단조 시계 |
| 타임아웃까지 남은 시간을 센다 | 단조 시계 |
| 재시도 간격을 센다 | 단조 시계 |
위쪽 셋은 값을 밖으로 내보냅니다. 나중에 사람이 읽거나 다른 기기의 값과 견주므로 벽시계가 아니면 뜻이 안 섭니다. 아래쪽 셋은 값을 밖으로 내보내지 않고 두 시점의 거리만 봅니다. 그 거리는 세상 시각과 상관이 없습니다.
분산 시스템에서는 위쪽마저 벽시계로 부족할 때가 있습니다. 서버마다 벽시계가 조금씩 어긋나 있어서, 아주 가까운 두 사건의 앞뒤를 시각만으로는 못 가리기 때문입니다.
관련 항목
벽시계와 맞세워지는 시계
단조 시계 · 논리 시계 · 벡터 시계 · 하이브리드 논리 시계 · CPU 시간 · 램포트 타임스탬프
벽시계 값이 세는 기준점과 표기 형식
에포크 · 유닉스 시간 · 타임스탬프 · ISO 8601 · RFC 3339 · UTC · 시간대 · 나노초
벽시계 시각을 뒤흔드는 원인
NTP · 윤초 · 클록 드리프트 · 클록 스큐 · 클럭 스텝 · 윤초 문지르기 · 수정 진동자 · 가상 머신
벽시계를 내주는 바탕 소프트웨어와 부품
운영체제 · 커널 · 시스템 콜 · 실시간 시계 · 시스템 클록 · clock_gettime · CLOCK_REALTIME · gettimeofday · 시간과 시계
벽시계 시각을 읽어 값을 정하는 처리
로그 · 만료 · 세션 · 인증서 · 감사 로그 · 스노우플레이크 · 캐시 만료
벽시계가 어긋나도 버티게 하는 기술
다른 이름: wall clock · wall-clock time · 벽시계 시각 · 벽시계 시간