redis哨兵 ,redis叢集 快取 以及某些問題: 最左字首原則,,celery架構

拆尼斯、帕丁顿發表於2024-03-25

Redis哨兵

# 主從複製存在的問題:
#1 主從複製,主節點發生故障,需要做故障轉移,可以手動轉移:讓其中一個slave變成master
    -哨兵解決
#2 主從複製,只能主寫資料,所以寫能力和儲存能力有限
    -叢集來解決

# 搭建哨兵的目的
    一旦一主多從的架構,主庫發生故障,能夠自動轉移
    一主多從架構的:高可用
        -redis服務對外高度可用
        -django服務專案是否是高可用的?
            -nginx的轉發(負載解決)
        
        
        
# 哨兵:sentinel
    - 1 監控 主從
    - 2 故障轉移
    - 3 通知
    
# 架構說明
可以做故障判斷,故障轉移,通知客戶端(sentinal其實是一個程序),客戶端直接連線sentinel的地址

1 多個sentinel發現並確認master有問題
2 選舉觸一個sentinel作為領導(選舉演算法Raft演算法(共識演算法))
3 選取一個slave作為新的master
4 通知其餘slave成為新的master的slave
5 通知客戶端主從變化
6 等待老的master復活成為新master的slave


# 只需要配置檔案配置好,上面的操作,可以自動來做



# 哨兵配置步驟:(啟動三個哨兵---》三個程序)
    -一臺機器:一主連從
    -一臺機器:啟動三個sentinel


#### 1 先搭建一主兩從配置檔案####
#第一個是主配置檔案
daemonize yes
pidfile /var/run/redis.pid
port 6379
dir "/root/redis/data"
logfile “6379.log”

#第二個是從配置檔案
daemonize yes
pidfile /var/run/redis2.pid
port 6381
dir "/root/redis/data2"
logfile “6378.log”
slaveof 127.0.0.1 6379
slave-read-only yes
#第三個是從配置檔案
daemonize yes
pidfile /var/run/redis3.pid
port 6381
dir "/root/redis/data3"
logfile “6377.log”
slaveof 127.0.0.1 6379
slave-read-only yes

#### 2 啟動三個redis服務####
redis-server ./redis_6379.conf
redis-server ./redis_6380.conf
redis-server ./redis_6381.conf


#### 3     三個sentinel 配置檔案#####
# sentinel_26379.conf  sentinel_26380.conf  sentinel_26381.conf
# 三個配置檔案,改一下埠和dir即可
port 26379
daemonize yes
dir ./data
protected-mode no
bind 0.0.0.0
logfile "redis_sentinel3.log"
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000



### 4 啟動哨兵
./src/redis-sentinel ./sentinel_26379.conf 
./src/redis-sentinel ./sentinel_26380.conf 
./src/redis-sentinel ./sentinel_26381.conf 


## 5 檢視哨兵是否正常啟動
ps aux |grep redis # 可以看到有三個redis-server和三個哨兵


# 6 哨兵---客戶端也可以連結
    redis-cli -p 26379
    info
    master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
    叢集名字叫:mymaster,狀態ok,主庫是:127.0.0.1:6379,有兩個從庫,哨兵有三個
# 7 停止一個從庫---》不會做轉移
    -不會轉移


# 8 停止主庫---》故障轉移---》有一個從庫,會變成主庫--》其他從庫,複製現在的主庫
    -即便原來的主庫啟動,它也是從庫了


    
    
    
# 配置檔案解釋
sentinel monitor mymaster 10.0.0.111 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
解釋:
sentinel monitor mymaster: 監控一個名為 mymaster 的主伺服器,後面跟著主伺服器的IP和埠,以及最少需要有多少個哨兵同意才進行故障轉移。
告訴sentinel去監聽地址為ip:port的一個master,這裡的master-name可以自定義,quorum是一個數字,指明當有多少個sentinel認為一個master失效時,master才算真正失效




sentinel down-after-milliseconds: 如果一個伺服器在指定的毫秒數內沒有響應,則認為它是主觀下線。
這個配置項指定了需要多少失效時間,一個master才會被這個sentinel主觀地認為是不可用的。 單位是毫秒,預設為30秒



sentinel parallel-syncs: 在故障轉移期間,可以有幾個從伺服器同時進行同步。
這個配置項指定了在發生主備切換時最多可以有多少個slave同時對新的master進行 同步,這個數字越小,完成主備切換所需的時間就越長,但是如果這個數字越大,就意味著越 多的slave因為replication而不可用。可以透過將這個值設為 1 來保證每次只有一個slave 處於不能處理命令請求的狀態。


sentinel failover-timeout: 故障轉移超時時間

python操作哨兵

# 要在別的機器能用
    -1 redis.conf   監聽地址:0.0.0.0
    -2 sentinel配置,寫真正ip地址
    sentinel monitor mymaster 10.0.0.111 6379 2

