REDIS 如何利用python 操作redis 集群 (投稿文章)

2021-12-28 AustinDatabases

註明: 此篇文章為投稿文字, 投稿人 閆樹爽, (資深程式設計師, 目前從事REDIS ,MONGODB ,以及資料庫運維自動化代碼工作)

在NOSQL 資料庫中的操作,與關係型資料庫不同的是,會一門程序對於NOSQL的資料庫操控是十分有利的,下面的內容是關於如何利用python程序來操作redis 集群的說明.

## 利用python操作redis集群

redis的cluster模式為大型應用中常用的方式,今天學習如何使用redis-py-cluster來操作redis集群

首先安裝redis-py-cluster

```

pip install redis-py-cluster

```

基本用法

```python

from rediscluster import RedisCluster

from string import ascii_letters

import random

conn = RedisCluster(host="127.0.0.1", port=6379, password='password')#創建連接

for i in range(10000):

    key = ''.join(random.sample(ascii_letters, k=7))#創建一個隨機字符串作為key

    # ex = random.randint(1, 60 * 60 * 24)

    conn.set(key, i, ex=500)

    print(i)

```

此時可以發現reidis中已經有了我們的數據

然而在現實使用中,redis的使用往往涉及高並發,每次都重新創建連接不是我們所建議的方式,我們可以使用連接池來創建連接,並通過多線程來進行訪問。

redis-py-cluster的官方文檔寫的比較簡單並沒有給出詳細的連接池使用方式,但是好在python能夠查看源碼,我們可以看到其中有一個ClusterConnectionPool類,這個從命名來看應該是連接池的。進源碼去看這個類是繼承自redis庫的ConnectionPool。

直接連接嘗試

```python

from rediscluster import ClusterBlockingConnectionPool,RedisCluster,ClusterConnectionPool

startup_nodes = [

    {'host': '10.50.132.92', 'port': 6379},

    {'host': '10.50.132.93', 'port': 6379},

    {'host': '10.50.132.94', 'port': 6379},

]

pool = ClusterConnectionPool(startup_nodes=startup_nodes,password="xxxx")

```

## 處理報錯

會發現報錯(個別現象,原因下面說),如果未遇到錯誤可以跳過這小節

```

redis.exceptions.ResponseError: unknown command `CONFIG`, with args beginning with: `GET`, `cluster-require-full-coverage`, 

```

原因是因為我將redis中的config命令rename掉了,但是在默認情況下,會檢查槽的完整性,所以會使用到config命令,但是config命令已經被我rename掉了啊,所以會報這個錯誤。解決方法有兩種:

1、修改自己的代碼,這個參數就是字面意思,跳過完整性檢查,這樣也就不會調用config命令了。

```python

pool = ClusterBlockingConnectionPool(startup_nodes=startup_nodes,password="xxxx",skip_full_coverage_check=True)

```

2、修改庫的代碼,這樣的好處是還是能夠檢查完整行,但是缺點是在進行項目遷移的時候還要再改一遍源碼,修改方法如下:

首先找到redis包中的client.py,一般在site-packages/redis/client.py

如果找不到可以從報錯的地方快速找到redis包的安裝路徑

![](F:\文檔\redis\python連接redis集群\1.PNG)

找到這個1239行

```python

    def config_get(self, pattern="*"):

        "Return a dictionary of configuration based on the ``pattern``"

        return self.execute_command('CONFIG GET', pattern)

```

修改這個CONFIG GET為rename後的命令就可以了

通過以上兩種方法,可以正常使用連接池了

## 連接池的使用

這個連接池的使用方法如下

```python

from rediscluster import ClusterBlockingConnectionPool,RedisCluster,ClusterConnectionPool

startup_nodes = [

    {'host': '10.50.132.92', 'port': 6379},

    {'host': '10.50.132.93', 'port': 6379},

    {'host': '10.50.132.94', 'port': 6379},

]

pool = ClusterConnectionPool(startup_nodes=startup_nodes,password="xxxx")

client = RedisCluster(connection_pool=pool)

```

這裡的pool使用上面的方式就可以得到了一個client

