Redis主從結構主節點執行寫入後wait命令對性能的影響

這裏的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可以加強(僅僅是加強,但不是保證)數據的安全性。
node

既然wait命令在當前鏈接以後會等待指定數量的從節點確認,其主節點的寫入效率必然會收到必定程度的影響,那麼這個影響有多大?
這裏作一個簡單的測試,環境2核4G的宿主機,docker下的集羣3主3從的Redis集羣,所以不用考慮網絡延遲,在執行寫入操做以後,使用兩個Case,對比使不使用wait命令等待傳送到salve的效率,
1,單線程循環寫入100000個key值
2,多線程併發,10個線程每一個線程寫入10000個key,一共寫入100000個keypython

Case1:單線程循環寫入100000個key值
結論:不使用wait命令,總體耗時33秒,集羣中單個節點的TPS爲1000左右;使用wait命令,總體耗時72秒,集羣中單個節點的TPS爲480左右,總體效率降低了50%多一點redis

單線程不使用WAITdocker

單線程使用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負載接近於瓶頸,所以不能再加更多的線程數,測試數據也僅供參考。app

 

總結:
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()


https://redis.io/commands/wait

相關文章
相關標籤/搜索