import redis
from redis.sentinel import Sentinel

# 連線哨兵伺服器(主機名也可以用域名)
# 10.0.0.101:26379
sentinel = Sentinel([('10.0.0.111', 26379),
                     ('10.0.0.111', 26380),
                     ('10.0.0.111', 26381)
         ],
                    socket_timeout=5)

print(sentinel)
# 獲取主伺服器地址
master = sentinel.discover_master('mymaster')
print(master)

# 獲取從伺服器地址
slave = sentinel.discover_slaves('mymaster')
print(slave)



##### 讀寫分離
# 獲取主伺服器進行寫入
# master = sentinel.master_for('mymaster', socket_timeout=0.5)
# w_ret = master.set('foo', 'bar')

# slave = sentinel.slave_for('mymaster', socket_timeout=0.5)
# r_ret = slave.get('foo')
# print(r_ret)

redis 叢集

#1 主從---》提高併發量
#2 哨兵----》高可用

#3 存在問題
1 併發量:單機redis qps為10w/s,但是我們可能需要百萬級別的併發量
2 資料量:機器記憶體16g--256g,如果存500g資料呢?

#4 解決:加機器,分散式
redis cluster 在2015年的 3.0 版本加入了,滿足分散式的需求


#5 資料分佈(分散式資料庫:多臺機器儲存一份資料)
    -分散式資料庫---》分割槽方案

####5.1 雜湊分佈    資料分散度高,建值分佈於業務無關,無法順序訪問,支援批次操作    一致性雜湊memcache,redis cluster,其他快取產品
5.1.1.節點取餘分割槽:

    缺點:節點擴容,新增一個節點,存在問題,很多資料需要偏移,總偏移量要大於80%,推薦翻倍擴容,由3變成6,資料量遷移為50%,比80%降低
    
    
    
    
5.1.2 一致性雜湊分割槽
客戶端分片:雜湊+順時針(最佳化取餘)
節點伸縮:隻影響臨近節點,但是還有資料遷移的情況
伸縮:保證最小遷移資料和無法保證負載均衡(這樣總共5個節點,資料就不均勻了),翻倍擴容可以實現負載均衡


5.1.3 虛擬槽分割槽(redis叢集)
    預設虛擬槽:每個槽對映一個資料子集(16384個槽),一般比節點數大(redis叢集不會超過16384臺機器)
    良好的雜湊函式:如CRC16
    服務端管理節點、槽、資料:如redis cluster(槽的範圍0–16383)

    
    5個節點,把16384個槽平均分配到每個節點,客戶端會把資料傳送給任意一個節點,透過CRC16對key進行雜湊對16383進行取餘,算出當前key屬於哪部分槽,屬於哪個節點,每個節點都會記錄是不是負責這部分槽,如果是負責的,進行儲存,如果槽不在自己範圍內,redis cluster是共享訊息的模式,它知道哪個節點負責哪些槽,返回結果,讓客戶端找對應的節點去存
    
服務端管理節點,槽,關係
    
    
    
# 5.2 順序分佈    資料分散度易傾斜,建值業務相關,可順序訪問,支援批次操作    BigTable,HBase
順序分割槽
# 原理:100個資料分到3個節點上 1--33第一個節點;34--66第二個節點;67--100第三個節點(很多關係型資料庫使用此種方式)




# 啟動 6個redis節點,一主一從架構  3個節點 存資料,每個有一個從庫,做高可用

-----------------------------------------------
# 第一步:寫6個redis配置檔案
# vi redis-7000.conf
port 7000
daemonize yes
dir "/root/redis-7.2.4/data/"
logfile "7000.log"
dbfilename "dump-7000.rdb"

cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-require-full-coverage yes 

# 第二步:快速生成剩餘5個配置檔案
sed 's/7000/7001/g' redis-7000.conf > redis-7001.conf
sed 's/7000/7002/g' redis-7000.conf > redis-7002.conf
sed 's/7000/7003/g' redis-7000.conf > redis-7003.conf
sed 's/7000/7004/g' redis-7000.conf > redis-7004.conf
sed 's/7000/7005/g' redis-7000.conf > redis-7005.conf


# 第三步;啟動6個節點
redis-server ./redis-7000.conf
redis-server ./redis-7001.conf
redis-server ./redis-7002.conf
redis-server ./redis-7003.conf
redis-server ./redis-7004.conf
redis-server ./redis-7005.conf


# 檢視叢集資訊
cluster info
cluster nodes

# 第四步:搭建叢集  
redis-cli --cluster create --cluster-replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
# yes  同意


# 第五步:測試
    往 7000 上寫 set name lqz 這個資料,寫不進去
    原因是:name被hash後,得到的槽不歸 7000管[0--5460槽]
    返回給我們錯誤,讓我們去7001上寫,順利寫進去
    