## 實際使用

我們可以用多線程為例子,使用連接池來操作redis

```python

from rediscluster import ClusterBlockingConnectionPool,RedisCluster,ClusterConnectionPool

import threading

from string import ascii_letters

import random

startup_nodes = [

    {'host': '10.50.132.92', 'port': 6379},

    {'host': '10.50.132.93', 'port': 6379},

    {'host': '10.50.132.94', 'port': 6379},

]

pool = ClusterConnectionPool(startup_nodes=startup_nodes,password="xxxx",max_connections=10)

threads=[]

lock = threading.Lock()

def test_thread(thread_id):

    client = RedisCluster(connection_pool=pool)

    for i in range(100):

        key = ''.join(random.sample(ascii_letters, k=7))

        client.set(key,random.randint(1,100),ex=100)

        lock.acquire()

        print(f"Thread-{thread_id}:processed {i+1} times")

        lock.release()

#創建線程

for i in range(10):

    threads.append(threading.Thread(target=test_thread,args=(i,)))

#開啟線程

for th in threads:

    th.start() 

```

此處我們的線程池裡面放10個連接,然後開啟10個線程,為了顯示的不太難看,我們在print的地方加了個鎖,我們期待這個樣能夠正常的10個線程同時向redis裡面寫數據,但是實際卻發現

![](F:\文檔\redis\python連接redis集群\2.PNG)

會顯示有太多的連接,猜測應該是連接池中的連接不夠,所以我們調大max_connections參數為50,發現可以正常使用了,但是為什麼呢?

熟悉redis的同學可能一下就猜測到了原因。因為我們的集群模式,key鍵是要根據hash值來分配的,具體連接到那個我們插入之間是不知道的,所以在連接創建之前,客戶端也是不知道的,所以顯示出這個。在初始化函數中果然找到了相應的參數max_connections_per_node。我們將它設置為True。

這樣測試,我們將max_connections設置為10,同時將max_connections_per_node設置為True

這樣一來每個節點都有10個連接,我們可以開啟30個線程,如下

```python

from rediscluster import ClusterBlockingConnectionPool,RedisCluster,ClusterConnectionPool

import threading

from string import ascii_letters

import random

startup_nodes = [

    {'host': '10.50.132.92', 'port': 6379},

    {'host': '10.50.132.93', 'port': 6379},

    {'host': '10.50.132.94', 'port': 6379},

]

pool = ClusterConnectionPool(startup_nodes=startup_nodes,password="xxx",max_connections=10,skip_full_coverage_check=True,max_connections_per_node=True)

threads=[]

lock = threading.Lock()

def test_thread(thread_id):

    client = RedisCluster(connection_pool=pool)

    for i in range(100):

        key = ''.join(random.sample(ascii_letters, k=7))

        client.set(key,random.randint(1,100),ex=100)

        lock.acquire()

        print(f"Thread-{thread_id}:processed {i+1} times")

        lock.release()

#創建線程

for i in range(30):

    threads.append(threading.Thread(target=test_thread,args=(i,)))

#開啟線程

for th in threads:

    th.start()

```

但是~~~,還是不行,跑了一段時間以後有的線程會報錯

![](F:\文檔\redis\python連接redis集群\3.PNG)

簡單思考下,因為這樣雖然開啟了這麼多的pool,但是對於30個線程來說,仍然有可能引發衝突,因為我們設置的是每個節點10個連接

但是30個線程在同一時間可能有11個訪問到了同一個節點,所以會產生這個錯誤。所以這裡應該改成max_connections=30

但是如果我們連接池只想創建20個呢?我們需要自己加鎖判斷嗎?這樣是否太麻煩了?

of course not~

在上面大家應該注意到,我在引入的時候還另外引入了一個ClusterBlockingConnectionPool,這個注釋中寫的是

```

Thread-safe blocking connection pool for Redis Cluster::

```

這是一個線程安全的連接池,繼承自ClusterConnectionPool,

使用我們僅需要將ClusterConnectionPool替換為ClusterBlockingConnectionPool即可。

