1. 存储结构的两种范式之争
第一次接触数据库存储引擎时,我被一个看似简单的问题困扰了很久:为什么同样的数据,有人用行存,有人用列存?直到亲手实现过一个简易的OLTP系统后,才真正理解这两种存储范式背后的设计哲学。就像选择交通工具一样,骑自行车和坐地铁各有适用场景——行存就像灵活的自行车,适合随时存取完整数据;列存则像高峰期的地铁,专为大批量定向查询优化。
在行式存储中,数据按记录为单位连续存储。想象一个员工表,每个员工的ID、姓名、部门、工资等信息会打包存放在一起,就像把一个人的全部档案装进一个文件袋。这种布局使读取完整记录非常高效,因为一次磁盘I/O就能获取该行所有列的值。PostgreSQL的堆表、MySQL的InnoDB都是典型行存代表。
而列式存储采用了完全不同的思路。它把同一列的所有值连续存储,相当于把所有人的工资单独存成一个文件,再把所有部门信息存成另一个文件。这种结构在分析"全公司工资总额"这类查询时优势明显,因为只需读取工资列而无需加载无关数据。ClickHouse、HBase的列族设计都是列存的实践案例。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 行式存储的深度解析
2.1 物理存储结构剖析
行存数据库的物理存储通常由三部分组成:
- 固定长度的元信息头(包含事务ID、指针等)
- 列值数据区(变长字段会有额外偏移量记录)
- NULL值位图(标记各列是否为NULL)
以MySQL InnoDB为例,其行格式(ROW_FORMAT)有Compact/Dynamic等选项。Dynamic格式下,变长字段会完全存放在溢出页,主记录中仅保留20字节指针。这种设计使得主记录大小固定,有利于提升缓存命中率。
实际案例:某电商订单表采用Dynamic行格式后,TPS从1200提升到2100,因为更多行能放入缓冲池
2.2 写入优化技术
行存系统为提升写入性能采用了若干关键技术:
- WAL机制:先写日志再改数据,保证崩溃恢复能力
- MVCC实现:通过回滚段实现多版本并发控制
- 填充因子:预留空间减少页分裂,如PostgreSQL的fillfactor参数
sql复制-- PostgreSQL创建表时指定填充因子
CREATE TABLE orders (
id SERIAL PRIMARY KE
