RocksDB 事务实现和应用场景

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
云数据库 RDS MySQL Serverless,价值2615元额度,1个月
简介: RocksDB 事务实现和应用场景

建议先阅读我的博客 RocksDB 架构

RocksDB 应用于分布式场景,底层通过 LSM - Tree 实现,分布式场景 LSM - Tree 这种结构的原因在于:

  • 顺序写:写速度快
  • 紧凑型存储:空间占用少,数据压缩比高

1、RocksDB 特性

1.1、列族

列族,CF column famliy。DB 的每个键值对都与唯一一个列族结合。若不指定列族,则与default结合

可以这样理解,DB 对应着数据库中的库,CF 对应着数据库中的表。实际上,一个列族可以是一个表,也可以是表的一个索引。一个列族里面可以包括多个表的数据。每个列族有一棵 LSM-Tree 和多个 Memtable。

列族提供了一种数据库逻辑分片的方法

  • 支持跨列族的原子写
  • 跨列族的一致性视图:快照
  • 允许对不同列族进行不同的配置。类比 Mysql 不同的表可以定制不同的存储引擎。
  • 可以快速添加 | 删除列族。Mysql DDL 操作代价较高,需要服务器在预启动时创建表的结构,而在分布式场景下,需要即时 DDL 操作。

如图所示:一个DB 的多个列族共享一个 WAL 日志,但是不共享 Memtable 和 SST 文件。通过共享 WAL 日志实现了原子写。通过隔离 Memtable 和 SST 文件,可以独立配置每个列族并且快速删除它们。

当一个列族刷盘时,创建一个新的 WAL 文件。此后,DB 中所有列族都会写入这个 WAL 文件。由于 WAL 共享,所以只有当所有的列族都把这个 WAL 里的数据刷盘了,才能删除该 WAL 文件。

1.2、快照

快照,snapshot。一个快照会捕获在创建的时间点的 DB 的一致性视图。快照在 DB 重启之后将消失。

一个快照相当于一个 SnapshotImpl 类的对象。其中用于实现 MVCC 的字段是:

  • number_:序列号,DB 下全局自增。
  • unix_time_:DML 操作的时间

快照的本质是双向链表,其应用主要体现在迭代器和事务上。

1.2.1、迭代器

迭代器读取一个指定快照中的数据。

1.2.2、事务

RocksDB 支持事务。

  • 悲观事务 TransactionDB:预写时加锁,适用于高并发写冲突大的场景。例:mysql
  • 乐观事务 OptimisticTransactionDB:预写时不加锁,只在提交事务时检查冲突,有冲突就丢弃修改。适用于写冲突小的场景,例:redis

事务的读流程

首先查找本事务对应的 Writebatch 中是否存在请求的数据。接着跳表查找内存的 Memtable(active + immutable );若不存在,则基于 SST 文件元数据查找是否缓存在 Block Cache 中;若没有被缓存,则读磁盘的 SST 文件,找到后并加载到 Block Cache 中。为了提高查找效率,会借助布隆过滤器,避免无效的数据 IO 和遍历操作。

事务的写流程

每个事务都有一个 Writebatch 对象,用于缓存该事务在提交前修改的所有数据。当通过 WriteBatch 写入多个 key 的时候,RocksDB 提供原子化操作。在事务提交时,先写入 WAL 日志,再写入内存可写的 Memtable。当 Memtable 达到阈值后会变为只读的 immutable,此时再新生成一个 Memtable。

数据写入 Memtable 后就意味着事务已经提交。数据的持久化和 compaction 都是异步进行的。当 Immutable Memtable 的数量达到阈值后,会被刷成 L0 SST 文件。在 L0 文件个数达到阈值后,合并到 L1 上并依次往下刷。RocksDB 中可以配置多个线程用于对每层数据文件进行 compaction。

事务的实现方式,见本文第三部分 MyRocks。

2、Pika

pika 是 360 开源的可持久化的大容量类 redis 数据库。

image.png

pika 特点

  • 容量大,支持百 G 数据量的存储
  • 实现 redis 协议。兼容 redis。
  • 支持主从
  • 数据存储使用 rocksdb

2.1、背景

内存数据库的缺陷:当数据量足够大时,导致内存不够用

  • 容量瓶颈:fork 写时复制,内存趋于饱和
  • 加载慢:数据库重启,需要加载所有的数据
  • 占用带宽:主从复制,在全量复制时,会严重占用带宽

若减少使用 List 命令,pika 可以达到 redis 80% 的性能。

2.2、安装编译

# 安装 lib
 sudo apt-get install libgflags-dev libsnappy-dev
 sudo apt-get install libprotobuf-dev protobuf-compiler
 sudo apt install libgoogle-glog-dev
 git clone https://github.com/OpenAtomFoundation/pika.git
 cd pika
 git submodule update --init
 git checkout -b v3.4.0
 make

使用

./output/bin/pika -c ./conf/pika.conf

2.3、* Blackwidow

Blackwidow 本质是基于 RocksDB 的封装,使只支持 KV 存储的 RocksDB 能够支持多种数据结构。

  • Redis 的对象类型对应着 DB
  • KV 分离存储分别对应着列族

String

String 由 1 个列族组成,RocksDB 落盘方式:

image.png

解释:value 增加了 4 B用于存储 timestamp 用于实现 expire 功能。每当获取一个 string 对象,首先解析 value 后 4 B,获取到 timestamp 后作出判断返回结果(0 - 未设置超时时间)。

Hash

哈希表由两个列族组成

  • 元数据 (meta_key, meta_value):存储哈希表的信息。
  • 普通数据(data_key, data_value):存储对应的 field 和 value。

