Index
数据库管理系统(database management system
)主要是目的就是存数据和查询数据。
数据库是模块化的,主要分为几个部分:传输层接受查询,解析查询、生成计划(包括优化器)、执行器执行计划、存储引擎。
存储引擎(storage engine
)负载在内存中和磁盘上储、检索和管理数据。数据库执行的计算可能很复杂,不过存储引擎往往提供简单的增删改查的接口。从这个角度看,数据库可以看作是在存储引擎之上提供了 schema、查询语言、索引、事务等功能。
从存储引擎的角度看,可以将数据视为字节序列,上层的子系统来解释其语义。
已经有各种各样的存储引擎了,比如 LevelDB、RocksDB、LMDB 等等。它们是独立的,可以嵌入数据库中,因此数据库开发者可以在此基础上构建上层的子系统。存储引擎插件化可以使得在不同存储之间切换,选择适合应用场景的存储。
Comparing Databases
一旦选择了一个数据库,后续如果由于性能、一致性等问题要迁移到其他数据库,成本很大,因此选型要做充分的调研。
每一个数据库都有优势和劣势。从组件(存储引擎、数据共享、replica)、排名(DB-Engines、Database of Databases、ThoughtWorks)、实现语言等角度考察,只能得到非常粗浅的结论,比如选择 HBase 还是 SQLite,而了解数据库是如何工作的和内部原理能帮助做出结论。
比较之前应该定义目标,即使有轻微偏差也会让整个调研无效。最好的方式是模拟真实的工作负载来测试数据库。比如性能和扩展性等一些问题需要一段时间才能暴露,因此要尽可能的模拟真实情况。模拟真实情况不仅能帮助理解数据库的性能,还能学习到如何使用、如何 debug、社区是否友好有帮助。数据库的选择是各种因素下的总和判断,性能并不是唯一的评价标准,毕竟快但是丢数据比慢但是稳定的数据库差不多。
详细地定义当前需求和期望,可以帮助理解使用场景。比如
- schema 和 record 大小
- 客户端数量
- 查询类型和访问模式
- 读写查询的速率
- 这些值可能的变化情况
知道这些可以帮助回答下面这些问题。
- 数据库支持必须的查询吗?
- 数据库能存储期望的数据量吗?
- 单个节点能处理多少读写请求?
- 系统需要多少节点?
- 给定期望的增长率,如何横向扩展集群?
- 维护流程
一旦有了答案,可以构造测试集群来模拟工作负载。大部分的数据库都有压力测试工具来模拟各种使用情况。如果没有,那么要打个问号了。要极可能利用现有的工具。
测试过程也是一个熟悉组件、代码的过程。至少能够理解日志、可配置的参数、如何定位问题。如果能够将数据库作为黑盒使用是再好不过了,但是现实往往会出现 bug、宕机、性能回归或者其他问题,使得我们不得不去了解细节。
选择数据库是一个长期的事情,最好跟踪新版本发布,理解有哪些更新及其原因、升级策略。新版本往往包含性能提升、修复 bugs 和安全问题,不过也可能会引入新的 bug、性能回归、异常行为等,在上线前要经过严格测试。
Understanding Trade-Offs
开发一个存储引擎并不是实现一个教科书上的数据结构就可以了。需要考虑很多细节和边界情况:要组织物理数据布局,决定序列化格式,如何进行垃圾回收,如何适配到数据库语义中,并发环境如何工作,不会丢数据。
这些问题的决定需要权衡利弊。比如按插入顺序保存数据,插入很快但是想按照逻辑顺序读取就需要查询的时候排序。不同的方法会有不同的优势和劣势。如果对所用场景都是最优的,那么很简单大家用就好了。但是这样的存储引擎不存在,不得不根据自己工作负载选择合适的方法。
各种存储引擎都要面对同样的挑战和约束。存储引擎的设计人员的决策会让其更适合某种场景:有些为了读或写的延迟优化,有些为最大化存储量即存储密度,有些专注于操作的简单性。