所有现代消费者应用程序都必须易于扩展且成本效益高,为此,线性数据库性能至关重要。凭借我们的无共享架构,我们已经反复证明,单个 Redis 企业集群可以通过简单地添加分片和节点来以线性方式无限扩展。但这并不意味着我们已经取得了成果。在 RedisConf18 上,我们展示了一个单个 Redis 企业集群可以实现 每秒 5000 万次操作 (ops/sec),仅使用 26 个 AWS 实例,同时将延迟保持在 1 毫秒以下,今天我们很高兴地宣布 Redis 企业已创造了新的行业性能记录。
在我们最新的基准测试中,Redis 企业在仅 40 个 AWS 实例上实现了超过 2 亿 ops/sec 的性能,延迟低于 1 毫秒。这代表着在不到 15 个月的时间里性能提高了 2.6 倍。在深入了解最新基准测试的配置、工具、工作负载和结果之前,让我们快速回顾一下 Redis 企业集群。
在 Redis 企业术语中,集群是由云实例、虚拟机或容器/Kubernetes POD 或裸机服务器组成的节点集。集群允许您在这些节点之间共享的内存池中创建 Redis 数据库,同时保持数据路径与控制和管理路径之间的完全分离。每个集群都包含 Redis 分片、零延迟代理和集群管理器。由于开源集群 API 允许 Redis 客户端直接访问包含键值对象的碎片,无需额外的网络跳转,因此 Redis 企业集群可以非常有效地扩展。控制路径(处理数据库配置/取消配置、自动故障转移、碎片迁移、集群平衡等等)产生的开销非常小,不会影响这种线性可扩展性。最后但并非最不重要的是,Redis 企业在为扩展目的向集群添加更多节点之前,会有效地利用每个集群节点上的所有内核。
以下是 Redis 企业无共享集群架构的直观描述,该架构配置了开源集群 API 并利用了给定集群节点中的所有内核
作为基准,我们想测试在单个 AWS 实例上运行时可以实现多少 ops/sec,并牢记以下要求
为了实现这一点,我们使用了以下系统设置:
1) 我们安装了 Redis 企业。
https://docs.redis.com/latest/rs/installing-upgrading/downloading-installing/
2) 我们设置了 Route53。
https://docs.redis.com/latest/rs/installing-upgrading/configuring/cluster-name-dns-connection-management/configuring-aws-route53-dns-redis-enterprise/
3) 我们创建了数据库。
指定 cluster_api_user、name、shards_count 和 port
如下所示,API 将创建具有 name:<name > 的数据库,具有 shards:<shards_count> ,侦听 port:<port>。 虽然我们在此基准测试中使用了单节点集群,但我们还设置了 proxy_policy、shards_placement 和 oss_cluster 参数。这些用于将碎片均匀放置并在所有节点上创建端点,以与多节点集群配置保持一致。
curl -v -k -u <cluster_api_user> https://localhost:9443/v1/bdbs -H "Content-type: application/json" -d
'{
"name": <name>,
"memory_size": 10000000000,
"type" : "redis",
"sharding": true ,
"shards_count": <shards_count>,
"shard_key_regex": [{"regex": ".*{(?<tag>.*)}.*"},{"regex": "(?<tag>.*)" }],
"proxy_policy": "all-master-shards",
"shards_placement": "sparse",
"oss_cluster":true,
"port":<port>
}'
为了确保测试公平,我们首先填充了数据库,以避免对不存在的键执行 GET 操作(指定 NUM_OF_KEYS):
memtier_benchmark -s <EndPoint> -p <Port> -t 48 --ratio=1:0 --pipeline=9 -c 1 -d 100 --key-minimum=1 --key-maximum=<NUM_OF_KEYS> -n allkeys --key-pattern=S:S
4) 我们为节点选择了 AWS EC2 上的 c5.18xlarge 实例类型,它具有 72 个 vCPU 144GB 和 3.0 GHz(使用英特尔 Turbo Boost 技术最高可达 3.5 GHz)英特尔至强 Skylake-SP 处理器。
5) 我们选择了 c5.9xlarge 实例类型来运行负载生成工具,作为我们的客户端计算机。
6) 最后,对于我们的负载生成工具,我们使用了 memtier_benchmark,参数如下:
memtier_benchmark -s <redis.endpoint.address> -p <port> -t $Threads —ratio=1:1 --pipeline=9 -c $Clients -d 100 --key-minimum=1 --key-maximum=<NUMBER_OF_KEYS> -n 1000000000 --cluster-mode
然后,我们尝试找到最佳的分片和代理线程平衡,并观察到最佳结果是在每个节点上有 8 个分片,这使我们获得了 420 万 ops/sec。由于代理线程上下文切换和 Redis 进程在不同 NUMA 节点的 CPU 之间切换,导致吞吐量不稳定。这种跨内存访问增加了处理延迟,即使系统仅加载了 50%。
如下图所示,我们能够在 c5.18xlarge 实例上使用 8 分片和 10 分片配置获得更好的结果,方法是使用 NUMA 绑定进行 Redis 分片和代理线程亲和性
注意:以下脚本仅在双插槽系统上有效。
NUMA_CNT=$(numactl --hardware | grep '^available' | awk '{print $2}')
if [ $NUMA_CNT -eq 2 ]; then
NODE_ID=$(cat /etc/opt/redis/node.id)
DMC_HALF_COUNT=$(expr $(/opt/redis/bin/ccs-cli hget dmc:$NODE_ID threads) / 2)
NUMA0_CPUS=$(numactl --hardware | grep 'node 0 cpus' | awk -F ': ' '{print $2}' | sed 's/ /,/g')
NUMA1_CPUS=$(numactl --hardware | grep 'node 1 cpus' | awk -F ': ' '{print $2}' | sed 's/ /,/g')
DMC_PID=$(sudo /opt/redis/bin/dmc-cli -ts root list | grep listener | awk '{printf "%in",$3}')
sudo taskset -apc $NUMA0_CPUS $DMC_PID
sudo /opt/redis/bin/dmc-cli -ts root list | grep worker | tail -$DMC_HALF_COUNT |
awk '{printf "%in",$3}' |
xargs -i sudo taskset -pc $NUMA1_CPUS {}
fi
NUMA_CNT=$(numactl --hardware | grep '^available' | awk '{print $2}')
REDIS_HALF_CNT=$(expr $(pgrep redis-server-5 | wc -l) / 2)
NUMA0_CPUS=$(numactl --hardware | grep 'node 0 cpus' | awk -F ': ' '{print $2}' | sed 's/ /,/g')
NUMA1_CPUS=$(numactl --hardware | grep 'node 1 cpus' | awk -F ': ' '{print $2}' | sed 's/ /,/g')
pgrep redis-server-5 | sort | head -$REDIS_HALF_CNT | xargs -i sudo taskset -apc $NUMA0_CPUS {} &&
pgrep redis-server-5 | sort | head -$REDIS_HALF_CNT | xargs -i sudo migratepages {} 1 0
pgrep redis-server-5 | sort | tail -$REDIS_HALF_CNT | xargs -i sudo taskset -apc $NUMA1_CPUS {} &&
pgrep redis-server-5 | sort | tail -$REDIS_HALF_CNT | xargs -i sudo migratepages {} 0 1
下表显示了我们如何在 AWS c5.18xlarge 实例上确定最佳的 Redis 分片和代理线程配置,以在将延迟保持在毫秒以下的同时实现最佳吞吐量(以 ops/sec 为单位)
分片 | 4 | 6 | 8 | 8 | 10 |
代理线程 | 16 | 24 | 28 | 28 | 32 |
评论 | 默认 | 默认 | 默认 | NUMA 调整 | NUMA 调整 |
延迟 (毫秒) | 0.92 | 0.91 | 0.98 | 0.81 | 0.92 |
吞吐量 (M ops/sec) |
2.9 | 3.89 | 4.2 | 4.8 | 5.3 |
每个分片的吞吐量 (K ops/sec) | 725 | 648 | 525 | 600 | 530 |
我们首先想匹配我们在 RedisConf18 上使用 26 个 m4.16xlarge 实例展示的 5000 万 ops/sec 基准测试。这次,我们能够使用仅 10 个 AWS 5.18xlarge 实例达到 5172 万 ops/sec
节点 (C5.18xlarge) | 10 |
客户端 (C5.9xlarge) | 10 |
每个节点的分片数 | 10 |
代理线程 | 32 |
预填充键 | 10M |
连接 | 14,800 |
平均 ops/sec | 51.72M |
平均延迟 | 0.92 |
每个节点的平均每秒操作数 | 5.17M |
节点 (C5.18xlarge) | 20 |
客户端 (C5.9xlarge) | 20 |
每个节点的分片数 | 10 |
代理线程 | 32 |
预填充键 | 10M |
连接 | 30,400 |
平均 ops/sec | 102.37M |
平均延迟 | 0.94 |
每个节点的平均每秒操作数 | 5.11M |
节点 (C5.18xlarge) | 40 |
客户端 (C5.9xlarge) | 30 |
每个节点的分片数 | 10 |
代理线程 | 32 |
预填充键 | 10M |
连接 | 72,000 |
平均 ops/sec | 201.17M |
平均延迟 | 0.99 |
每个节点的平均每秒操作数 | 5.02M |
我们在 5.0 版本中对 Redis 核心进行了多项改进(特别是在大型管道性能方面),再加上对 Redis Enterprise 代理以及它与 Redis 分片通信方式的诸多增强,再加上新的 AWS C5 实例系列和适当的 NUMA 配置,这一切都帮助我们实现了超过每秒 200M 次操作,而只使用了一个40 节点集群,同时保持延迟低于1 毫秒。
我们的无共享架构为每个 Redis 分片额外提供了每秒 500K+ 次操作,并且为添加到集群中的每个节点提供了每秒 5M+ 次操作,接近最佳的 94% 线性扩展。换句话说,从单个节点到 40 节点集群,每个节点的吞吐量仅下降了 6%。
在这些测试中,我们甚至设法实现了每个 Redis 分片每秒 800K 次操作(并保持一致的亚毫秒级延迟),但由于每秒网络数据包的限制,我们无法扩展节点吞吐量以实现更高且稳定的性能。
我们对打破我们 15 个月前的数据库性能记录所看到的显著改进感到惊讶:
2018 年 3 月 | 2019 年 6 月 | 改进 | |
集群吞吐量 | 每秒 50M 次操作 | 每秒 200M 次操作 | x4 |
节点数 | 26 | 40 | x2.6 |
分片数 | 512 | 400 | x5 |
此实验表明,通过 Redis Enterprise 所实施的适当架构,Redis 可以打破数据库领域内的任何性能记录,同时使用比其他数据库少得多的硬件资源。