至此已經可以正常使用redis連接池。

## 時間對比:

多線程連接池方式30個線程每個線程插入100個共3000次數據耗時5.1秒

單線程訪問插入1000個數據用時49秒,如果3000個應該在150秒左右,大概差了不到30倍,符合我們開的30個線程

## 空閒超時

在沒有經過測試之前,我有一個擔憂就是服務端設置了空閒超時,我們這個python客戶端會被服務端斷開,那麼單機還好,因為每次是單獨的創建連接,那麼連接池呢?

我們可以在test_thread中加入一個延時,然後過一段時間讓連接空閒並超時以後再訪問。經過測試,還是能連接的。

相關焦點

  • Python操作Redis
    Part1前言前面我們都是使用 Redis 客戶端對 Redis 進行使用的,但是實際工作中,我們大多數情況下都是通過代碼來使用 Redis 的,由於小編對 Python 比較熟悉,所以我們今天就一起來學習下如何使用 Python 來操作 Redis。Part2環境準備Python 安裝好(建議使用 Python3)。
  • 在Kubernetes上部署一套 Redis 集群
    $(headless service name)有了這個映射,就可以在配置集群時使用域名替代IP,實現有狀態應用集群的管理三、方案藉助StatefulSet和Headless Service,集群的部署方案設計如下(圖片來自參考文章):
  • 走進 Redis:Redis 的安裝、使用以及集群的搭建
    二、Redis的啟動與基本操作1.運行redis在redis的安裝目錄下直接運行 ./redis-server就可以啟動redis,但這是前端啟動。如果我們想後臺啟動就需要:1、進入redis-3.0.0.tar.gz解壓出來的文件夾,複製裡面的redis.conf文件到安裝目錄下。
  • Python操作Redis大全
    Redis的redis模塊對字符串(string)的主要操作函數包括:SET、GET、GETSET、SETEX、SETNX、MSET、MSETNX、INCR(INCRBY,DECR,DECRBY在python中庸同一個函數incr實現)、APPEND、SETRANGE、STRLEN。
  • acl 框架中的 Redis 庫已經支持集群版 Redis 3.0
    redis 進行分庫了,之前人們為了使單機版的 redis 能支持集群方式,往往是在客戶端或通過加一個中間的代理層(比如使用 tweaproxy)做很多工作,現在有了集群版的 redis3.0 ,這些額外的操作都不再需要。
  • Golang中間件——go-redis操作Redis
    Feature-rich:We support pipelines, transactions, publish/subscribe, Lua scripts, mocks, distributed locks, and more.一句話理解:它是開箱即用,它是一款支持redis伺服器、redis集群、redis Sentinel,redis伺服器環的客戶端,它具備類型安全
  • Python | Python學習之Redis交互詳解
    前言最近在學習scrapy redis,順便複習了redis。本篇為redis篇,包含實例演示,主從服務配置,python交互等內容。配置啟動:sudo redis-server /etc/redis/redis.conf查看redis進程:ps-ef|grep redis殺死進程:sudo kill -9 pidredis數據結構與操作redis數據結構redis是key-value的數據結構,每條數據都是一個鍵值對鍵的類型是字符串,且鍵不能重複值的類型分為五種:字符串strin
  • Docker 搭建 Redis Cluster 集群環境
    使用 Docker 搭建 Redis Cluster,最重要的環節就是容器通信的問題,這一塊我們在之前的文章中已經給大家解決了《Docker 網絡模式詳解及容器間網絡通信》,本篇文章主要練習使用多個容器完成 Redis Cluster 集群環境的搭建,順便為學習 Docker Compose 鋪鋪路。
  • Redis的集群搭建,原來這麼簡單
    回歸正題,上篇主要說了下Redis的主從複製是如何做的,高可用集群有幾種方式部署(傳送門《原來你是這樣的高可用呀》)。其中針對Redis高可用cluster集群說明時,有一些遺漏的東西,會在本篇前半部分進行說明,後面的內容主要是說怎麼來搭建cluster集群。
  • 用 Python 操作 Redis,看這一篇就夠了
    前言前面兩篇文章聊到了 Python 處理 Mysql、Sqlite 資料庫常用方式,本篇文章繼續說另外一種比較常用的數據存儲方式:RedisRedis:Remote Dictionary Server,即:遠程字典服務,Redis 底層使用 C 語言編寫,是一款開源的、基於內存的 NoSql 資料庫由於 Redis 性能遠超其他資料庫,並且支持集群、分布式及主從同步等優勢
  • 認識Redis集群——Redis Cluster
    之前沒有好好的全面理解Redis集群,特別是Redis Cluster,以為這就是redis集群的英文表達啊,故寫本篇博文來儘可能全面加深理解Redis Cluster。主要參考資料《Redis設計與實現》,主要是PDF電子版,有需要的朋友評論或者私聊! 一、Redis Cluster簡單概述1.
  • Redis服務​之Redis Cluster
    ;但對於redis的單機寫入問題還是一直存在;在sentinel+主從同步架構中,程序寫數據,始終是把讀寫請求發送給master(當然,如果有語句路由器讀寫請求是可以分開的);這樣一來對於master來講它就承擔了所有的寫操作,很顯然這種在寫操作非常的頻繁的場景,單臺master肯定無法承受這麼大的壓力;為了解決單機master寫入數據的瓶頸問題, redis 官方在 redis 3.0 版本之後推出了無中心架構的
  • Redis面試總結
    Redis是單進程單線程的redis利用隊列技術將並發訪問變為串行訪問,消除了傳統資料庫串行控制的開銷我們可以利用master來插入數據,slave提供檢索服務。這樣可以有效減少單個機器的並發訪問數量。
  • 實戰:Redis集群環境下的-RedLock(真分布式鎖)
    每天為您推送優質技術文章在不同進程需要互斥地訪問共享資源時,分布式鎖是一種非常有用的技術手段。 有很多三方庫和文章描述如何用Redis實現一個分布式鎖管理器,但是這些庫實現的方式差別很大,而且很多簡單的實現其實只需採用稍微增加一點複雜的設計就可以獲得更好的可靠性。
  • Python 實現 Redis ORM
    這篇文章的靈感來自於 Django ORM。這篇文章假定你對 Redis 以及 Python 中的 redis 庫 redis-py 有了基本的了解。實體假設我們正在開發一個輪詢應用程式,這個應用包括 Question 和 Choice。每一個問題都有多個選項。
  • Redis面試突擊專用
    單線程的redis為什麼這麼快 redis的數據類型,以及每種數據類型的使用場景,Redis 內部結構 redis的過期策略以及內存淘汰機制【~】 Redis 為什麼是單線程的,優點 如何解決redis的並發競爭key問題 Redis 集群方案應該怎麼做?都有哪些方案?有沒有嘗試進行多機redis 的部署?如何保證數據一致的?對於大量的請求怎麼樣處理 Redis 常見性能問題和解決方案?
  • Redis如何實現分布式鎖?
    文章已收錄Github精選,歡迎Star:https://github.com/yehongzhi前言如果在一個分布式系統中,我們從資料庫中讀取一個數據,然後修改保存,這種情況很容易遇到並發問題。因為讀取和更新保存不是一個原子操作,在並發時就會導致數據的不正確。
  • 經典面試題:Redis的熱key問題如何發現和解決?
    方法二:在客戶端進行收集這個方式就是在操作redis之前,加入一行代碼進行數據統計。那麼這個數據統計的方式有很多種,也可以是給外部的通訊系統發送一個通知信息。缺點就是對客戶端代碼造成入侵。方法三:在Proxy層做收集有些集群架構是下面這樣的,Proxy可以是Twemproxy,是統一的入口。
  • Redis精進:List的使用和應用場景
    點擊上方「Java知音」,選擇「置頂公眾號」技術文章第一時間送達!
  • 面試官:Redis有哪幾種集群方案?原理和優缺點是什麼?
    所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位協議優化傳輸速度和帶寬節點的fail是通過集群中超過半數的節點檢測失效時才生效客戶端與redis節點直連,不需要中間代理層.客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可在