ID가 주소가 아니라 지식이면 어떻게 되는가


주소는 아무것도 모른다

데이터베이스에서 이순신을 찾으려면 ID가 필요하다.

위키데이터에서 이순신의 ID는 Q8492다.

이 번호는 이순신을 가리킨다. 하지만 Q8492라는 문자열 자체는 아무것도 모른다.

이것이 인간인지 건물인지 모른다. 한국인인지 프랑스인인지 모른다. 16세기 인물인지 21세기 인물인지 모른다. 살아있는지 죽었는지 모른다.

Q8492는 주소다. 편지를 배달하는 우체부는 편지 안에 뭐가 쓰여 있는지 모른다. 봉투에 적힌 주소를 보고 배달할 뿐이다.

UUID도 마찬가지다. 550e8400-e29b-41d4-a716-446655440000. 128비트의 무작위 숫자. 충돌을 피하기 위해 고유할 뿐, 이 번호가 가리키는 대상에 대해서는 아무것도 말해주지 않는다.

지난 50년간 데이터베이스의 ID는 이렇게 작동해왔다. ID는 주소고, 정보를 알려면 그 주소를 따라가서 데이터를 읽어야 한다.


따라가야 안다

이것이 왜 문제인가.

“19세기에 태어난 독일 국적의 남성 철학자"를 찾고 싶다고 하자.

전통적인 데이터베이스에서는 이렇게 작동한다.

1. persons 테이블에서 gender = 'male' 필터링
2. nationalities 테이블과 JOIN하여 country = 'Germany' 필터링
3. birth_dates 테이블과 JOIN하여 year BETWEEN 1800 AND 1899 필터링
4. occupations 테이블과 JOIN하여 occupation = 'philosopher' 필터링

네 번의 JOIN 연산. 각 JOIN마다 두 테이블의 행을 대조한다. 테이블이 크면 인덱스를 타고, 인덱스가 없으면 전체 스캔. 데이터가 10억 건이면 이 과정이 수 초에서 수십 초.

왜 이렇게 복잡한가?

ID가 아무것도 모르기 때문이다. Q8492를 보고는 이것이 독일인인지 한국인인지 알 수 없으니, 반드시 다른 테이블로 가서 정보를 가져와야 한다.

모든 질문에 대해 ID가 가리키는 곳까지 따라가야 한다. 이것이 50년간의 데이터베이스가 지불해온 비용이다.


만약 ID가 안다면

발상을 뒤집어보자.

만약 ID 자체에 핵심 정보가 들어있다면?

ID를 보는 것만으로, 이것이 인간인지, 어느 나라 사람인지, 어느 시대 인물인지, 어떤 분류에 속하는지 알 수 있다면?

“19세기 독일 남성 철학자"를 찾기 위해 JOIN이 필요 없어진다.

10억 개의 ID를 훑으면서, 각 ID의 비트를 보고 조건에 맞는지 즉시 판단할 수 있다.

이것이 의미정렬 인덱스(Semantically-Aligned Index)의 핵심 아이디어다.


ID에 의미를 정렬한다

SIDX(Semantically-Aligned Index)는 64비트 식별자다.

이 64비트는 무작위 숫자가 아니다. 비트의 위치에 의미가 할당되어 있다.

상위 비트에는 가장 중요한 정보가 들어간다. 이것이 어떤 종류의 개체인지. 인간인지, 장소인지, 사건인지, 개념인지.

그 다음 비트에는 분류 정보가 들어간다. 인간이라면 어떤 시대의 인간인지, 어떤 지역의 인간인지.

하위 비트로 갈수록 구체적인 정보가 담긴다.

핵심은 이것이다:

비트의 순서가 정보의 중요도 순서다.

가장 근본적인 분류가 최상위에, 가장 세부적인 구분이 최하위에.

이것은 단순한 정렬이 아니다. 이것은 설계 철학이다.


