TECH 으로 돌아가기
TECH HACKER NEWS 오늘 9분 읽기 31 READS

MySQL에 LSM 스토리지 엔진을 더하다: TideSQL이 온다

MySQL에 LSM 스토리지 엔진을 더하다: TideSQL이 온다
SOURCE IMAGE · HACKER NEWS

MySQL 사용자에게 선택지가 하나 더 생겼다. 쓰기와 공간 효율에 초점을 맞춘 스토리지 엔진 라이브러리 TidesDB가 'TideSQL'이라는 플러그인 형태로 MySQL에서 동작하게 됐다. TidesDB는 원래 이 라이브러리를 플러그인으로 넣기 위한 MySQL 포크로 출발했지만, 당시에는 이를 구현할 통로가 마땅치 않아 방향을 틀어야 했다. 약 1년이 지나 외부 플러그인 엔진으로 제안이 다시 올라왔고, 이번에 실제 배포로 이어졌다.

주목할 점은 TideSQL이 포크가 아니라 플러그인이라는 사실이다. 사용자는 정식 MySQL 서버를 그대로 띄운 뒤 엔진을 적재하면 되고, 같은 서버 안에서 TidesDB 테이블과 InnoDB 테이블이 나란히 공존할 수 있다. 현재 설치는 플러그인 저장소의 install.sh 스크립트를 통해 이뤄지며, 직접 빌드한 결과물을 지정하거나 스크립트가 번들을 빌드하도록 맡길 수 있다. 이 과정을 더 간편하게 만드는 방안은 관련 당사자들과 논의 중이라고 한다. TideSQL for MySQL v2.0.0은 TidesDB v10.1.1과 짝을 이루며, MySQL 서버 v9.7.0과 v26.7.0에서 테스트됐다.

설정은 JSON 속성으로

MySQL에는 엔진별 CREATE TABLE 문법이 없기 때문에, 테이블은 자신의 TidesDB 옵션을 ENGINE_ATTRIBUTE에 JSON 객체로 적어 넣는다. 서버는 이 값을 읽지 않고 그대로 저장해 엔진에 넘기며, 이름을 정의한 주체가 엔진이므로 검증도 엔진이 한다. 덕분에 철자가 틀린 옵션은 조용히 무시되는 대신 문장 자체가 실패한다. 모든 옵션에는 tidesdb_default_* 세션 변수가 짝지어져 있어, 정책을 한 번 설정해 두면 이후의 모든 CREATE TABLE이 이를 상속한다. 테이블이 명시하지 않은 값은 생성 시점에 확정되어 그 테이블에 고정된다. 압축은 기본 활성화 상태이며 NONE, SNAPPY, LZ4, ZSTD, LZ4_FAST 중 고를 수 있고 기본값은 LZ4다.

큰 값은 따로 보관한다

이 엔진의 핵심 설계 중 하나는 큰 값을 컴팩션에서 떼어 놓는 것이다. 각 SSTable은 자체 키 로그를 갖지만 값은 데이터베이스 전체가 공유하는 하나의 세그먼트형 값 로그에 모인다. tidesdb_value_separation_threshold 이상인 값은 값 로그로 보내지고 키 로그에는 논리 ID만 남으므로, 이후 병합은 값 대신 포인터만 옮긴다. 다만 이 방식은 스캔 시 행마다 값 로그 읽기가 한 번씩 더 든다. 병합보다 스캔이 훨씬 잦은 테이블이라면 {"keep_values_inline": true}로 크기와 무관하게 값을 키 로그에 둘 수 있다. LSM의 형태도 테이블 단위로 조절된다. level_size_ratio, min_levels, l1_file_count_trigger로 레벨 증가율과 최소 깊이, 레벨 1의 병합 임계치를 정한다. 컴팩션 정책은 따로 고를 필요가 없고, 엔진이 트리 상태에 따라 선점·분할·파티션 병합 중 알아서 택한다. 삭제가 잦은 테이블은 tombstone_density_trigger로 툼스톤 비율이 기준을 넘은 SSTable의 컴팩션을 앞당길 수 있다.

내구성은 tidesdb_memtable_sync_mode 하나로 결정되며, 모든 테이블이 라이브러리의 단일 WAL을 공유하므로 이 설정이 전체 커밋을 지배한다. 기본값 FULL은 커밋된 쓰기가 장치에 도달해 전원 손실에도 살아남음을 뜻한다. INTERVAL은 커밋을 OS에 넘겨 지정 간격 안에 장치에 도달시키므로 프로세스 크래시에는 손실이 없고 머신 크래시에서도 그 창만큼만 잃는다. NONE은 커밋 시점에 아무것도 하지 않아 승인된 커밋이 버퍼에 머물다 이후 배치로 기록되며, 프로세스 크래시 시 유실될 수 있다.