# 第六步:檢視機器資訊
    cluster nodes
    cluster info
# 第六步:叢集模式連線--->自動切換到不同節點
     redis-cli -p 7000 -c
    

# 第七:停掉一個主---》原來的從會升級為主

# 第八步:python操作redis叢集
# pip3 install redis-py-cluster

from rediscluster import RedisCluster
startup_nodes = [{"host":"127.0.0.1", "port": "7005"},{"host":"127.0.0.1", "port": "7001"},{"host":"127.0.0.1", "port": "7002"}]
rc = RedisCluster(startup_nodes=startup_nodes)
rc.set("foo", "bar")
print(rc.get("foo"))

擴成8臺機器--4個節點

#1 準備兩臺機器
sed 's/7000/7006/g' redis-7000.conf > redis-7006.conf
sed 's/7000/7007/g' redis-7000.conf > redis-7007.conf
#2 啟動兩臺機器
redis-server ./redis-7006.conf
redis-server ./redis-7007.conf

# 3 兩臺機器加入到叢集中去
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000
        
# 4 讓7007複製7006
redis-cli -p 7007 cluster replicate 7947d7df93b5a66832068f6baed8af7d823b02c3
        
# 5 遷移槽
redis-cli --cluster reshard 127.0.0.1:7000 
    -遷移4096個槽   16384/4
    -7006的機器接收槽
    -all

縮容成6臺機器--3個節點

# 第一步:下線遷槽(把7006的1366個槽遷移到7000上)
redis-cli --cluster reshard --cluster-from 7947d7df93b5a66832068f6baed8af7d823b02c3 --cluster-to 6adbf098377c788c5bd47cfa524eae5bb0c6ee32 --cluster-slots 1365 127.0.0.1:7000
yes

redis-cli --cluster reshard --cluster-from 7947d7df93b5a66832068f6baed8af7d823b02c3 --cluster-to 427b88b91d86bd876da6f36a7e0b22eabcdc28f2 --cluster-slots 1366 127.0.0.1:7001
yes

redis-cli --cluster reshard --cluster-from 7947d7df93b5a66832068f6baed8af7d823b02c3 --cluster-to 21662603b66a0263cd8b581ed58c0c5b041fd255 --cluster-slots 1365 127.0.0.1:7002
yes


#第二步:下線節點 忘記節點,關閉節點
redis-cli --cluster del-node 127.0.0.1:7000 408242644423ff0d9d29434f7c7c5450b0ec996f # 先下從,再下主,因為先下主會觸發故障轉移
redis-cli --cluster del-node 127.0.0.1:7000 7947d7df93b5a66832068f6baed8af7d823b02c3

# 第三步:關掉其中一個主,另一個從立馬變成主頂上, 重啟停止的主,發現變成了從

快取

快取更新策略

# 如果記憶體中redis資料滿了,再繼續往裡存資料,redis會觸發快取更新的策略
# 有如下幾種
LRU/LFU/FIFO演算法剔除:例如maxmemory-policy(到了最大記憶體,對應的應對策略)

# LRU -Least Recently Used,沒有被使用時間最長的
# LFU -Least Frequenty User,一定時間段內使用次數最少的
# FIFO -First In First Out,最早放進去的key

快取穿透 快取擊穿 快取雪崩

###  快取穿透
#描述:
快取穿透是指快取和資料庫中都沒有的資料,而使用者不斷髮起請求,如發起為id為“-1”的資料或id為特別大不存在的資料。這時的使用者很可能是攻擊者,攻擊會導致資料庫壓力過大。
#解決方案:
1 介面層增加校驗,如使用者鑑權校驗,id做基礎校驗,id<=0的直接攔截;
2 從快取取不到的資料,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null,快取有效時間可以設定短點,如30秒(設定太長會導致正常情況也沒法使用)。這樣可以防止攻擊使用者反覆用同一個id暴力攻擊
3 透過布隆過濾器實現:把所有使用者id放到布隆過濾器中---》請求過來---》去布隆過濾器中檢查 id在不在,如果在---》資料肯定有---》繼續往後走  ---》如果布隆過濾器中沒有---》不是我們的資料---》直接拒絕

    

### 快取擊穿
#描述:
快取擊穿是指快取中沒有但資料庫中有的資料(一般是快取時間到期),這時由於併發使用者特別多,同時讀快取沒讀到資料,又同時去資料庫去取資料,引起資料庫壓力瞬間增大,造成過大壓力
#解決方案:
設定熱點資料永遠不過期。

 
### 快取雪崩
#描述:
快取雪崩是指快取中資料大批次到過期時間,而查詢資料量巨大,引起資料庫壓力過大甚至down機。和快取擊穿不同的是,快取擊穿指併發查同一條資料,快取雪崩是不同資料都過期了,很多資料都查不到從而查資料庫。
# 解決方案:
1 快取資料的過期時間設定隨機,防止同一時間大量資料過期現象發生。
2 如果快取資料庫是分散式部署,將熱點資料均勻分佈在不同得快取資料庫中。
3 設定熱點資料永遠不過期。