10억에서 1만으로, 한 번에

SIDX의 실용적 위력은 숫자로 드러난다.

WMS에 10억 개의 엔티티가 있다. 각 엔티티의 SIDX는 64비트. 총 용량: 10억 × 8바이트 = 8GB.

이 8GB가 메모리에 통째로 올라간다.

“인간이면서 동아시아 출신인 엔티티"를 찾고 싶다. 상위 비트에 “인간” 플래그, “동아시아” 코드가 들어있으니, 비트마스크 하나로 필터링할 수 있다.

mask   = 0xFF00_0000_0000_0000  (상위 8비트: 타입+지역)
target = 0x8100_0000_0000_0000  (인간+동아시아)

for each sidx in 10억개:
    if (sidx & mask) == target:
        후보에 추가

이 연산은 SIMD로 병렬화된다. AVX-512 기준, 한 번에 8개의 SIDX를 동시 비교. 10억 개 스캔: 약 12밀리초.

GPU로 하면? 1밀리초 미만.

10억 건이 1만 건으로 좁혀진다. 1만 건에서 상세 필터링하는 것은 순식간이다.

JOIN 0번. 인덱스 트리 탐색 0번. 그냥 비트 AND 한 번.


왜 64비트로 충분한가

처음에는 더 큰 공간이 필요하다고 생각했다.

32바이트(256비트). 32차원의 FP16 벡터. 엔티티의 모든 핵심 속성을 ID 안에 우겨넣으려 했다. 인간 여부, 성별, 국적, 시대, 직업, 지역, 생사 여부, 분류 경로…

그러나 깨달은 것이 있다.

ID가 모든 걸 알 필요가 없다.

10억 건에서 1만 건으로만 좁히면 된다. 나머지는 WMS가 알아서 한다.

이것은 검문소와 같다. 고속도로 톨게이트에서 차량 번호판을 보고 “이 차는 경기도 방향"이라고 판단하는 데 차 안에 뭐가 실려있는지 알 필요 없다.

64비트면 충분하다. 상위 비트로 타입과 대분류를 잡고, 하위 비트로 세부 분류를 잡으면, 10억 건을 1만 건으로 좁히는 데 64비트는 넘치고도 남는다.

그리고 64비트 = 16비트 워드 4개. 스트림 안에서 자연스럽게 흐른다. 32바이트짜리 ID는 스트림을 무겁게 만들지만, 64비트 SIDX는 가볍고 빠르다.


우아한 열화: 비트가 잘려도 의미가 살아남는다

의미정렬의 또 다른 강점은 열화 특성이다.

SIDX의 비트가 상위부터 중요도 순으로 정렬되어 있으므로, 하위 비트가 손상되거나 잘려나가도 상위 비트의 핵심 정보는 보존된다.

64비트 전체:  "16세기 조선의 해군 장수 이순신"
48비트:       "16세기 조선의 군인"
32비트:       "16세기 동아시아의 인간"
16비트:       "인간"
8비트:        "물리적 존재"

정보가 잘려나갈수록 구체성을 잃지만, 가장 근본적인 분류는 마지막까지 살아남는다.

이것은 “우아한 열화” 원칙의 비트 수준 구현이다.

네트워크가 끊겨서 데이터의 일부만 도착해도, 시스템은 “정확히 누군지는 모르겠지만, 적어도 인간에 대한 이야기"라는 것을 알고 추론을 이어갈 수 있다.

완전한 침묵보다 흐릿한 윤곽이 낫다. 완전한 실패보다 부분적 이해가 낫다.


쿼리가 ID가 된다

의미정렬 인덱스가 여는 가장 흥미로운 가능성이 있다.

자연어 질의를 임시 SIDX로 변환할 수 있다는 것이다.

사용자가 묻는다: “임진왜란 때 일본군을 무찌른 장군이 누구야?”