RocksDB 落盘方式

image.png

解释:hash_size 用于实现 hlen 命令。

版本号version指的是快照的序列号,用于标记删除,真正的删除操作是在 compaction 过程中。元数据中 key 对应的 value 中的 version 记录了最新的有效版本信息

  • 若元数据 key 对应的 version 是一个无效版本信息,说明整个数据结构已被删除
  • 若普通数据某个字段 field 的 version 大于 key 的 version,说明该字段结点已被删除

由于 String 结构只有一个列族,可以直接删除;对于其他的数据结构,存在多个列族,需要版本号来确定是删除整个 key,还是删除其中的一个 field-value。而对于时间戳timestamp,只有 key 存在过期时间。

list

list 由两个列族组成:元数据和普通数据。

RocksDB 落盘方式

解释:list_size 实现 llen 命令,left_index 和 right_index 用于实现 lpush 和 rpush 命令。index 实现有序。

Set

list 由两个列族组成:元数据和普通数据。

RocksDB 落盘方式

解释:set_size 实现 scard 命令

Zset

list 由三个列族组成:元数据和普通数据。

RocksDB 落盘方式

解释:第 3 列族用于 zset 排序,按 score 排序,若 score 相等,对 member 来排序。

version 如何用于删除

3、MyRocks

MyRocks 将 RocksDB 替换 InnoDB 作为 Mysql 存储引擎 。

优点:

  • 顺序写:写性能更好,但是 InnoDB 读性能远大于 RocksDB。
  • 紧凑存储:占用更少的存储空间,具备更高的压缩效率,每一层级的空间浪费控制在 10% 以下;而对于 InnoDB B+ 树来说,若一个节点数据满触发分裂,造成 50% 空间浪费,存在内部碎片。

适用场景

  • 大数据量业务。占用更少的存储空间,具备更高的压缩效率
  • 写密集业务。rocksdb 顺序写,append 的方式记录 DML 操作,适用于批量插入和更新频繁的业务场景。

3.1、事务实现

事务实现的核心

  • 序列号 sequence number:RocksDB 中的每一条记录都有一个序列号 sn,存储在记录的 key 中
  • 快照 snapshot:快照,与序列号一一对应,对于一个序列号为 sn 的快照来说,只能看到小于或等于 sn 的快照记录,大于 sn 的快照记录不可见
InternalKey:| User key (string) | sequence number (7 bytes) | value type (1 byte) |

隔离级别的实现

  • RC:每个 DML 操作,创建 snapshot。每次读取数据时,生成新的 snapshot。
  • RR:只有在事务开启时创建 snapshot。没有解决幻读问题,只在主键实现了间隙锁。

MVCC的实现

序列号 sn 提供了记录的多版本信息。当查询记录时,不需要加锁,而是根据当前序列号 sn 创建一个快照,查询过程中只能读取小于或等于 sn 的快照记录,查询结束后释放 snapshot。

  • InnoDB 的快照缺陷:undo log 存储所有旧版本的事务,这是因为 undo log 是通过链表实现的,即时有些版本已经没有事务在引用,依然保留,无法删除,占用空间大,读取效率低。
  • MyRocks 的快照优化:compaction 操作时会删除中间版本,仅保留当前活跃事务可见版本和记录最新的版本,在满足 MVCC 的基础上,减少了占用空间,读取效率高
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
2月前
|
存储 分布式计算 测试技术
加速LakeHouse ACID Upsert的新写时复制方案
加速LakeHouse ACID Upsert的新写时复制方案
30 2
|
1月前
|
NoSQL Redis
Redis事务:保证数据操作的一致性和可靠性
Redis事务:保证数据操作的一致性和可靠性
|
3月前
|
缓存 NoSQL 关系型数据库
数据库缓存一致性学习笔记(一)
数据库缓存一致性学习笔记(一)
|
3月前
|
存储 关系型数据库 MySQL
InnoDB 引擎底层事务的原理
InnoDB 引擎底层事务的原理
|
4月前
|
存储 SQL 缓存
|
10月前
|
SQL NoSQL 关系型数据库
深度解析 MySQL 事务、隔离级别和 MVCC 机制:构建高效并发的数据交响乐(三)
深度解析 MySQL 事务、隔离级别和 MVCC 机制:构建高效并发的数据交响乐(三)
346 0
|
SQL 存储 缓存
【ACID底层实现原理、一致性非锁定读(MVCC的原理)、BufferPool缓存机制、重做日志刷盘策略、隔离级别】
【ACID底层实现原理、一致性非锁定读(MVCC的原理)、BufferPool缓存机制、重做日志刷盘策略、隔离级别】
51495 1
【ACID底层实现原理、一致性非锁定读(MVCC的原理)、BufferPool缓存机制、重做日志刷盘策略、隔离级别】
|
缓存 NoSQL 关系型数据库
高并发核心技术Redis系列(五)--------持久化和事务(下)
始终同步,每次Redis的写入都会立刻记入日志,性能较差但数据完整性比较好
126 0
高并发核心技术Redis系列(五)--------持久化和事务(下)
|
存储 NoSQL 算法
高并发核心技术Redis系列(五)--------持久化和事务(上)
由于Redis的数据都存放在内存中,如果没有配置持久化,Redis重启后数据就全丢失了,于是需要开启Redis的持久化功能,将数据保存到磁盘上,当Redis重启后,可以从磁盘中恢复数据。
114 0
高并发核心技术Redis系列(五)--------持久化和事务(上)
|
消息中间件 存储 缓存
怎么保证缓存和数据库一致性
怎么保证缓存和数据库一致性
428 0
怎么保证缓存和数据库一致性