블록체인과 분산 데이터베이스의 차이, MPT와 LevelDB 구조
블록체인과 분산 데이터베이스의 일관성·성능 차이를 비교하고 Merkle Patricia Trie와 LevelDB 기반 상태 저장 구조를 정리한다.
2026-08-14 · 최초 발행 2025-10-31
합의 원장과 분산 데이터베이스는 출발점이 다르다
블록체인은 탈중앙 합의로 순서를 정한 불변 원장과 상태(state) 스토어를 결합한다. PoS나 BFT 같은 합의 과정이 전역 순서를 결정하고, 결과는 블록 단위로 커밋된다. 트랜잭션 실행 결과는 상태 트라이(MPT)로 축약되며, 그 루트 해시는 블록 헤더에 고정된다.
분산 데이터베이스는 단일 조직이 관리하는 여러 노드의 스토리지와 쿼리 시스템이다. MVCC, 2PL, Raft/Paxos 리더십 같은 일관성 모델을 통해 ACID 또는 BASE를 달성하며, OLTP·OLAP 확장성과 낮은 지연, SQL 또는 키-값 인터페이스 제공이 목적이다.
MPT(Merkle Patricia Trie)는 Patricia의 경로 압축과 Merkle 해시 축약을 결합한, 해시로 검증 가능한 키-값 트라이다. 키를 니블 단위로 나누고 Branch, Extension, Leaf 노드로 구성하며, 루트 해시는 전체 상태를 대표하는 단일 커밋 증거가 된다.
LevelDB는 Google의 LSM-Tree 기반 키-값 저장소다. MemTable과 SSTable 계층, WAL, Compaction, Bloom Filter를 이용해 쓰기 작업을 최적화한다. Ethereum 등 클라이언트에서는 블록·상태·인덱스의 영속 저장소로 널리 사용되며, 클라이언트에 따라 RocksDB, MDBX, Pebble 등 다른 백엔드를 사용할 수 있다.
성능과 일관성에서 드러나는 선택 기준
| 항목 | 블록체인(퍼블릭) | 분산 데이터베이스(RDB/NewSQL) | 하이브리드(허가형 DLT) |
|---|---|---|---|
| 성능 | 수십 |
수만~수백만 TPS, 밀리초 지연 | 수천 |
| 확장성 | 네트워크·검증 비용 증가에 따른 제한 | 수평 확장 용이, 샤딩·리플리케이션 | 채널·파티션으로 업무 분리 확장 |
| 일관성 | 최종적 합의(Finality 지연) | 강한 일관성·조정 가능한 일관성 | BFT 합의로 빠른 파이널리티 |
| 안정성 | 비가변성, 암호학적 무결성 | 관리자 기반, 장애 도메인 좁음 | 거버넌스 기반 신뢰·감사 강화 |
| 운영 편의 | 운영 복잡도 높음, 노드·체인 동기화 | 성숙한 운영도구·모니터링 풍부 | 정책·권한 관리 필요, 교육 비용 |
블록체인에서는 트랜잭션이 메모리풀(Mempool)에 쌓인 뒤 합의로 순서가 정해지고, 정해진 순서대로 실행되어 블록으로 커밋된다. 결정적 실행을 사용하므로 전역 락 없이 상태를 전이할 수 있고, 블록 단위 커밋은 일괄 원자성을 보장한다.
분산 DB는 MVCC 또는 락 기반 트랜잭션 격리, 리더 복제와 쿼럼 커밋으로 낮은 지연의 강한 일관성을 제공한다. 반면 블록체인은 Append-only 원장과 상태 스냅샷 루트를 중심으로 동작하며, 롤백은 체인 재구성(리오그)으로 처리한다.
상태를 해시로 고정하고 디스크에 영속화하는 방식
MPT의 Branch 노드는 16-way 분기를 담당하고, Extension 노드는 경로를 압축하며, Leaf 노드는 키의 끝을 나타낸다. 노드는 RLP 등으로 직렬화한 후 해시된다. 특정 키를 검증할 때는 해당 경로의 노드 집합을 제공하면 되며, O(log N) 복잡도로 무결성을 확인할 수 있다.
LevelDB의 쓰기 경로는 Write-Ahead Log에서 MemTable, Flush를 거쳐 SSTable로 이어진다. 읽기는 MemTable, Block Cache, SSTable과 Bloom Filter를 차례로 활용한다. Compaction 정책은 읽기 증폭·공간 증폭·쓰기 증폭 사이의 트레이드오프를 결정하며, Snapshot과 Iterator는 안정적인 스캔을 지원한다.
블록 헤더·바디·인덱스는 block:<num>, tx:<hash> 같은 프리픽스 키로 구분할 수 있다. 상태 노드는 hash(node)를 키로, RLP(node)를 값으로 저장한다. 이 구조에서는 루트만 바뀌어도 전체 상태의 새 커밋을 표현할 수 있다.
트랜잭션이 상태 루트와 저장소에 반영되는 흐름
동시성은 결정된 순서를 따라 일괄 적용되므로 전역 레코드 락이 필요하지 않다. LevelDB 내부에서는 MemTable과 Compaction 단위로 락을 처리한다. 서명·논스·가스·revert 검증에 실패한 트랜잭션은 제외되며, 블록은 유효성 기준을 충족할 때 커밋된다.
무결성 검증과 분석 계층을 나누는 구성
기관 간 데이터 무결성 감사를 구성할 때는 내부 DB에서 자유로운 스키마와 고성능 처리를 유지하고, 핵심 요약치인 해시나 지표만 체인에 고정할 수 있다. 검증 시에는 오프체인 데이터를 다시 해시해 온체인 커밋과 대조하고 변조 여부를 확인한다.
경량 클라이언트는 전체 상태를 동기화하지 않고도 MPT 증명으로 특정 계정이나 스토리지 키를 검증할 수 있다. 모바일·엣지 환경에서는 네트워크 비용을 줄이면서 신뢰 최소화 모델을 유지하는 방식이다.
온체인 데이터를 분석 계층으로 옮길 때는 블록 동기화 후 LevelDB를 스캔하고, ETL을 거쳐 ClickHouse·BigQuery 같은 OLAP 저장소에 적재할 수 있다. 이벤트나 로그 인덱스를 다시 구축할 때도 LevelDB 키스페이스 범위 스캔을 활용한다.
허가형 DLT는 채널 또는 네임스페이스로 업무를 나누고 BFT 합의로 빠른 파이널리티를 확보한다. 스마트컨트랙트는 워크플로 엔진 역할을 맡고 감사 추적을 자동화할 수 있다.
장기 운영에서 관리해야 할 저장소 비용
LevelDB는 블록 캐시를 예를 들어 512MB~2GB로 설정하고, Bloom Filter를 켜며, Write Buffer 크기와 Compaction 우선순위를 조정할 수 있다. 쓰기 증폭을 낮추는 선택은 읽기 지연을 높일 수 있고, 공간 효율과 CPU·IO 사용량도 서로 맞바꿔야 한다.
동기화 노드의 백업에는 LevelDB 스냅샷 API나 ZFS/LVM 파일시스템 스냅샷을 사용할 수 있다. 재색인 비용을 고려해 정기 스냅샷 주기를 정하고, 원장 검증 절차도 함께 포함해야 한다.
키 프리픽스 체계는 고정하고 마이그레이션 도구를 제공하는 편이 좋다. 긴 수명의 체인에서는 Compaction의 장기 비용을 고려해야 하며, SSD/NVMe 같은 디스크 계층화도 운영 설계에 포함된다. 퍼블릭 체인은 파이널리티와 재구성 리스크를 평가하고, 허가형 체인은 키 관리·접근제어·감사 정책을 명문화한다. 배포 전에는 클라이언트별 스토리지 백엔드 호환성을 확인해야 한다.
MPT 증명 검증의 복잡도는 O(log N)이며, 라이트 클라이언트의 검증 비용을 줄인다. 허가형 BFT 환경에서는 2,00020,000 TPS와 0.53초 수준의 파이널리티가 가능하지만 네트워크와 합의 파라미터에 의존한다. 상태가 증분으로 증가하면 MPT 노드 수도 늘어나며, 프루닝·스냅싱으로 30~70% 디스크 절감 사례가 있다. 완전동기화 대신 스냅샷 복구를 사용하면 초기화 시간을 수일에서 수시간으로 단축할 수 있다.
Go와 LevelDB로 블록 및 상태 조각 저장하기
전제조건: Go 1.21+, 모듈 github.com/syndtr/goleveldb/leveldb, 로컬 디스크 권장(NVMe)
// go.mod: require github.com/syndtr/goleveldb/leveldb v1.0.0
package main
import (
"fmt"
"log"
"github.com/syndtr/goleveldb/leveldb"
)
func main() {
db, err := leveldb.OpenFile("./chain-data", nil)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 블록헤더 저장: key = "block:1000"
headerRLP := []byte{0xf9, 0x01, 0x23} // 예시 바이트
if err := db.Put([]byte("block:1000"), headerRLP, nil); err != nil {
log.Fatal(err)
}
// MPT 노드 저장: key = "mpt:<hash(node)>"
mptNodeHash := "0xabc123"
mptNodeRLP := []byte{0xc2, 0x01, 0x02}
if err := db.Put([]byte("mpt:"+mptNodeHash), mptNodeRLP, nil); err != nil {
log.Fatal(err)
}
// 조회
data, err := db.Get([]byte("block:1000"), nil)
if err != nil {
log.Fatal(err)
}
fmt.Printf("block:1000 size=%d\n", len(data))
}
키 프리픽스와 직렬화 포맷(RLP/SSZ/Proto)은 일관되게 유지해야 한다. block:0000~ 범위처럼 Iterator로 스캔할 때는 Snapshot을 사용한다.
온체인에는 핵심 거래 요약과 무결성 증거를 두고, 대량 조회와 분석은 오프체인에 맡기는 구성이 적합하다. MPT와 LevelDB의 조합은 상태 커밋 증거와 쓰기 최적화된 영속 저장을 함께 제공하며, 규모가 커질수록 Compaction·프루닝·스냅샷 전략이 운영의 중심이 된다.