인코더가 이 질문을 분석한다. 인간이다. 동아시아다. 16세기다. 군사 관련이다. 이 속성들을 비트로 조립하면 하나의 임시 SIDX가 만들어진다.

이 임시 SIDX로 WMS의 10억 개 SIDX를 스캔한다. 비트 패턴이 가장 유사한 엔티티들이 후보로 올라온다. 이순신, 원균, 권율, 이억기…

여기서 상세 정보를 대조하면 최종 답이 나온다.

이것은 검색과 엔티티 링킹을 하나의 메커니즘으로 통합한다. 별도의 검색 엔진이 필요 없다. 별도의 NER(Named Entity Recognition) 파이프라인이 필요 없다. SIDX 비교 한 번에 끝난다.


왜 B-Tree가 아닌가

전통적인 데이터베이스는 B-Tree 인덱스를 사용한다.

B-Tree는 정렬된 데이터에서 특정 값을 O(log n)으로 찾는 데 탁월하다. “Q8492를 찾아라"에는 최적이다.

그러나 “인간이면서 동아시아 출신인 모든 엔티티를 찾아라"에는 취약하다. 복합 조건 검색에는 여러 인덱스를 교차해야 하고, 이 교차 비용이 데이터 규모에 따라 급증한다.

SIDX + SIMD 전수조사는 다른 접근이다.

B-Tree가 “이 주소에 사는 사람이 누구인가"를 빠르게 찾는 전화번호부라면, SIDX 스캔은 “이런 특징을 가진 사람이 누구인가"를 빠르게 찾는 프로파일링이다.

질문의 종류가 다르고, 그래서 최적의 자료구조도 다르다.

질문 유형B-TreeSIDX 스캔
특정 ID 조회O(log n), 최적불필요 (해시로 해결)
복합 조건 필터링JOIN 필요, 느림비트 AND 한 번, 빠름
유사 엔티티 검색불가능벡터 유사도로 가능
삽입O(log n), 리밸런싱O(1), append
구현 복잡도높음낮음

WMS는 B-Tree를 쓰지 않는다. 10억 개의 SIDX를 메모리에 올리고, SIMD 비트마스크로 전수 조사한다.

단순하고, 무식하고, 빠르다.


허프만의 지혜

SIDX의 비트 할당 구조는 허프만 코딩의 원리를 따른다.

허프만 코딩에서는 자주 등장하는 심볼에 짧은 코드를, 드물게 등장하는 심볼에 긴 코드를 할당한다.

SIDX에서는 가장 자주 필요한 분류 정보에 상위 비트를, 드물게 필요한 세부 정보에 하위 비트를 할당한다.

그리고 이런 언어의 패킷 타입 프리픽스도 같은 원리다. 가장 고빈도인 Tiny Verb Edge에 가장 짧은 프리픽스를, 저빈도인 Event6 Edge에 긴 프리픽스를.

허프만의 지혜가 이런 설계 전체를 관통한다. 비트 하나도 낭비하지 않는다. 가장 중요한 것에 가장 적은 비용을.


요약

전통적인 ID는 주소다. 주소는 아무것도 모른다.

  1. ID가 의미를 모르면, 매번 데이터를 따라가서 읽어야 한다. 이것이 JOIN이다.
  2. 10억 건에서 4번의 JOIN은 느리다.
  3. SIDX는 ID 자체에 의미를 정렬하여 인코딩한다.
  4. 비트마스크 AND 한 번으로 10억 건을 1만 건으로 좁힌다. JOIN 0번.
  5. 64비트로 충분하다. ID가 모든 걸 알 필요 없다. 후보를 좁히면 된다.
  6. 상위 비트에 중요한 정보가 있으므로, 비트가 잘려도 핵심이 살아남는다.
  7. 자연어 질의를 임시 SIDX로 변환하면, 검색이 벡터 연산이 된다.

ID가 주소이기를 그만두고 지식이 되는 순간, 데이터베이스의 규칙이 바뀐다.