EloqKV 的多线程性能以及跨分片事务测试
前言
Redis 是一款高性能的开源内存数据库,广泛用于缓存和简单的 KV 数据存储,但在事务支持方面有所局限。
Redis 事务
Redis 使用 *MULTI 和 EXEC* 实现事务,MULTI 开启事务,然后将事务中的命令放入一个队列中,EXEC 执行事务队列的命令,这些命令会按顺序执行,且具有以下特点:
1.原子性(部分):要么所有命令都执行,要么所有命令都不执行,且 Redis 在发生失败时不会回滚已成功执行的命令。
2.顺序性: 事务中的命令严格按提交顺序执行。
3.隔离性: 在事务执行期间,其他客户端的命令不会插入到当前事务的执行中
可以看出,与 MySQL 这种关系型数据库事务的不同之处,MySQL 的事务中只要一个命令出现异常,所有的命令都不会执行(回滚),而 Redis 无法做到这一点。
如图,我们在 Redis 中开启了一个事务,第二条 hset 命令和第一条 set 命令都操作了同一个 key,执行时显然是要报错的,我们看第二条和第三条命令是否可以正常执行。
如图,exec 执行事务队列中的命令,第二条命令报错,但是第一、三条命令都正常执行。所以,*Redis 的事务不具备原子性*。
除此之外,Redis 在事务原子性方面的不足,还表现在以下几个方面:
1.单线程模型
Redis 采用单线程处理请求,这虽然简化了设计,避免了锁的复杂性,但也限制了其垂直扩展能力。在多核现代服务器上,Redis 的性能提升有限,无法充分利用多线程处理能力,导致高并发时的性能瓶颈。
从 Redis 6 开始,虽然可以通过配置文件显式启用多线程功能,但多线程被用于处理网络请求的读写操作,比如接受客户端连接、读取请求数据、发送响应。Redis 的核心执行逻辑仍然保持单线程模型。
2.不支持跨分片通信
在 Redis Cluster 模式下,key 是通过哈希槽分配到不同的节点(分片)上的,因为 Redis 的多个分片实例之间无法相互通信,这意味着无法在多个分片之间实现一致的原子性事务。
Redis 中 WATCH 命令来监控一个或多个键的变化。在 WATCH 后,如果这些键在执行 MULTI 和 EXEC 之间发生了变化,Redis 会自动放弃执行事务。
假如我们在节点 A 上执行上述命令,key1 在节点 A、key2 在节点 B,当你执行 MULTI 和 EXEC 时,节点 A 将会尝试提交包含 SET key1 和 SET key2 的事务,但由于 key2 在节点 B 上,节点 A 无法知道 key2 是否已经被修改。
节点 A 不能跨节点监听到其他节点上键的变化,期间如果 key2 发生变化,所以就无法取消 key1 的操作,不能保证事务的原子性。
3.手动管理 Key 映射
为了解决上面跨分片的事务问题,Redis 给开发者提供了 *hashtag* 的方式,以确保这些 Key 映射到同一分片。hashtag 是一种通过在键名中使用特定的标记(例如 {})来限制哈希槽范围的方式。只有被括起来的部分会参与哈希计算,确保多个键在同一个分片上。
例如,key1 和 key2 默认情况下可能会分配到不同的分片,但如果你指定了 hashtag(如 {key1} 和 {key2}),Redis 会计算{key1} 和 {key2} 中的大括号部分来计算哈希槽,从而确保这两个键落在同一个分片上。
{user:1000}:name 和 {user:1000}:age 会被视为同一个“hashtag”部分,即 user:1000,这确保了这两个键会被映射到同一个分片
这种方式对业务代码的侵入性较强,增加业务代码的复杂性,并增加了管理复杂性。
EloqKV
EloqKV 是一款兼容 Redis 协议的分布式 Key-Value 数据库,旨在解决这些不足之处。作为高性能、弹性扩展、支持事务的分布式数据库,EloqKV 具有以下特点:
1.多线程架构
EloqKV 支持多线程,能够利用现代多核服务器资源,提升吞吐量并实现垂直扩展。
2.分片之间支持通信
EloqKV 的分布式架构支持不同分片之间的通信,使其能够在跨分片的事务中保持一致性。 EloqKV 可以根据业务增长动态扩展,满足大规模分布式场景需求,并为开发者提供一致的体验。
测试环境
本次测试在两台服务器上进行配置:
1.服务器一:32 核物理机,部署 EloqKV/Redis 实例。
2.服务器二:32 核物理机,部署测试服务器。
本实验主要是为了验证多 Key 访问的原子性能力,因此 Redis 没有开启 RDB、AOF,EloqKV 没有开启 enable_data_store 和 enable_wal。其他选项可以根据自己需要灵活配置。
场景模拟:社交软件中的点赞事务
在社交应用场景中,点赞操作通常包含两个步骤:更新点赞计数、将用户 ID 添加到点赞用户列表中。这两个步骤需要原子性,即它们要么全部成功,要么全部失败,以确保数据的一致性。
在 Redis 中,我们可以通过 MULTI/EXEC 命令来实现多步操作的事务,但这种事务仅在单分片内有效。在跨分片的场景中,Redis 要求手动调整 Key 的分片映射,使其都在同一实例上。
相比之下,EloqKV 能够在不同分片之间进行通信,直接支持多分片事务,这对于需要分布式事务的业务场景尤为重要。
实验 Benchmark
在本实验中,我们使用 Go 语言编写了一段模拟代码,以实现上述社交软件点赞场景的事务操作。代码会统计 QPS 和 Latency 等性能指标,以评估在高并发场景下两种数据库的性能差异。
Go 语言代码示例:
Python 语言代码示例:
测试结果
我们通过运行上面高并发调用模拟点赞操作的代码,并分别记录在 Redis 和 EloqKV 下的 QPS(每秒查询数)和平均延迟(Latency)指标。
Redis 测试结果:*10 万 QPS,延迟 0.01ms*
EloqKV 测试结果:*40 万 QPS,延迟 0.00ms*
从上面的测试结果我们可以得出结论:*Redis 在高并发环境下,由于单线程限制,Redis 的 QPS 受到瓶颈限制,平均延迟较高。而 EloqKV 的多线程架构能够支持更高的 QPS,延迟显著降低。
补充实验:跨分片事务
Redis 要求所有涉及事务性操作的键必须映射到同一个分片上,所以 Redis Cluster 默认不支持跨分片的事务操作。那么 EloqKV 是否支持跨分片的是事务场景。
这里我们同样使用 python 和 go 实现了跨分片事务的测试代码,在代码中只需要修改 IP 和端口,就能分别对 Redis Cluster 和 EloqKV 的分片事务测试。
Python 代码如下:
go 语言示例代码如下:
运行以上代码,因为在水平扩展场景下,Redis Cluster 默认不支持跨分片的事务操作,所以 Redis Cluster 会返回错误:*CROSSSLOT Keys in request don't hash to the same slot。*
如果开启了重定向,虽然不同的 key 会被重定向到对应的节点上,但是最后 exec 执行也是失败的。
如果没有开启重定向,key 无法重定向,在 exec 也会报错。
而 EloqKV cluster 可以正确处理跨分片事务请求,运行结果如图:
我们进入 EloqKV 的命令行,查看数据是否已经被放入到 EloqKV 中。
我们通过*cluster keyslot*进一步验证两条数据的 key 是否在不同的分片上。
如图,user:1 和 user:2 所在的 slot 在不同的分片上。
总结
通过本次测试,我们可以清晰地看到 EloqKV 在事务能力上相较 Redis 的显著优势:
1.跨分片事务支持
EloqKV 支持跨分片事务,实现了在水平扩展环境中的一致性事务处理,降低了对业务代码的侵入性。
2.多线程架构
EloqKV 的多线程架构在高并发场景下能够提供更高的 QPS 和更低的延迟,充分利用多核服务器资源,提升了整体性能。
3.一致的开发体验
EloqKV 提供了兼容 Redis 协议的接口,使开发者无需更改代码,即可在大规模分布式环境中获得一致的事务体验。
结语
EloqKV 不仅为开发者提供了与 Redis 兼容的接口,还通过其高效的事务处理能力为复杂的分布式应用提供了更强大的支持,满足了现代业务对事务性和扩展性的需求。
版权声明: 本文为 InfoQ 作者【晨章数据】的原创文章。
原文链接:【http://xie.infoq.cn/article/f94f06c926c18830729f77a60】。文章转载请联系作者。
评论