1.CockroachDB: 弹性、地理分布式SQL 数据库
2.RocksDb 源码剖析 (1) | 如何混合 new 、mmap 设计高效内存分配器 arena ?
3.译:一文科普 RocksDB 工作原理
4.MQTT Broker 选型
5.FREE SOLO - 自己动手实现Raft - 15 - leveldb源码分析与调试-1
6.Sonic:用Rust编写的Elasticsearch的极简替代品
CockroachDB: 弹性、地理分布式SQL 数据库
现代 OLTP 负载正迅速地跨越地域分布,这使得跨国公司必须构建可扩展的应用系统并根据法律法规细粒度地控制数据存放位置。在这种背景下,wekan 源码编译安装CockroachDB(CRDB)应运而生,它是一个可扩展的 SQL 数据库管理系统,旨在支持全球性的 OLTP 负载的同时,保持高可用性和强一致性。 CRDB 从头构建,支持在普通商用硬件上实现跨地域的分布式事务,并且能够像蟑螂一样抵御灾难。其创新的事务模型、容错机制和高性能特性使其成为跨国公司理想的选择。此外,CRDB 还提供了 SQL 接口和自动根据数据库集群规模进行伸缩的能力,以满足数据存储和管理的需求。 为了满足跨国公司的需求,CRDB 重点关注以下几个特性:合规性、容错性和高性能。它具有前沿的查询优化器和分布式 SQL 执行引擎,支持在线模式更改、备份和恢复、快速导入、JSON 支持以及与外部分析系统的集成等功能。此外,CRDB 的源码已入驻 GitHub,且从 BSL 许可转为 Apache 开源 2.0 协议,用户无需依赖第三方 SQL 扩展专利或受制于云供应商宕机风险,避免了供应商锁定问题。 本文将详细介绍 CRDB 的各个组成部分,包括架构、复制和数据分布机制、事务模型、时间戳排序、SQL 数据模型、执行和模式变化、性能评估和案例学习、经验总结、相关著作以及结论与展望。接下来,我们将从系统架构角度深入剖析 CRDB 的设计与实现。系统架构概述
CRDB 使用无共享架构(share-nothing),所有的节点都同时提供存储和计算能力,集群可以包含任意数量的节点,这些节点可以在同一数据中心或分布于全球。客户端可以连接集群中的任何一个节点。 CRDB 的架构可以分为以下几层:SQL 层
最顶层是 SQL 层,它是连个锤子源码所有用户与数据库交互的接口。它包括解析器、优化器和 SQL 执行引擎,该引擎将高级 SQL 语句转换为底层 key-value (KV) 存储的低级读写请求。 通常,SQL 层并不了解数据是如何分区或分布的,因为下面的层抽象了一个单体的 KV 存储。然而,在第 5 节中,我们将详细介绍某些查询如何打破这种抽象,以实现更高效的分布式 SQL 计算。事务 KV 层
来自 SQL 层的请求被传递到事务 KV 层,该层确保跨越多个 KV 对的原子性更改。它在很大程度上对 CRDB 的隔离保障负有责任。这些原子性和隔离保证将在第 3 节和第 4 节中详细描述。数据分布层
这一层抽象了按 key 排序的单体逻辑键空间。在这个键空间中,所有数据都是可寻址的,无论是系统数据(用于内部数据结构和元数据)还是用户数据(SQL 表和索引)。 CRDB 对 key 进行范围分区,将数据分成连续有序的,大小约为 MB 的块,我们把这些块叫做“Ranges”。这些 Ranges 之间的排序由一个两层索引结构维护,保存在一系列系统级别 Rranges 里面,并被预缓存以支持快速的按 key 查询。本层负责确定查询的某个子集应该由哪个 Range 处理,并合理路由。 MB 的 Range 足够小,可以允许快速迁移,又足够大,足以保存一块连续的经常一起被访问的数据。Ranges 的初始状态为空,随着尺寸变化,经历分割、合并。Ranges 分割还可以根据负载进行,以减少 CPU 热点与资源不平衡。数据复制层
默认情况下,每个 Range 保存 3 个副本,每个副本存储在不同的节点上。在第 2.2 节中,我们将描述复制层如何使用基于共识的复制确保修改的持久性。存储层
这是最底层,代表一个本地磁盘支持的 KV 存储。它提供了高效的写和范围扫描,以支持高性能的 SQL 执行。在撰写本文时,我们依赖的是 RocksDB,它在其他地方有详细的atomic包源码记录,本论文中将其作为黑盒处理。容错和高可用性
使用RAFT复制
一个 Range 的所有副本组成一个 Raft group,其中一个副本是持久的 leader,协调所有发给这个 Raft group 的写操作,其他副本是 follower。复制的单元是命令,代表要存储层处理的一个编辑序列。Raft 在每个 Range 的所有副本范围内,维护一个一致的、排序的更新日志,每个副本各自按顺序在其本地存储引擎里应用那些已经声明被提交的日志。 CRDB 使用 Range 层面上的租约,其中一个副本(通常是 Raft group leader)承担 leaseholder 角色,因此是唯一允许提供权威最新读取或提交写请求给 Raft group leader 的副本。所有写操作都经过了 leaseholder,因此所有的读都可以在不牺牲一致性的情况下绕过 Raft 所需的网络往返成本。 用户级 Ranges 的租约和 leaseholder 所在节点的存活性绑定,存活性通知通过节点每 4.5 秒发送一个特殊心跳到系统级 Range 实现。系统级 Range 转而使用基于到期的租约,必须每 9 秒更新一次。如果某个节点探测到 leaseholder 不存活了,它就尝试自己获取租约。 为了确保每个时间点只有一个副本拥有租约,租约获取在现有的 Raft 框架内完成,提交一个特殊的获取租约日志记录。每个租约获取请求包含一个它在请求时认为合法的租约数据,两个副本的请求内的租约不重叠就可以达成这个保证。在第 4 节中,我们还会讨论租约不重叠是 CRDB 隔离机制的前提。成员变化与自动负载(再)平衡
集群运行中,节点可能加入或离开该集群,也可能暂时或永久失败。CRDB 使用相同的方法应对这些场景:在最新的存活节点中间重新分配负载。 节点短暂失败,而多数节点仍然可用的情况下,CRDB 可以持续运行。如果失败的是 Raft group 的 leader,Raft 保证新 leader 的选举。失败节点上先后可以重新加入原来的 group,同伴们帮它追赶错失的更新操作。方法包括:1)发送全量 Range 数据快照给它 2)发送错失的 Raft log 记录集合给它。具体选择根据该副本节点不可用期间错失的操作量作出。 节点长时间失败,CRDB 自动根据存活的副本为复制等级不够的 Ranges 创建出新的足够的副本。其存放位置由下一节描述选择。决策依赖的相关数据比如,存活节点信息、集群监测指标使用点对点的 Gossip 协议分发。副本存放
支持手动和自动选择。防好123源码 手动选择需要用户为每个节点配置属性,比如节点特性(特殊硬件、RAM、硬盘类型...)、节点位置(国家、地区、可用 zone...)。还可以在表模式里指定限制、偏好,比如指定 region 列,可以用来帮助分区,和把分区映射到特定地理区域。 自动选择根据用户制定的规则和不同的启发式算法自动跨失败域分布副本,容错不同程度的失败(硬盘级、机架级、数据中心级、区域级别)。数据存放策略
CRDB 的副本存放和 leaseholder 存放机制支持广泛的数据存放策略,用户可以借此做到数据合规,并在性能和容错间合理取舍。以下是一些多区域模式。 本文篇幅较长,将分为三篇发布。RocksDb 源码剖析 (1) | 如何混合 new 、mmap 设计高效内存分配器 arena ?
本文旨在深入剖析RocksDb源码,从内存分配器角度着手。RocksDb内包含MemoryAllocator和Allocator两大类内存分配器。MemoryAllocator作为基类,提供MemkindKmemAllocator和JemallocNodumpAllocator两个子类,分别集成memkind和jemalloc库的功能,实现内存分配与释放。
接着,重点解析Allocator类及其子类Arena的实现。基类Allocator提供两个关键接口:内存分配与对齐。Arena类采用block为单位进行内存分配,先分配一个block大小的内存,后续满足需求时,优先从block中划取,以减少内存浪费。一个block的大小由kBlockSize参数决定。分配策略中,Arena通过两个指针(aligned_alloc_ptr_和unaligned_alloc_ptr_)分别管理对齐与非对齐内存,提高内存利用效率。
分配内存时,Arena通过构造函数初始化成员变量,包括block大小、内存在栈上的分配与mmap机制的使用。构造函数内使用OptimizeBlockSize函数确保block大小合理,减少内存对齐浪费。华侨娱乐源码Arena中的内存管理逻辑清晰,尤其在分配新block时,仅使用new操作,无需额外内存对齐处理。
分配内存流程中,AllocateNewBlock函数直接调用new分配内存,而AllocateFromHugePage和AllocateFallback函数则涉及mmap机制的使用与内存分配策略的统一。这些函数共同构成了Arena内存管理的核心逻辑,实现了灵活高效地内存分配。
此外,Arena还提供AllocateAligned函数,针对特定对齐需求分配内存。这一函数在使用mmap分配内存时,允许用户自定义对齐大小,优化内存使用效率。在处理对齐逻辑时,Arena巧妙地利用位运算优化计算过程,提高了代码效率。
总结而言,RocksDb的内存管理机制通过Arena类实现了高效、灵活的内存分配与管理。通过深入解析其源码,可以深入了解内存对齐、内存分配与多线程安全性的实现细节,为开发者提供宝贵的内存管理实践指导。未来,将深入探讨多线程内存分配器的设计,敬请期待后续更新。
译:一文科普 RocksDB 工作原理
RocksDB 是一种可持久化的、内嵌型的键值存储(KV 存储)。它旨在存储大量 key 及其对应的 value,常被用于构建倒排索引、文档数据库、SQL 数据库、缓存系统和消息代理等复杂系统。RocksDB 在 年从 Google 的 LevelDB 分叉而来,针对 SSD 服务器进行了优化,并目前由 Meta 开发和维护。它以 C++ 编写,支持 C、C++ 及其他语言(如 Rust、Go、Java)的嵌入。如果你熟悉 SQLite,可以认为 RocksDB 是一种内嵌式数据库,需依赖应用层实现特定功能。
RocksDB 使用日志结构合并树(LSM-Tree)作为核心数据结构,这是一种基于多个有序层级的树形数据结构,可用于应对写密集型工作负载。LSM-Tree 的顶层是 MemTable,一个内存缓冲区,用于缓存最近的写入数据。较低层级的数据存储在磁盘上,以 L0 层为例,存储从内存移动到磁盘的数据,其他层级存储更旧的数据。当某一层级的数据量过大时,会通过合并操作转移到下一层。
为了保证数据持久化,RocksDB 将所有更新写入磁盘上的预写日志(WAL)。当应用重启时,可以通过回放 WAL 来恢复 MemTable 的原始状态。WAL 是一个只允许追加的文件,包含一组更改记录序列,每个记录包含键值对、操作类型和校验和。
当 MemTable 变满时,会触发刷盘(Flush)操作,将不可变的 MemTable 内容持久化到磁盘,并丢弃原始 MemTable,同时开始写入新的 WAL 和 MemTable。MemTable 默认基于跳表实现,以提高查询和插入效率。RocksDB 支持各种压缩算法,如 Zlib、BZ2、Snappy、LZ4 或 ZSTD,用于存储 SST 文件。
SST 文件是 MemTable 刷盘后生成的,包含了有序的键值对。每个 SST 文件由数据部分和索引块组成,数据部分包含一系列有序的键值对,而索引块存储了数据块中最后一个键的偏移量,便于快速定位键值对。RocksDB 还支持布隆过滤器,用于快速检测某个键是否存在于 SST 文件中。
当数据库大小增加时,空间放大(存储数据所用实际空间与逻辑大小的比值)和读放大(用户执行一次逻辑读操作所需实际 IO 次数)的问题变得明显。为了解决这些问题,RocksDB 实现了 Compaction 机制,通过合并 SST 文件来降低空间和读放大,同时增加写放大。Leveled Compaction 是默认策略,它会在不同层级之间进行选择性合并,以优化空间使用。
RocksDB 的读路径相对简单,主要涉及从 MemTable 开始,下探到 L0 层,然后继续向更低层级查找,直到找到目标键或检查完整个树。合并(merge)操作允许用户在内存中对键值进行聚合操作,适用于需要对已有值进行少量更新的场景。然而,这种操作增加了读时的复杂性,因为读操作需要在多次调用 merge 函数后才能得到最终结果。
使用 RocksDB 需要针对特定工作负载进行配置调优,因为它提供了许多可配置项,但理解其内部原理并调整这些配置通常需要深入研究源代码。RocksDB 是构建高性能数据库模块的优秀选择,能够帮助开发者专注于上层业务逻辑实现,而无需从零开始设计底层存储系统。
MQTT Broker 选型
MQTT Broker选型 在构建分布式系统时,选择合适的MQTT Broker至关重要,它负责接收发布者发布的消息并将其分发给不同的订阅者。市面上有许多MQTT Broker可供选择,以下是一些常用选项的对比与分析。Mosquitto
Mosquitto是由Eclipse出品的开源MQTT Broker,基于C/C++语言编写,当前版本为1.5.8。其特点包括支持MQTT 3.1/3.1.1协议,但性能上存在一些限制,如内存优化、多线程的锁机制等。它适合运行在低功耗设备上,如嵌入式传感器、手机和微处理器,但不适用于大规模云服务。官方文档显示其理论支持约万连接,实际使用中还需根据具体情况进行评估。EMQ (emqttd)
EMQ,一款国人开发的开源MQTT Broker,目前版本为2.0和3.0,2.0版本支持本地共享订阅,3.0版本新增集群共享订阅功能。EMQ具有完整QoS支持、单节点万连接能力、分布式集群支持、多种验证插件(如LDAP、MySQL、PostgreSQL等)以及API、Web监控界面等特性。官方宣称支持MQTT 3.1、3.1.1和5.0版本,并在性能上做了优化。然而,开源版本不支持服务器内部消息持久化,这是其一个显著限制。HiveMQ
HiveMQ是一款企业级MQTT Broker,使用Java编写,功能丰富,支持MQTT 3.1、3.1.1和5.0版本,完整QoS支持,分布式集群,持久化支持,流量控制,IPv6支持等。其唯一限制在于高昂的费用,没有公开源码供参考。集群基于Jgroups,数据同步通过自定义一致性哈希和VectorClock实现。多线程和并发控制使用Google的guava库,代码质量高。MqttWk
MqttWk是一个基于nutzboot、netty、redis和kafka实现的MQTT服务开源Broker,代码简洁易懂。它支持MQTT和Websocket连接方式,集群功能和消息分发重试,但存在一些限制,如消息队列非队列结构、消息分发重试机制较差、主题限制等。它是上生产的项目,经历过2万设备连接的考验。Jmqtt
Jmqtt是一个基于Java的开源MQTT Broker,对现有开源Broker进行了优化,特别是在CONNECT处理和Session过期管理方面。支持MQTT和Websocket连接方式,使用RocksDB进行本地存储,但不支持集群和SSL。Moquette
Moquette是一个功能齐全的Java编写的开源MQTT Broker,提供完整的QoS服务和认证方式,支持多种持久化存储。然而,0.版本中存在内存泄漏问题,官方修复后发布为irubant/moquette。其集群功能仅使用Hazelcast作为消息总线,不支持共享订阅。 综上所述,选择合适的MQTT Broker需考虑应用的具体需求,包括连接数量、协议版本、性能要求、集群支持、消息持久化、安全认证等因素。在选择时,应充分评估各Broker的特性和限制,以满足实际应用场景的需要。FREE SOLO - 自己动手实现Raft - - leveldb源码分析与调试-1
leveldb 是由 Google 基础架构工程师 Jeff Dean 所设计的,是一种高效、可靠的键值对存储系统。它基于LSM(Log-Structured Merge)存储引擎,代码简洁精炼,非常适合深入学习与理解。leveldb 不仅可以作为一个简单的键值对引擎使用,而且内部组件如LRU Cache也具有独立的实用性,还能在此基础上封装出其他操作接口,例如vraft中的raftlog和metadata等。
通过理解leveldb,能够对后续学习如rocksdb等更高级的数据库引擎提供坚实基础。本文旨在从状态机的角度解析leveldb,帮助读者深入理解其内部工作原理。
在leveldb中,关键状态包括但不限于内存、磁盘状态以及LRU Cache状态。内存数据与磁盘数据的交互是leveldb的核心,用户的键值对数据通过日志写入到memtable,然后通过immutable memtable最终到达磁盘上的sorted table文件,这些文件按照级别(level)从0到6逐级存储。通过在关键时刻添加ToJson函数,可以记录这些状态的变化,便于分析。
LRU Cache在leveldb中的实现同样值得深入研究。它作为一种缓存机制,有助于优化数据访问效率。通过在LRU Cache中添加ToJson函数并打印状态,可以直观地观察其内部结构和状态的动态变化。
为了更好地理解leveldb,本文将重点分析关键数据结构,并通过观察不同动作导致的状态变化,来深入探究leveldb的内部机制。在后续文章中,将详细展示leveldb内部状态的转换过程,以帮助读者掌握其核心工作原理。
Sonic:用Rust编写的Elasticsearch的极简替代品
Sonic 是一个开源搜索索引服务器,使用 Rust 编写,旨在提供简单、高性能且轻量级的解决方案。它通过接受用户查询并返回标识符(实际文档在关系数据库中的引用)来工作,这些标识符用于从另一个数据库(如 MongoDB、MySQL 等)中提取实际结果数据。Sonic 不存储文档本身,因此在存储方面非常简单有效。
创建 Sonic 的初衷是为了在不使用昂贵的开源搜索索引软件(如 Elasticsearch)的情况下,为 Crip 公司提供更经济的解决方案。作者 Valerian Saliou 在经营 Crip 时遇到了用户对消息搜索的需求,而传统的系统对免费增值商业模型来说成本过高。因此,他将 Sonic 打造成“可搜索的 Redis”,一种简单功能和简单网络协议的结合。
选择 Rust 作为 Sonic 的编写语言是基于其简单性和速度的优点。Rust 的语言约束,如借用检查器和无 NULL 值的事实,确保了在生产环境中运行项目时不会遇到某些类型的错误。此外,Sonic Channel 作为通过网络与 Sonic 通信的协议,使得数据能够高效地推送到索引或从索引中查询,而不采用基于 HTTP 的协议。
为了支持索引和自动完成,Sonic 使用了 LSM(Log-Structured Merge-tree)存储结构,底层使用了 RocksDB。FST(有限状态转换器)用于自动完成和拼写错误校正,其存储在磁盘上并进行内存映射,以确保快速访问。RocksDB 作为存储选择,因其在保持性能稳定的同时,通过压缩旧数据来最小化磁盘使用而受到青睐。
在构建 Sonic 时,选择使用jemalloc作为内存分配器,因为其专为现代 CPU 架构设计,尤其在管理多核架构上的内存方面表现出色。Sonic 的源码已经开源,允许开发者深入理解其运作方式。此外,Sonic 在实际应用中表现良好,索引速度迅速,用户满意度高,索引了大量对象,并在不同负载条件下展现出高效的内存使用和搜索延迟。
如果有人想要构建类似于 Sonic 的工具,建议先深入研究已有的实现和相关技术,以便了解如何优化设计和实现过程。选择合适的存储解决方案和优化内存管理是关键,同时确保代码的清晰性和可维护性,以支持长期的稳定运行。
2024-12-22 16:39
2024-12-22 16:11
2024-12-22 16:04
2024-12-22 15:47
2024-12-22 14:58
2024-12-22 14:46