Algorithms + Data Structures = Programs —— Niklaus Wirth 先搬出写了 Pascal、又拿了图灵奖的大师的名言,这里不讨论该断言的合理性,只作为本文的引子。在数据库领域,究其原理,绕不开数据存储。本文从几个经典应用出发,管窥数据结构在存储中所起到的作用。 引入 通常,像电商交易、支付系统的后端服务,往往是 I/O 密集型,I/O 性能会制约整个服务的性能。因此后端工程师往往会非常关注数据库的读写 TPS,IOPS 等监控指标。在特定的技术环节,比如为数据库表设计索引,用 Kafka 为秒杀场景提供大并发写的吞吐,这些大家非常熟悉的技术,其实都包含了对数据结构的运用。 磁盘 I/O 对数据库的读写本质是磁盘 I/O(不考虑 Redis 等内存数据库)。在讲存储中的数据结构之前,先简单介绍磁盘 I/O 特性对数据库设计的影响,详细的解释可以阅读美团技术博客 《磁盘 I/O 那些事》[1]。 传统意义上数据库需要将数据以文件的形式存储到磁盘上,对数据库的操作可以简单抽象为对磁盘文件的读写,而磁盘 I/O 耗时会影响数据库的读写性能。一次磁盘 I/O 可以拆分为:磁头寻道时间 seek time、扇区旋转延迟时间 latency time、数据传输时间 transmission time,参考下图示意。 磁头寻道:磁头移动到数据所在磁道位置,参考值 3-15ms; 扇区旋转:通过盘片的旋转,将数据所在扇区移动到磁头下方,参考值 2-5ms; 数据传输:数据通过系统总线从磁盘加载到内存的时间,通常小于 1ms; 寻道时间 > 旋转延迟 >> 数据传输,所以磁盘 I/O 耗时 ≈ seek time + latency time,IOPS = 1000 / (seek time + latency time)。 我们已经知道,磁盘的顺序 I/O 比随机 I/O 更快(参考下图),原因在于顺序 I/O 磁头几乎不用换道,或者换道的时间很短;而随机 I/O 磁头则会频繁换道。对随机 I/O 而言,7200 转的磁盘,随机 I/O 的 IOPS 通常为 70 ~ 80。而对顺序 I/O,比如读取一块连续存储的文件,理想的情况是在一次 I/O 后就可以顺序读写,其 IOPS 会非常高。因此包括数据库在内,但凡涉及存储,都会考虑更多利用顺序 I/O 来提高性能。 相对于读,写的性能瓶颈会更凸显,因此对存储的设计会优先考虑提升写性能。由于文件系统保证了文件是顺序写入,所以可以采用追加写入文件的方式实现顺序写。由于是对文件的增量追加写入,所以在数据读取时,需要倒过来检索,从最新的文件逐个往回查,比如通过简单的二分,二叉搜索树等算法进一步优化,减少 I/O 次数,归根结底性能优化