这里的 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()
来源: http://www.linuxidc.com/Linux/2020-01/161956.htm