当前位置:首页 > 焦点

底层数据库

你手机里那张照片,底层在屏幕上只是数据一堆像素,存进闪存芯片里,底层就是数据一连串0和1。但真正让这张照片能被找到、底层被修改、数据被删除的底层,是数据底层数据库在干活。它不直接跟人打交道,底层却决定了你每一次点击“保存”之后,数据数据到底去了哪。底层

我拆过一块老式机械硬盘。数据盘片亮得像镜子,底层磁头悬浮在纳米级的数据高度上,靠磁畴方向记录信息。底层数据库系统把数据拆成固定大小的页,比如InnoDB默认16KB一页。你写一条记录,它先塞进内存里的缓冲池,再按页刷到磁盘。刷的时候不是随便找个地方写,而是按B+树的结构定位到具体位置。树的分支节点存指针,叶子节点存真实数据,层数通常不超过四层。一亿条记录,查一条,最多读四次磁盘。这个设计从1970年代B树论文发表到现在,基本骨架没变过。

但磁盘太慢了。一次随机读写要几毫秒,CPU等得发慌。所以数据库在内存里搞了缓存,读过的页留着,写过的页攒着。你改一行数据,它先在内存里改,同时写一条日志到磁盘。日志是顺序追加的,比随机写快两个数量级。等系统闲下来,再把脏页慢慢刷回去。万一断电,重启时读日志重放,数据不会丢。这套机制叫WAL,Write-Ahead Logging。SQLite用它,PostgreSQL用它,几乎所有正经数据库都绕不开。

索引是另一回事。你给用户表的手机号建个索引,数据库不会去翻整张表。它单独维护一棵B+树,键是手机号,值是行号。查的时候从根节点往下走,三次磁盘IO找到叶子。但索引不是白建的。每插一条新用户,除了写主表,还要更新索引树,可能触发页分裂。页分裂就是把一个满页拆成两个,各占一半空间,再往上调整父节点指针。频繁分裂会让索引变得稀疏,查询变慢。所以批量导入数据时,老手会先删索引,导完再重建。

事务是数据库最拧巴的部分。你要转账,从A扣100,给B加100,两个操作必须一起成功或一起失败。数据库用锁和版本号来保证。InnoDB默认行级锁,改A行时锁住A行,别人读可以,改不行。但锁多了会死锁,两个事务互相等对方释放锁,谁也动不了。数据库有个死锁检测线程,发现环就回滚代价小的一方。你看到“Deadlock found”报错,就是它干的。

现在很多数据库把存储引擎换了。LSM树把随机写变成顺序写,先写内存表,满了就刷到磁盘成SSTable,后台再合并。LevelDB、RocksDB走这条路,写入吞吐比B+树高不少,但读放大严重,查一条可能要翻好几层。选哪个,看你是写多还是读多。

底层数据库没什么魔法。它就是在磁盘慢、内存快、断电会丢数据这三个约束下,用日志、索引、缓存、锁这几样东西反复权衡。你每次下单、发朋友圈、刷短视频,背后都是这些1970年代就定下来的老办法在撑。新硬件来了,参数调一调,骨架还是那副骨架。

分享到: