赞
踩
在大数据和AI时代,数据库成为各类应用不可或缺的重要组成部分。而数据库中的数据依赖存储引擎进行管理,包括数据的存储、查询、更新和删除等。因此,在设计系统时,选择正确的数据库存储引擎方案变得尤为重要。这篇文章将以关系型、NoSQL和NewSQL数据库,以及OLTP、OLAP和HTAP处理方式为切入点,深入探讨不同类型的数据库背后的存储引擎方案选型取舍。
下图展示了关系型数据库、NoSQL数据库、NewSQL数据库的发展过程。
关系型数据库也称为SQL数据库,最早的数据库发展可以追溯至1970年IBM研发的第一个SQL数据库System R,这也是最早的SQL数据库,再后来1980~1990年这段时间涌现出来了一些SQL数据库产品,例如Oracle、DB2、SQL Server、PostgreSQL、MySQL等。
到2000年左右,关系型数据库越来越丰富,出现了很多迄今一直在发挥重要的组件,例如MySQL、Oracle等。
SQL数据库按照以“行”为单位的二维表格存储数据,这种方式最符合现实世界中的实体,同时通过事务的支持为数据的一致性提供了非常强的保证。因此SQL数据库主要适合的场景是读多写少的场景。
关系型数据库中为了适配不同的应用场景,通常会将存储引擎设计为插件式的接口。然而主流的存储引擎,仍然是读多写少的特点。以MySQL为例,InnoDB存储引擎被广泛运用,它通过B+树来存储索引和数据。B+树这种数据结构,由于其独特的特性使得查询的性能非常高。
B+树存储引擎适用于需要高效的数据查找、范围查询和顺序访问的场景。它在关系型数据库中被广泛应用,如MySQL的InnoDB存储引擎和Oracle的B+树索引。然而,B+树存储引擎对于频繁的数据插入和删除操作可能会有一定的开销,因为这会触发节点的分裂和合并操作。
在面对海量数据存储、高并发访问的场景下,关系型数据库的扩展性和性能会受到限制。随着互联网的飞速发展,到2000年左右,存储海量数据、高并发处理读写的需求变得非常明显。这对SQL数据库提出了巨大挑战。为了解决这个问题,出现了支持数据可扩展性、最终一致性的NoSQL数据库。因此,NoSQL数据库可以看作是基于SQL数据库的缺陷而诞生的一种新产品。
NoSQL组件普遍选择牺牲复杂SQL的支持及ACID事务功能,以换取弹性扩展能力和更高的读写性能。这类系统主要存储半结构化或非结构化数据。根据存储的数据种类,NoSQL数据库主要分为基于文档存储的文档数据库(Document-based Database)、基于键-值存储的键值数据库(Key-Value Database)、图数据库(Graph-based Database)、时序数据库(Time Series Datebase)、宽列式存储(Wide Column-based Store)以及多模数据库(Multi-Model Database)。
不同类型的NoSQL数据库特性如下图所示。
NoSQL数据库典型的特点是具备很高的读写性能,但数据一致性保证较弱。绝大多数的NoSQL数据库适合写多读少、写多读多的场景。以列式数据库、时序数据库而言,它们通过LSM的思想,提供了非常高的写入性能。这类系统的存储引擎广泛意义上也称为LSM Tree存储引擎,这些系统单机的存储引擎有RocksDB、LevelDB等。此外再以键值数据库为例,它们绝大部分通过利用哈希表这种数据结构,外加内存介质存储数据。实现非常高的读写性能。Redis就是这类系统的典型代表。
虽然NoSQL数据库解决了关系型数据库存储的缺陷,但它也没法完全替代掉关系型数据库。在NoSQL数据库出现后的一段时间内,互联网软件的构建基本上都是结合二者来提供服务。在不同的场景下选择不同的数据库进行存储数据。虽然这样的合作方式很好,但是在这样的模式下,一个用户可能会因为场景的不同而存储多份相同的数据到不同的数据库中,当用户量级和存储数据量很小的情况下没什么问题。一旦量级发生变化就会引发出新的问题。
随着存储数据量的不断增加,造成资源的浪费和成本的上升不容忽略。于是工业界和学术界都在寻找更好的解决方案,直到2010年左右,诞生了NewSQL数据库(也称为分布式数据库)。它的出发点是结合关系型数据库事务一致性,又具备NoSQL数据库的扩展性及访问性能。这无疑给系统的设计及实现带来了更大的挑战,NewSQL数据库不仅要考虑单机环境下高效存储的问题,还需要考虑多机情况下数据复制、一致性、容灾、分布式事务等问题。目前NewSQL数据库典型的代表作有TiDB、OceanBase、CockroachDB等。NewSQL数据库中绝大部分的系统还是采用LSM 树存储引擎,来实现系统高性能的写入。
在现代数据管理领域,OLTP、OLAP和HTAP是常见的数据库类型,它们各自针对不同的数据处理场景和需求。本文将对这三种数据库进行对比,以帮助读者更好地理解它们的特点和适用性。
OLTP数据库(联机事务处理)是专门设计用于处理事务性工作负载的数据库系统。它们被广泛应用于业务应用程序,如在线购物、银行交易和订单处理等。OLTP数据库的主要特点是高并发、低延迟和高事务吞吐量。它们通过支持ACID(原子性、一致性、隔离性和持久性)特性来确保数据的一致性和可靠性。OLTP数据库通常采用规范化的数据模型,以支持高效的事务处理和即时的数据更新。
OLTP数据库主要的功能是处理用户在线实时的请求,直接为用户提供服务,因此这类数据库通常对处理请求的时延要求比较高,绝大部分的请求正常情况下会在毫秒级完成。OLTP数据库很多,除了大家最熟悉的关系型数据库(如MySQL、Oracle)外,还有Redis、MongoDB等这些非关系型数据库。绝大部分的OLTP数据库则是采用B树、B+树甚至哈希表来构建存储引擎。
OLAP数据库(联机分析处理),它们专注于支持决策支持和分析工作负载。OLAP数据库用于处理大量数据的复杂分析查询和报表生成。OLAP系统的关键特点是高度可扩展、支持复杂的分析操作和提供灵活的数据聚合能力。为了实现这些特性,OLAP数据库通常采用了针对分析查询优化的特殊数据结构,如多维数据模型(如星型或雪花模型)和列存储技术。此外,OLAP数据库还提供了灵活的查询语言和数据切片、切块、钻取等功能,以支持交互式的数据分析和探索。
OLAP数据库在功能上侧重于对数据或者任务进行离线处理,它不直接对用户提供服务。OLAP系统对请求的处理通常比OLTP慢得多,一般在秒级、分钟级甚至小时级,通常在数据统计、报表分析、推荐系统数据聚合分析等场景用的比较多。这一类数据库典型的代表有HBase、Teradata、Hive、Presto、Druid、ClickHouse等。互联网企业往往都需要使用OLTP和OLAP。因此为了满足这两类需求,通常需要结合多个系统一起开发使用。这样的做法当然是可行的,而且基本也是采用这种方式进行实现。绝大部分的OLAP数据库是采用LSM树构建存储引擎。
随着数据处理需求的不断演变,需要存储的数据量爆炸式增长,在这种模式下直接带来的存储成本问题成为新的矛盾点,人们开始探索是否能诞生一种数据库将OLTP和OLAP这两类应用合二为一呢?于是,HTAP(混合事务/分析处理)数据库应运而生。HTAP数据库旨在将OLTP和OLAP的功能集成到同一个数据库系统中,以满足实时分析和事务处理的需求。HTAP数据库通过在同一数据库上同时支持事务处理和分析查询,消除了数据复制和数据移动的需求,提供了更高的数据一致性和实时性。HTAP数据库通常采用了内存计算、分布式架构和智能查询优化等技术,以保证高性能和灵活性。这类数据库既可以处理在线事务处理,又可以处理在线分析处理。可以认为HTAP=OLTP+OLAP。HTAP的主要代表有TiDB、OceanBase、CockroachDB等。
在选择数据库时,需要考虑具体的业务需求和性能要求。如果您需要处理大量的事务性工作负载,如在线交易,那么OLTP数据库是一个理想的选择。如果您的需求是进行复杂的数据分析和报表生成,那么OLAP数据库可能更适合。而如果您需要同时满足实时分析和事务处理的需求,那么HTAP数据库是一个值得考虑的选项。
总而言之,OLTP、OLAP和HTAP数据库各自针对不同的数据处理场景和需求。了解它们的特点和适用性,可以帮助您在选择数据库时做出明智的决策,并确保满足业务的需求和性能要求。
如果以组件的类型是关系型数据库还是非关系型数据库,并结合服务的场景是OLTP还是OLAP来对业界各种存储组件进行划分的话,可以得到如下图所示的结果。关系型数据库中既有为OLTP设计的,也有为OLAP设计的,同时还有新兴发展起来兼容二者的HTAP数据库。这些系统都有各自适用的业务场景,它们在存储引擎选型时,往往会根据适用场景来决定。如果是读多写少的场景,通常会选择B+树、哈希表来构建存储引擎。而如果是写多读少的场景,往往会选择LSM树来构建存储引擎。
《深入浅出存储引擎 》
带你吃透存储引擎底层原理与实践技巧,攻克业务难题。通过阅读本书,读者不仅能对存储引擎,尤其是单机的存储引擎有一个整体的框架,而且能对两类存储引擎的实现思路及背后原理有个深刻的掌握,只有深刻理解了存储引擎的背后实现原理,读者不仅可以自己动手开发自己的存储引擎,更可以很快掌握关系型数据库或者NoSql这类组件的核心原理,对未来实际应用与开发提供参考。**
购书链接:点此进入
关于作者:文小飞 (网名:jaydenwen/jaydenwen123),大厂资深研发工程师、公司级讲师。曾就职于腾讯等互联网公司,从事基础架构、后端开发、推荐系统架构等工作,具有丰富的基础架构经验。对技术充满热情,尤其对存储引擎、分布式共识算法等技术有较为深入的理解,曾编写开源书籍“自底向上分析 BoltDB 源码”,并发布“数据存储与检索”等网络课程。
文章目录
前言
第1章 存储引擎概述1
1.1 数据存储体系1
1.1.1 OLTP、OLAP与HTAP1
1.1.2 关系数据库、NoSQL数据库与
NewSQL数据库2
1.1.3 内存型存储组件与磁盘型存储
组件8
1.1.4 读多写少组件、写多读少组件
和读多写多组件9
1.1.5 数据存储与检索10
1.2 数据存储的核心:存储引擎10
1.2.1 存储引擎整体架构10
1.2.2 存储引擎的共性问题13
1.3 存储引擎的分类13
1.3.1 读多写少:基于B+树的存储
引擎14
1.3.2 写多读少:基于LSM派系的
存储引擎15
1.4 小结17
第2章 索引数据结构18
2.1 基础数据结构18
2.1.1 数组18
2.1.2 链表20
2.2 Hash类数据结构22
2.2.1 Hash表22
2.2.2 位图27
2.2.3 布隆过滤器28
2.3 二叉树类数据结构32
2.3.1 二叉搜索树33
2.3.2 红黑树36
2.3.3 跳表45
2.4 多叉树类数据结构48
2.4.1 B树49
2.4.2 B+树57
2.4.3 其他多叉树61
2.5 小结61
第3章 数据存储介质64
3.1 内存65
3.1.1 内存的基本内容65
3.1.2 内存管理机制69
3.1.3 虚拟内存管理机制80
3.2 持久化内存92
3.3 磁盘96
3.3.1 磁盘的基本内容97
3.3.2 磁盘管理机制102
3.3.3 加速磁盘访问的方案111
3.4 小结112
第4章 从宏观角度理解B+树存储
引擎的原理113
4.1 B+树存储引擎产生的起点114
4.1.1 诞生的背景114
4.1.2 设计的目标116
4.2 B+树存储引擎方案选型117
4.2.1 数据结构方案对比117
4.2.2 目光转向磁盘118
4.2.3 索引维护和存储121
4.2.4 选择B树还是B+树125
4.3 B+树存储引擎方案选型结果128
4.3.1 方案选型结果128
4.3.2 反向论证130
4.4 小结130
第5章 从微观角度理解B+树存储
引擎的工程细节132
5.1 边界条件处理132
5.1.1 B+树在磁盘和内存中的映射132
5.1.2 读操作的处理133
5.1.3 写操作的处理137
5.2 异常情况处理154
5.2.1 异常情况总体分析154
5.2.2 数据部分写入的异常处理156
5.3 事务158
5.3.1 事务的基本概念158
5.3.2 并发控制160
5.4 范围查询与全量遍历170
5.5 小结171
第6章 BoltDB核心源码分析172
6.1 BoltDB整体结构172
6.1.1 BoltDB项目结构172
6.1.2 BoltDB整体实现架构173
6.2 page解析175
6.2.1 page基本结构176
6.2.2 元数据页177
6.2.3 空闲列表页179
6.2.4 分支节点页183
6.2.5 叶子节点页186
6.3 node解析187
6.3.1 B+树结构概述187
6.3.2 node结构分析187
6.3.3 node的增删改查189
6.3.4 node分裂190
6.3.5 node合并195
6.4 Bucket解析199
6.4.1 Bucket结构分析199
6.4.2 Bucket遍历的Cursor核心
结构分析201
6.4.3 Bucket的增删改查206
6.4.4 KV数据的增删改查210
6.4.5 Bucket的分裂和合并211
6.5 Tx解析213
6.5.1 Tx结构分析213
6.5.2 Commit()方法分析214
6.5.3 Rollback()方法分析217
6.6 DB解析219
6.6.1 DB结构分析219
6.6.2 Open()方法分析221
6.6.3 Begin()方法分析224
6.6.4 Update()和View()方法分析226
6.6.5 Batch()方法分析227
6.7 小结229
第7章 深入理解LSM Tree原理232
7.1 LSM Tree的发展背景232
7.2 从零推导LSM Tree234
7.2.1 存储介质的选择234
7.2.2 写请求的处理234
7.2.3 读请求的处理239
7.3 LSM Tree的架构演进240
7.3.1 数据更新分类240
7.3.2 双组件LSM Tree结构241
7.3.3 多组件LSM Tree结构242
7.3.4 实际的LSM Tree结构243
7.4 LSM Tree的核心问题245
7.4.1 数据压缩/合并245
7.4.2 数据分区246
7.4.3 读放大、写放大和空间放大249
7.4.4 写放大优化251
7.5 小结252
第8章 LSM派系存储引擎253
8.1 LSM Tree存储引擎253
8.1.1 LSM Tree工程应用253
8.1.2 LSM Tree的KV分离存储
技术WiscKey256
8.2 LSM Hash存储引擎264
8.2.1 LSM Hash的起源264
8.2.2 Bitcask的核心原理265
8.3 LSM Array存储引擎269
8.3.1 LSM Array的设计思想269
8.3.2 Moss的核心原理270
8.4 其他LSM存储引擎274
8.4.1 LSM存储引擎扩展274
8.4.2 消息队列Kafka存储引擎275
8.5 小结277
第9章 LevelDB核心源码分析278
9.1 LevelDB概述278
9.1.1 LevelDB整体架构279
9.1.2 LevelDB项目结构280
9.2 DB核心接口分析282
9.2.1 DB结构282
9.2.2 Open(options,dbname,dbptr)
的实现284
9.2.3 Put(k,v)和Delete(k)的实现285
9.2.4 Get(k)的实现292
9.3 MemTable的实现分析294
9.3.1 MemTable结构294
9.3.2 Add(k,v)和Get(k)的实现295
9.3.3 SkipList结构297
9.4 WAL日志的实现分析302
9.4.1 WAL日志的格式302
9.4.2 Writer的实现303
9.4.3 Reader的实现307
9.5 SSTable的实现分析311
9.5.1 SSTable的数据格式312
9.5.2 Block的写入和读取316
9.5.3 SSTable的写入和读取325
9.5.4 SSTable的读取全过程334
9.6 Compact的实现分析338
9.6.1 Compact过程339
9.6.2 Minor Compact340
9.6.3 Major Compact 343
9.7 多版本的实现分析352
9.7.1 Version和VersionEdit结构352
9.7.2 VersionSet结构356
9.8 小结361
后记362
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。