補充 redis的跳躍表:

跳躍表(skiplist)是一種隨機化的資料結構是一種可以於平衡樹媲美的層次化連結串列結構——查詢、刪除、新增等操作都可以在對數期望時間下完成,以下是一個典型的跳躍表例子:

為什麼使用跳躍表?

  • 效能考慮: 在高併發的情況下,樹形結構需要執行一些類似於 rebalance 這樣的可能涉及整棵樹的操作,相對來說跳躍表的變化只涉及區域性 (下面詳細說)
  • 實現考慮: 在複雜度與紅黑樹相同的情況下,跳躍表實現起來更簡單,看起來也更加直觀

但是會存在一些問題

插入一個節點之後,就會打亂上下相鄰兩層連結串列上節點個數嚴格的 2:1 的對應關係

為了避免這一問題,它不要求上下相鄰兩層連結串列之間的節點個數有嚴格的對應關係,而是 為每個節點隨機出一個層數(level)

Redis 中的跳躍表由 server.h/zskiplistNodeserver.h/zskiplist 兩個結構定義,前者為跳躍表節點,後者則儲存了跳躍節點的相關資訊,同之前的 集合 list 結構類似,其實只有 zskiplistNode 就可以實現了,但是引入後者是為了更加方便的操作

/* ZSETs use a specialized version of Skiplists */
typedef struct zskiplistNode {
    // value
    sds ele;
    // 分值
    double score;
    // 後退指標
    struct zskiplistNode *backward;
    // 層
    struct zskiplistLevel {
        // 前進指標
        struct zskiplistNode *forward;
        // 跨度
        unsigned long span;
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    // 跳躍表頭指標
    struct zskiplistNode *header, *tail;
    // 表中節點的數量
    unsigned long length;
    // 表中層數最大的節點的層數
    int level;
} zskiplist;

建立跳躍表

具體實現 參考Redis(2)——跳躍表 - 知乎 (zhihu.com)

 

問題---

  1. MySQL索引:
    MySQL索引是資料庫表中一種特殊的資料結構,它可以幫助快速查詢和檢索資料。索引的工作原理類似於書籍的目錄,它可以讓你不用遍歷整本書就能快速找到你需要的資訊。在MySQL中,常見的索引型別有B-Tree索引(最常用),全文索引,雜湊索引等。使用索引可以顯著提高查詢效率,但也會增加寫操作的成本,因為索引也需要維護。

  2. 最左字首原則:
    最左字首原則是指在使用複合索引(一個索引包含多個列)時,查詢從索引的最左邊開始匹配字首。只有當查詢條件中使用了索引最左邊的列時,索引才會被利用。例如,如果有一個索引是(A, B, C),那麼查詢條件必須包含列A,索引才會被使用;如果查詢條件包含了列A和列B,索引會更有效;如果查詢條件只包含列B和列C,那麼這個索引就不會被使用。

  3. Celery架構:
    Celery是一個強大的非同步任務佇列/作業佇列,基於分散式訊息傳遞。它被用來處理非同步任務,從而使得應用程式的響應更加快速。Celery架構主要包括以下幾個元件:

    • 任務(Tasks): 被定義為函式,它是非同步執行的工作單元。
    • 工作者(Workers): 是執行任務的程序。
    • 訊息代理(Message Broker): 如RabbitMQ或Redis,用於儲存傳送給Celery的任務。
    • 結果後端(Result Backend): 用於儲存任務的執行結果,可以是Redis、資料庫或其他儲存方案。
    • 客戶端(Clients): 傳送任務的程式。

工作流程是這樣的:客戶端將任務傳送到訊息代理,工作者監聽訊息代理,接收任務並執行,執行結果可以被儲存在結果後端,以便之後檢索。

  1. Redis主從同步原理:
    Redis主從同步是指在Redis主從架構中,從伺服器(Slave)複製主伺服器(Master)的資料。同步分為全量複製和部分複製:
    • 全量複製: 當一個新的從伺服器連線到主伺服器時,或者主從資料同步出現問題需要重新同步時,會進行全量複製。主伺服器會生成一個當前資料庫快照,並將這個快照傳送給從伺服器,從伺服器接收到快照後載入並開始從這個狀態同步資料。
    • 部分複製: 當主從連線正常,且只是因為網路等問題導致短暫的資料不一致時,主伺服器只會將丟失的資料部分傳送給從伺服器進行同步。

Redis透過使用複製偏移量和複製積壓緩衝區來實現這兩種複製機制,確保資料的一致性和完整性。

相關文章