동시성은 낙관적 MVCC다. 비관적 행 잠금이 없으니 잠금 대기도, 조율해야 할 잠금 대기 데드락도 없다. 쓰기 충돌은 문장 안이 아니라 커밋 시점에 ER_ERROR_DURING_COMMIT(1180)로 드러나므로, REPEATABLE READ 이상에서 명시적 BEGIN ... COMMIT을 쓰는 애플리케이션은 이 오류에 재시도로 대응해야 한다. 오토커밋 문장은 READ COMMITTED로 돌아 쓰기-쓰기 검사를 하지 않고, 아무것도 쓰지 않은 트랜잭션은 애초에 충돌하지 않는다. 행 만료는 테이블·행·세션 단위로 걸 수 있고 그 순서로 해소된다. 저장 시 암호화는 행 단위 2계층 키 구조라 마스터 키 교체가 행 데이터를 건드리지 않는다. 암호화된 테이블의 행은 라이브러리에 닿기 전 이미 암호문이라 해당 컬럼 패밀리의 압축은 강제로 꺼지지만, 보조 인덱스는 비암호화 비교 키를 지녀 선택한 압축을 유지한다.

벤치마크가 말하는 것

개발자는 sysbench 1.0.20으로 두 엔진을 같은 서버에서 비교했다. 각각 500만 행 테이블 8개를 기본 설정으로 돌렸고, 정식 설정에서 바꾼 것은 READ COMMITTED와 내구성 모드 두 가지뿐이며 양쪽을 동일하게 맞춰 쓰기에서 어느 쪽도 이득을 보지 않게 했다. 장치에 기록된 바이트는 엔진 자체 집계가 아니라 /proc/diskstats에서 측정해 동일 기준을 적용했다. 약 8GB 데이터셋은 TideSQL의 256MB 블록 캐시·256MB 멤테이블이나 InnoDB의 128MB 버퍼 풀 어느 쪽에도 담기지 않는다. 이 조건에서 InnoDB는 상주하지 않은 16KB 페이지를 읽어 고치고 더블라이트 버퍼와 리두를 거쳐 16KB 전체를 되쓰는 반면, TidesDB는 수백 바이트를 덧붙이고 나중에 정리한다.

표준 sysbench 워크로드는 값 로그를 건드리지 않는다. sbtest 행은 약 188바이트이고 값은 1024바이트 이상일 때만 로그로 가므로 모든 작업이 키 로그에 머문다. 이 설계의 진가는 큰 값에 있기에, 4KB 텍스트 컬럼을 가진 20만 행 테이블 4개로 별도 시험을 돌렸다. 로그 줄이나 JSON 문서처럼 압축이 잘 되는 데이터를 썼고, 세 번째 구성으로 keep_values_inline을 켠 TidesDB를 대조군으로 두어 값 로그만이 유일한 변수가 되도록 했다. 삽입에서 인라인과 InnoDB는 같은 수치에 수렴했고 값 분리는 그 세 배였다. 즉 큰 쓰기의 이득은 LSM이라는 점이 아니라 컴팩션이 키만 다시 쓰고 값은 제자리에 둔다는 데서 나온다. 반대로 읽기에서는 인라인의 중앙값이 0.07ms로 가장 빨랐지만 평균은 0.38ms, 최악은 40ms를 넘겼다. 컴팩션이 그 값들을 읽기 밑에서 끌고 다니기 때문이다. 기본 설정은 평균 0.09ms, 최악 6ms였다. 공간 면에서는 같은 3.05GiB 페이로드가 InnoDB에서 4.24GiB, TidesDB에서 1.19GiB로 내려앉았고, 적재 시간은 InnoDB 17초 대 TidesDB 8초였다.

정리하면 TideSQL은 쓰기량이 많거나 큰 값을 다루는 워크로드, 저장 공간 절감이 중요한 환경에서 InnoDB를 보완하는 선택지가 될 수 있다. 다만 커밋 시점에 드러나는 쓰기 충돌에 대한 재시도 로직, 값 분리 시 스캔 비용, 설치 자동화의 미성숙 같은 지점은 도입 전에 따져볼 대목이다. 두 엔진이 같은 서버에 공존한다는 점은 전면 이관 없이 특정 테이블만 옮겨 실제 워크로드로 검증해 볼 여지를 남긴다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://tidesdb.com/articles/tidesdb-now-available-for-mysql...
SHARE
NEXT · CHOOSE

변화를 읽었다면,
내가 만들 수익 구조를 고릅니다.

정보를 더 모으는 데서 멈추지 않고, 광고·외주·판매·중개·구독 중 내 상황에 맞는 출발점을 정해보세요.

21가지 수익 구조 살펴보기 →
처리 중...