本文转载自微信公众号“吴佩轩”,作者AlexPetrov。转载本文请联系吴佩萱公众号。任何数据库管理系统的主要工作都是“可靠地存储数据”并使其可供用户使用。我们使用数据库作为主要数据源,帮助我们在应用程序的不同部分之间共享数据。我们使用数据库,而不是在每次创建新应用程序时寻找存储和检索信息的方法,或者每次都发明一种新的方法来组织数据。这样,我们就可以专注于应用程序逻辑而不是基础设施。数据库是由多个部分组成的模块化系统:接受请求的传输层、确定运行查询的最有效方式的查询处理器、执行操作的执行引擎和存储引擎。存储引擎(或数据库引擎)是数据库的一个软件组件,负责在内存和磁盘中存储、检索和管理数据,旨在在每个节点[REED78]持久保存数据。数据库可以响应复杂的查询,而存储引擎将以更细粒度的级别查看数据,并提供一组简单的数据操作API,允许用户创建、更新、删除和检索数据记录。从某种角度来看,数据库是建立在存储引擎之上的应用程序,它提供表结构(模式)、查询语言、索引、事务和许多其他有用的功能。为了灵活性,键和值都可以是没有预设格式的任意字节序列。它们的排序和表示语义在更高级别的子系统中定义。例如,您可以在一个表中使用int32(32位整数)作为键,在另一个表中使用ascii(ASCII字符串);从存储引擎的角度来看,这两个键都只是序列化的条目。BerkeleyDB、LevelDB(及其后代RocksDB)、LMDB(及其后代libmdbx、Sophia和HaloDB)等存储引擎是独立于它们现在嵌入的数据库开发的。使用可插拔存储引擎使数据库开发人员能够使用现有的存储引擎构建数据库系统并专注于其他子系统。同时,数据库系统组件之间的明确解耦提供了在不同引擎之间切换的机会,每个引擎可能都适合特定的用例。例如:流行的数据库MySQL有多个存储引擎,包括InnoDB、MyISAM和RocksDB(在MyRocks发行版中),而MongoDB允许在WiredTiger、内存和(现已弃用的)MMAPv1存储引擎之间切换。数据库的比较会对数据库系统的选择产生长期的影响。如果选择的数据库不合适(因为它会导致性能问题、一致性问题或操作挑战),那么最好在开发周期的早期就发现这一点,因为迁移到不同的系统可能并不容易,甚至在在某些情况下,您还需要对应用程序的代码进行重大更改。每个数据库系统都有优点和缺点。为了降低代价高昂的迁移风险,您可以花一些时间来选择数据库,以确保它具有满足您的应用程序需求的能力。尝试根据数据库的组件(例如使用的存储引擎、数据共享、复制和分发的方式等)、它们的排名(由ThoughtWorks等咨询机构或DB-Engines和DatabasesofDatabases)values)或实现语言(C、Java或Go等)来比较数据库可能导致无效和过早的结论,这些方法只能用于高层比较,并且可能出现,对于例如,在HBase和SQLite之间进行选择时,这样一个粗略的比较。因此,即使只是粗略地了解每个数据库的工作原理及其内部结构,也可以帮助您得出更可靠的结论。每次比较都应从明确定义的目标开始,因为即使是最小的偏差也会使整个调查完全无效。如果您正在寻找一个非常适合您当前(或未来)工作负载的数据库,您能做的最好的事情就是在不同的数据库系统上模拟这些工作负载,测量对您重要的性能指标,然后比较结果。一些问题(尤其是性能和可伸缩性方面的问题)只会在一段时间后或随着容量的增长才开始出现。为了发现潜在的问题,最好在尽可能接近真实生产环境的环境中进行长期运行测试。模拟真实世界的工作负载不仅可以帮助您了解数据库的运行方式,还可以帮助您了解如何操作和调试它,并了解其社区的友好程度和乐于助人的程度。数据库的选择总是综合了这些因素,性能通常不是最重要的方面:使用保存数据慢的数据库通常比丢失数据快的数据库要好得多。要比较数据库,详细了解用例并定义当前变量和预期变量会很有帮助,例如:表结构和记录大小客户端数量查询类型和访问模式读写查询率任何这些变量的预期变化识别这些变量可以帮助回答如下问题:数据库是否支持所需的查询?数据库能否处理我们计划存储的数据量?单个节点可以处理多少次读写?一个系统规划了多少个节点?给定预期增长率,我们如何扩展集群?维护过程是怎样的?回答完这些问题后,您可以构建一个测试集群并模拟您的工作负载。大多数数据库已经具有可用于重现特定用例的压力测试工具。如果没有用于在数据库生态系统中生成真实随机工作负载的标准压力测试工具,那么这可能是一个危险信号。如果有什么东西阻止您使用数据库附带的工具,那么您可以尝试现有的通用工具,或者从头开始实施一个。如果测试结果令人满意,进一步熟悉数据库代码可能会有更大的帮助。为了阅读源代码,首先了解数据库的各个部分,如何找到不同组件的源代码,然后浏览这些组件。即使只是粗略地了解数据库代码库,也可以帮助您更好地理解它产生的日志和配置参数,并帮助您发现使用数据库的应用程序中的问题,甚至是数据库代码本身的问题。有些人认为在不知道其内容的情况下将数据库视为黑盒是一件好事。但实践往往表明,迟早会遇到错误、服务中断、性能下降或其他问题。你最好为这些问题做好准备,如果你知道并了解数据库的内部结构,你可以降低业务风险并更有可能快速恢复。用于基准测试、性能评估和比较的流行工具是Yahoo!云服务基准(YCSB)。YCSB提供了一个框架和一组可以应用于不同数据存储的通用工作负载。与任何通用的东西一样,您应该谨慎使用此工具,因为使用它很容易得出错误的结论。为了做出公平的比较并做出明智的决定,您需要投入足够的时间来了解您的数据库将在其中运行的真实环境,并相应地调整基准测试的内容。TPC-C基准事务处理性能委员会(TPC)提供了一组基准,数据库供应商使用这些基准来比较和宣传其产品的性能。TPC-C是一种联机事务处理(OLTP)基准,混合了只读和更新事务以模拟常见的应用程序工作负载。该基准测试侧重于执行的并发事务的性能和正确性。主要性能指标是吞吐量:数据库系统每分钟能够处理的事务数。它要求执行事务具有ACID属性并符合基准本身定义的属性集。该基准测试不关注任何特定的业务线,而是提供一组抽象的操作,这对适用于OLTP数据库的大多数应用程序都很重要。它包括几个表和实体,例如仓库、库存、客户和订单,并指定表布局、可以在表上执行的事务的详细信息、表的最小行数和数据持久化约束。这并不意味着基准只能用于比较数据库。基线可用于定义和测试服务级别协议注1的详细信息,了解系统要求、容量规划等。在使用数据库之前,您对数据库的了解越多,在生产中运行时节省的时间就越多。选择数据库是一项长期决策,最好跟踪新版本以准确了解更改内容和原因,并制定升级策略。新版本通常包含对错误和安全问题的改进和修复,但也可能引入新错误、性能回归或意外行为,因此在部署新版本之前对其进行测试也很重要。查看数据库开发人员过去如何处理升级可能会让您对未来发生的事情有一个很好的了解。过去的顺利升级并不能保证以后的升级也一样顺利,但过去复杂的升级也可能预示着未来的升级不会一帆风顺。了解权衡作为用户,我们可以看到数据库在不同条件下的行为,但在使用数据库时,我们必须做出直接影响其行为的选择。设计存储引擎肯定比仅仅实现教科书数据结构更复杂:很难在一开始就获得许多细节和边缘情况。我们需要设计物理数据布局和组织指针,确定序列化格式,了解数据将如何被垃圾收集,存储引擎如何适应整个数据库系统的语义,探索如何使其在并发中工作环境,最后确保在任何情况下都不会丢失数据。不仅有很多事情需要决定,而且大多数决定都涉及权衡取舍。例如,如果我们按照数据插入数据库的顺序保存数据,我们可以更快地存储它们;但是如果我们按字典顺序检索它们,我们必须在将结果返回给客户端之前重新排序。正如您将在本书中看到的那样,存储引擎设计有许多不同的方法,每个实现都有自己的优点和缺点。当我们检查不同的存储引擎时,我们会讨论它们的优点和缺点。如果每个可能的用例都有一个绝对最佳的存储引擎,那么每个人都会使用它。但是不存在这样的存储引擎,因此我们需要根据服务的工作负载和用例来明智地选择。市场上有许多存储引擎,它们使用各种数据结构,并以不同的语言实现——从C等低级语言到Java等高级语言。所有存储引擎都面临着相同的挑战和限制。可以类比为城市规划:我们为一定的人口规模建造一座城市,然后选择是否扩大城市的高度或面积。这两种情况都可以让相同数量的人进入城市,但这些方法导致了截然不同的生活方式。当城市建在高处时,人们住在公寓里,人口密度会导致较小区域的交通增加;而在一个更大、更分散的城市,人们更可能住在大房子里,但通勤需要更长的距离。同样,存储引擎的开发人员做出设计决策,使它们更适合不同的情况:一些优化低读写延迟,另一些试图最大化存储密度(每个节点存储的数据量),而一些则侧重于操作简单性。
