共计 3158 个字符,预计需要花费 8 分钟才能阅读完成。
这里的 Redis 主从结构可以是简单的主从,sentinel,redis cluster 中的主从等。
wait 命令的作用:
此命令将阻塞当前客户端,直到当前 Session 连接(主节点上)所有的写命令都被传送到指定数据量的 slave 节点。
如果到达超时 (以毫秒为单位),则即使尚未完全传送到达指定数量的 salve 节点,该命令也会返回(成功传送到的节点的个数)。
该命令将始终返回确认在 WAIT 命令之前发送的写命令的副本数量,无论是在达到指定数量的副本的情况下,还是在达到超时的情况下。
具体说就是:比如对于 1 主 2 从的结构,Wait 要求 3 秒钟之内传送到 2 个节点,但是达到超时时间 3 秒钟之后只成功传送到了 1 个 slave 节点上,此时 wait 也不会继续阻塞,而是返回成功传送的节点个数 (1)。
有点类似于 MySQL 的半同步复制,但是效果完全不能跟半同步相比,因为 Redis 本身没有回滚的功能,这里的 wait 命令发起之后,即便是超时时间之后没有送到任何一个 slave 节点,主节点也不会回滚。
wait 命令无法保证 Redis 主从之间的强一致,不过,在主从、sentinel 和 Redis 群集故障转移中,wait 能够增强(仅仅是增强,但不是保证)数据的安全性。
既然 wait 命令在当前连接之后会等待指定数量的从节点确认,其主节点的写入效率必然会收到一定程度的影响,那么这个影响有多大?
这里做一个简单的测试,环境 2 核 4G 的宿主机,docker 下的集群 3 主 3 从的 Redis 集群,因此不用考虑网络延迟,在执行写入操作之后,使用两个 Case,对比使不使用 wait 命令等待传送到 salve 的效率,
1,单线程循环写入 100000 个 key 值
2,多线程并发,10 个线程每个线程写入 10000 个 key,一共写入 100000 个 key
Case1: 单线程循环写入 100000 个 key 值
结论:不使用 wait 命令,整体耗时 33 秒,集群中单个节点的 TPS 为 1000 左右;使用 wait 命令,整体耗时 72 秒,集群中单个节点的 TPS 为 480 左右,整体效率下降了 50% 多一点
单线程不使用 WAIT
单线程使用 WAIT(redis_conn.execute_command(‘wait’, 1, 0))
Case2: 多线程循环写入 100000 个 key 值
结论:不使用 wait 命令,整体耗时 19 秒,集群中单个节点的 TPS 为 1700 左右;使用 wait 命令,整体耗时 36 秒,集群中单个节点的 TPS 为 900 左右,整体效率与单线程基本上一致,下降了 50% 多一点
多线程不使用 WAIT,单节点上 TPS 可达到 1700 左右
多线程使用 WAIT,单节点上 TPS 可达到 850 左右
鉴于在多线程模式下,CPU 负载接近于瓶颈,因此不能再加更多的线程数,测试数据也仅供参考。
总结:
wait 能够在主节点写入命令之后,通过阻塞的方式等待数据传送到从节点,wait 能够增强(但不保证)数据的安全性。
其代价或者说性能损耗也是不小的,通过以上测试可以看出,即便是不考虑网络传输延迟的情况下,其性能损耗也超出了 50%。
#!/usr/bin/env Python
# coding:utf-8
import sys
import time
import datetime
from rediscluster import StrictRedisCluster
import threading
from time import ctime,sleep
def redis_cluster_write():
redis_nodes = [{‘host’:’172.18.0.11′,’port’:8888},
{‘host’:’172.18.0.12′,’port’:8888},
{‘host’:’172.18.0.13′,’port’:8888},
{‘host’:’172.18.0.14′,’port’:8888},
{‘host’:’172.18.0.15′,’port’:8888},
{‘host’:’172.18.0.16′,’port’:8888}]
try:
redis_conn = StrictRedisCluster(startup_nodes=redis_nodes,password=’******’)
except Exception:
raise Exception
redis_conn.config_set(‘cluster-require-full-coverage’, ‘yes’)
counter = 0
for i in range(0,100000):
counter = counter+1
redis_conn.set(‘key_’+str(i),’value_’+str(i))
#redis_conn.execute_command(‘wait’, 1, 0)
if counter == 1000:
print(‘insert 1000 keys ‘+str(str(datetime.datetime.now())))
counter = 0
def redis_concurrence_test(thread_id):
redis_nodes = [{‘host’:’172.18.0.11′,’port’:8888},
{‘host’:’172.18.0.12′,’port’:8888},
{‘host’:’172.18.0.13′,’port’:8888},
{‘host’:’172.18.0.14′,’port’:8888},
{‘host’:’172.18.0.15′,’port’:8888},
{‘host’:’172.18.0.16′,’port’:8888}]
try:
redis_conn = StrictRedisCluster(startup_nodes=redis_nodes, password=’******’)
except Exception:
raise Exception
redis_conn.config_set(‘cluster-require-full-coverage’, ‘yes’)
counter = 0
for i in range(0, 10000):
counter = counter + 1
redis_conn.set(‘key_’ + str(thread_id)+’_’+str(counter), ‘value_’ + str(i))
#redis_conn.execute_command(‘wait’, 1, 0)
if counter == 1000:
print(str(thread_id)+’:insert 1000 keys ‘ + str(str(datetime.datetime.now())))
counter = 0
if __name__ == ‘__main__’:
#redis_cluster_write()
threads = []
for i in range(10):
t = threading.Thread(target=redis_concurrence_test, args=(i,))
threads.append(t)
begin_time = ctime()
for t in threads:
t.setDaemon(True)
t.start()
for t in threads:
t.join()
: