전 세계 웹사이트의 상당수가 같은 데이터베이스를 씁니다. 이름은 MySQL입니다. 그런데 그 데이터베이스에는 쌍둥이가 있습니다. 이름은 MariaDB. 두 제품은 코드가 거의 같고, 명령도 거의 같고, 심지어 버전 번호까지 맞춰 왔습니다.
왜 같은 물건이 두 개일까요. 이 이야기는 기술사가 아니라 소유권의 역사입니다. 그리고 그 끝에 한 사람의 딸 이름이 있습니다.
1995년: 두 사람과 하나의 이름
MySQL은 1995년 스웨덴에서 시작했습니다. 만든 사람은 미카엘 '몬티' 비데니우스(Michael "Monty" Widenius)와 데이비드 악스마크(David Axmark)입니다.
이름의 유래가 재밌습니다. 그리고 흔히 오해되는 지점이기도 합니다. 'My'는 몬티의 첫 딸 My의 이름에서 왔습니다. 한국어로는 '미' 또는 '뮈'로 옮기지만, '마이'가 아닙니다. 영어 발음 'my(마이)'가 아니라 스웨덴어 이름 My입니다. 'SQL'은 Structured Query Language입니다. 즉 MySQL = 'My의 SQL'입니다.
처음부터 오픈소스였습니다. 정확히는 듀얼 라이선스(dual license) 모델이었습니다. GPL로 공개해 누구나 쓸 수 있게 하되, GPL을 피하고 싶은 기업에게는 상용 라이선스를 팔았습니다. 이 모델이 회사 MySQL AB의 수익 구조였습니다.
당시 웹이 폭발하고 있었습니다. PHP와 함께 묶여 'LAMP 스택'(Linux, Apache, MySQL, PHP)의 한 축이 됐습니다. 값싸고 충분히 빠른 조합이었습니다.
핵심 구조: 서버와 스토리지 엔진의 분리
MySQL의 설계에서 가장 중요한 결정은 아키텍처를 두 층으로 나눈 것입니다.
· SQL 레이어(서버 층) — 연결 관리, 파서, 옵티마이저, 캐시. SQL을 해석하고 실행 계획을 세웁니다.
· 스토리지 엔진 층 — 실제로 데이터를 저장하고 읽는 부분. 플러그인 형태로 교체 가능합니다.
이 분리가 MySQL의 정체성을 만들었습니다. 하나의 데이터베이스 안에서 테이블마다 다른 엔진을 쓸 수 있습니다. CREATE TABLE ... ENGINE=InnoDB처럼 지정합니다.
이 구조는 양날의 검입니다. 유연하지만, '어떤 엔진을 쓰느냐'가 트랜잭션·동시성·복구 가능성을 결정합니다. 초심자가 가장 많이 밟는 지뢰가 여기 있습니다.
MyISAM의 시대, 그리고 InnoDB의 승리
2000년대 초까지 기본 엔진은 MyISAM이었습니다. 빠르고 단순했습니다. 테이블 단위로 파일이 나뉘어 다루기 쉬웠습니다.
그런데 치명적 한계가 있었습니다.
· 트랜잭션이 없다. 여러 문장을 하나로 묶어 실패 시 되돌릴 수 없습니다.
· 테이블 단위 락(table-level lock). 한 행을 쓰는 동안 테이블 전체가 잠깁니다. 동시 쓰기가 많으면 급격히 느려집니다.
· 크래시 복구가 약하다. 장애 후 테이블 검사(repair)가 필요하고, 데이터가 깨질 수 있습니다.
이 자리를 채운 것이 InnoDB입니다. 2001년 핀란드 회사 Innobase가 만들었고, MySQL AB가 라이선스해 붙였습니다. 2005년 MySQL 5.0에서 정식 지원되고, 5.5(2010)에서 기본 엔진이 됐습니다.
InnoDB가 제공한 것은 세 가지입니다. 트랜잭션(ACID), 행 단위 락(row-level locking), MVCC. 앞서 데이터베이스 원리 글에서 다룬 그 개념들입니다.
InnoDB 내부: 클러스터드 인덱스라는 결정
InnoDB의 가장 큰 구조적 특징은 데이터가 프라이머리 키 순서로 물리 저장된다는 점입니다. 이를 클러스터드 인덱스(clustered index)라고 합니다.
즉 프라이머리 키 B+ 트리의 잎 노드가 곧 실제 행 데이터입니다. 테이블과 인덱스가 분리된 구조(힙 테이블)와 다릅니다.
결과는 이렇습니다.
· PK로 찾으면 빠릅니다. 인덱스에서 위치를 얻고 다시 행을 읽는 2단계가 아니라, 한 번에 데이터까지 도달합니다.
· PK 순서로 삽입하면 빠릅니다. 순차 쓰기가 됩니다.
· 무작위 PK는 느립니다. 중간에 끼워 넣으면 페이지 분할(page split)이 일어납니다.
그래서 실무 규칙이 나옵니다. AUTO_INCREMENT 정수 PK가 권장되는 이유가 여기 있습니다. UUID를 PK로 쓰면 삽입 위치가 매번 달라져 페이지 분할과 단편화가 늘어납니다. (최근 버전은 UUID v7이나 순서형 UUID로 완화합니다.)
보조 인덱스(secondary index)는 다릅니다. 잎에 PK 값을 담습니다. 그래서 보조 인덱스로 찾으면 인덱스 → PK → 행의 2단계를 거칩니다. 이때 커버링 인덱스(필요한 컬럼이 인덱스에 모두 있는 경우)로 만들면 두 번째 단계를 생략할 수 있습니다.
버퍼 풀, 리두 로그, 더블라이트
InnoDB의 성능은 버퍼 풀(buffer pool)이 결정합니다. 데이터 페이지를 메모리에 올려두는 캐시입니다. 설정값 innodb_buffer_pool_size가 전체 성능의 절반을 좌우한다고 말하는 이유입니다.
쓰기는 리두 로그(redo log)를 먼저 씁니다. WAL 방식입니다. 로그는 순차 쓰기라 빠르고, 실제 페이지는 나중에 내려씁니다.
여기에 InnoDB 특유의 장치가 하나 더 있습니다. 더블라이트 버퍼(doublewrite buffer)입니다. 페이지를 원래 위치에 쓰기 전에 별도 영역에 한 번 더 씁니다.
왜 두 번 쓸까요. 디스크의 섹터 크기(보통 512바이트~4KB)와 InnoDB 페이지 크기(16KB)가 다르기 때문입니다. 쓰기 도중 전원이 나가면 페이지가 절반만 기록될 수 있습니다(torn page). 그러면 리두 로그로도 복구가 안 됩니다. 더블라이트는 이 경우 원본 사본에서 복원할 수 있게 합니다.
대가는 쓰기 2배입니다. 그래서 일부 환경에서는 이 기능을 끄기도 합니다. 안전성과 속도의 명시적 트레이드오프입니다.
복제: 바이너리 로그
MySQL의 복제는 바이너리 로그(binlog) 기반입니다. 프라이머리가 변경 이벤트를 binlog에 기록하고, 레플리카가 그것을 받아 재생합니다.
기록 형식에 세 가지가 있습니다. STATEMENT(SQL 문 자체), ROW(변경된 행), MIXED(자동 선택).
오래된 방식은 STATEMENT였습니다. 그런데 NOW()나 RAND()처럼 비결정적인 함수가 있으면 프라이머리와 레플리카의 결과가 달라집니다. 그래서 현재는 ROW가 기본입니다. 안전하지만 로그가 커집니다.
복제 방식도 발전했습니다. 기본은 비동기입니다. 프라이머리는 레플리카의 확인을 기다리지 않고 커밋합니다. 빠르지만 프라이머리가 죽으면 아직 전송 안 된 변경이 사라질 수 있습니다.
그래서 나온 것이 세미 싱크(semi-sync)입니다. 최소 한 레플리카가 로그를 받았다는 확인을 기다립니다. 완전 동기는 아니지만 유실 위험을 크게 줄입니다. 대신 지연(latency)이 늘어납니다.
또 하나의 진화가 GTID입니다. 기존 복제는 파일 이름과 위치(offset)로 진행을 표시했습니다. 위치가 어긋나면 복제가 깨졌고, 수동 복구가 필요했습니다. GTID는 각 트랜잭션에 전역 고유 ID를 부여해 '어디까지 적용했는가'를 ID로 판단합니다. 장애 조치가 훨씬 단순해집니다.
[[image:1]]
몬티 비데니우스(왼쪽)와 데이비드 악스마크, 2003년. MySQL을 만든 두 사람입니다. (CC BY-SA 2.5)
2010년: 소유권이 바뀐다
여기서 이야기가 꺾입니다.
2008년, MySQL AB가 썬 마이크로시스템즈에 인수됩니다. 2010년, 썬이 오라클에 인수됩니다. 결과적으로 MySQL의 소유권이 오라클로 넘어갔습니다.
커뮤니티의 불안은 즉각적이었습니다. 오라클은 MySQL과 경쟁하는 상용 데이터베이스를 함께 팔기 때문입니다. '오픈소스 프로젝트가 경쟁사 손에 들어갔다'는 우려입니다.
MariaDB: 딸의 이름을 다시 붙이다
2010년, 몬티 비데니우스는 회사를 떠나 MariaDB를 만들었습니다. 이름은 둘째 딸 마리아(Maria)에서 왔습니다. 첫째 딸의 이름이 MySQL이었으니, 가족 이름이 두 번 붙은 셈입니다.
중요한 것은 MariaDB가 처음부터 다시 쓴 제품이 아니라는 점입니다. MySQL의 GPL 코드를 기반으로 분기(fork)했습니다. 그래서 명령, 드라이버, 프로토콜이 거의 호환됩니다. 기존 애플리케이션이 대부분 그대로 돌아갑니다.
이 '드롭인 호환성(drop-in replacement)'이 MariaDB의 확산 전략이었습니다. 리눅스 배포판들이 하나둘 기본 패키지를 MariaDB로 바꿨습니다. RHEL 계열도, Debian도 그 흐름에 참여했습니다. 이유는 단순했습니다. MySQL과 같은 것을 쓰면서 오라클 라이선스 문제를 피할 수 있었기 때문입니다.
[[image:2]]
MySQL Workbench 편집 화면. SQL 레이어와 스토리지 엔진이 분리된 구조가 이 제품의 정체성입니다. (CC BY-SA 4.0)
갈라진 지점: 무엇이 달라졌나
2010년대 초까지 두 제품은 거의 같았습니다. 갈라진 것은 오라클이 MySQL을 발전시킨 방향과 MariaDB 커뮤니티가 간 방향이 달라지면서입니다.
MySQL(오라클) 쪽 — JSON 타입과 함수를 대폭 강화, 윈도우 함수(8.0), CTE, InnoDB 중심의 성능 개선, 문서 스토어(X Protocol), 그리고 상용 에디션 기능(Thread Pool, Enterprise Backup 등) 확장.
MariaDB 쪽 — 엔진 다양성 유지, Aria(MyISAM의 크래시 안전 대안), ColumnStore(컬럼형 분석 엔진), Galera Cluster(동기식 다중 마스터 클러스터), 스레드 풀 기본 제공, 그리고 시스템 버전 테이블(행의 이력 조회) 같은 독자 기능.
흥미로운 차이가 JSON 처리입니다. MySQL은 JSON을 전용 바이너리 타입으로 저장하고 함수를 내장했습니다. MariaDB는 JSON을 텍스트 컬럼의 별칭으로 처리하고, 검색을 위해 가상 컬럼 + 인덱스를 쓰는 방식을 택했습니다. 접근이 다릅니다.
또 하나, MariaDB는 버전 번호를 10.x로 점프시켰습니다. MySQL 5.5 이후의 번호를 따라가지 않겠다는 선언이었습니다. 이후 MySQL은 8.0으로, MariaDB는 10.x·11.x로 각자 갑니다.
반전: '오픈소스가 죽었다'는 예측은 빗나갔다
2010년의 예측은 대체로 이랬습니다. '오라클이 MySQL을 죽일 것이다. MariaDB가 대체할 것이다.'
15년이 지난 결과는 둘 다 살아남았고, 둘 다 성장했습니다. 오라클은 MySQL 8.0에서 성능과 기능을 크게 개선했습니다. MariaDB는 클라우드·배포판·특정 워크로드에서 확고한 자리를 잡았습니다.
즉 포크(fork)가 '대체'가 아니라 '분업'으로 끝난 드문 사례입니다. 이게 가능했던 이유는 GPL이 있었기 때문입니다. 코드를 붙잡아 둘 수 없었던 것입니다.
다만 균형이 필요합니다. 호환성이 완벽하지는 않습니다. MySQL 8.0의 윈도우 함수·CTE·JSON 함수를 쓰는 애플리케이션은 MariaDB에서 그대로 돌지 않을 수 있습니다. 반대로 MariaDB의 시스템 버전 테이블이나 Galera는 MySQL에 없습니다. '거의 같다'는 말은 버전이 가까울 때만 참입니다.
실무에서 무엇을 고를까
MySQL이 유리한 경우 — 최신 SQL 표준 기능(윈도우 함수, CTE, 강력한 JSON)을 쓰고 싶을 때, 클라우드 관리형 서비스(RDS, Cloud SQL, Aurora 계열)의 지원이 중요할 때, 상용 지원과 생태계 도구가 필요할 때.
MariaDB가 유리한 경우 — 순수 오픈소스 라이선스로 통일하고 싶을 때, Galera 같은 동기 클러스터가 필요할 때, 컬럼형 분석(ColumnStore)을 같은 계열에서 쓰고 싶을 때, 리눅스 배포판 기본 패키지와 맞추고 싶을 때.
둘 다 해당하는 경우 — 단순한 OLTP, 읽기 중심 웹 서비스, 소규모 스타트업. 이 경우 선택 기준은 기술이 아니라 운영 인력의 경험입니다.
공통 함정: 엔진과 인덱스
두 제품 모두에서 반복되는 실수가 있습니다.
· MyISAM 테이블이 남아 있다. 오래된 스키마를 그대로 쓰면 트랜잭션이 안 걸립니다. SHOW TABLE STATUS로 엔진을 확인해야 합니다.
· 문자셋이 뒤섞여 있다. utf8과 utf8mb4를 혼용하면 이모지가 깨지고 인덱스가 안 탑니다. 현재는 utf8mb4가 표준입니다.
· 인덱스는 있는데 안 탄다. 컬럼에 함수를 적용하거나(WHERE DATE(created)=...), 형변환이 일어나면 인덱스가 무효화됩니다.
· 슬로우 쿼리 로그를 안 켠다. slow_query_log와 long_query_time을 켜지 않으면 문제를 발견조차 못 합니다.
그래서 이 이야기의 핵심
MySQL과 MariaDB의 30년은 기술보다 거버넌스의 이야기입니다. 같은 코드가 누구 손에 있느냐에 따라 완전히 다른 미래를 가질 수 있다는 것을 보여줍니다.
그리고 GPL이라는 법적 장치가 없었다면 MariaDB는 존재할 수 없었습니다. 코드를 가져갈 수 있는 권리가 생태계를 지킨 것입니다.
마지막으로 잊지 말아야 할 것. 이름의 유래입니다. MySQL과 MariaDB는 한 개발자의 두 딸 이름입니다. (My는 '마이'가 아니라 스웨덴어 이름입니다.) 세계에서 가장 많이 설치된 데이터베이스 중 하나가 가족 이름으로 불리고 있습니다.
여러분은 MySQL과 MariaDB 중 어느 쪽을 쓰시나요? 마이그레이션을 시도했다가 되돌린 경험이 있다면, 어디서 막혔는지 댓글로 남겨주세요.
참고 자료
- MySQL 공식 문서 — InnoDB 아키텍처(클러스터드 인덱스, 버퍼 풀, 더블라이트 버퍼), 바이너리 로그, 복제
- MariaDB 공식 문서 — MySQL과의 호환성 차이, Galera Cluster, 시스템 버전 테이블, ColumnStore
- MySQL AB → Sun Microsystems(2008) → Oracle(2010) 인수, MariaDB 분기(2010)
- 본 글은 두 제품의 설계와 역사를 정리한 것이며, 특정 버전의 동작은 공식 문서를 확인해야 합니다.
댓글1
-
빗방울
2026.10.30 22:26
모든 이야기1502
가벼운 안부부터 깊은 생각까지, 함께 나눠요.
와 My가 마이가 아니었다니 처음 알았네요ㅋㅋ 딸들 이름으로 지었다는 게 낭만적이긴 함ㄷㄷ