1. 程式人生 > >程式設計師學點xx 之 Redis

程式設計師學點xx 之 Redis

程式設計師學點xx 之 Redis

概述

其實程式設計師也要和作業系統打交道, 比如最常見的,部署自己電腦上的開發環境.

當然有時某些牛人, 覺得運維或基礎部門的同事不夠給力, 親自上手部署伺服器或線上環境,這種情況也是存在的.綜上所述, 程式設計師和運維接觸的東西是一致的, 只是涉及群集或動作原理上會差一點點.

我的目的,就是花點時間把這些運維的細碎知識梳理一下, 保證被人問起來完全不虛.

群集

單獨的redis大家應該都會部署了, 下載原始碼包, 編譯一下就成. 或者使用docker 命令 pull 一下.

下面來說群集 CLUSTER

一般 CLUSTER 習慣布6臺: 3主 3從

如果單臺會部署的話,其實也很簡單

修改一下 redis.conf, 把監聽埠, 節點等選項開啟就行了

主要涉及下列專案:

  • bind
  • daemonize
  • requirepass
  • masterauth
  • logfile
  • cluster-enabled
  • cluster-config-file
  • cluster-node-timeout
  • dbfilename
  • appendfilename

修改完成後, 把6臺redis 啟動起來, 登入進redis

/root/docker_redis_cluster/redis-3.2.11/src/redis-cli -h 127.0.0.1 -p 6379
auth 123456
info replication

然後用meet命令把其他群集加入進來即可

CLUSTER MEET 172.17.0.3 6379 
CLUSTER MEET 172.17.0.4 6379 
...
CLUSTER NODES

需要分配下槽位

redis-cli -p 6379 cluster addslots {0..5461}
redis-cli -p 6380 cluster addslots {5462..10922}
redis-cli -p 6381 cluster addslots {10923..16383}
CLUSTER NODES

分配的3臺就是主了

登入另外3臺伺服器, 分別把主新增給自己, 叢集就完成了

CLUSTER NODES
CLUSTER REPLICATE 760e4d0039c5ac13d04aa4791c9e6dc28544d7c7
# 分別登入從機,加入主伺服器的id

茶歇

昨天搞定了redis的叢集, 本來是沒什麼問題了, 但看完關於矯情的記述後我覺得, 一定會有人跳出來:

"low貨,人家redis叢集都是用叢集指令碼安裝的."

作為一個9012年的猿, 雖然yann覺得可以舉100個理由證明叢集指令碼的不方便性, 但指令碼本身還是可以瞭解一下的.

指令碼安裝

這裡所說的指令碼是叫做 redis-trib 的rb指令碼.

雖然100個理由比較難, 但少數不用的理由還是舉的出來的:

  • 需要ruby環境
  • 對容器的支援不好
  • 不支援密碼

下面逐條開撕:

需要ruby環境
yum install ruby
# 或 apt-get install ruby
gem install redis

這裡還會有一個坑:

gem時系統會報ruby的版本太低

至於如何使用rvm修改版本就是另一篇文了

對於潔癖人士 (比如yann)來說, 無端在系統裡安裝ruby是不可接受的.

況且還有衍生問題,後面會敘述.

命令引數

cd /usr/local/src/redis-3.2.11/src
./redis-trib.rb help
create 
check
info 
fix
reshard 
建立叢集
./redis-trib.rb create --replicas 1 127.0.0.1:6479 127.0.0.1:6480 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484

​ ps. 如果需要指定伺服器為從庫, 只能先新增3臺, 再通過新增節點指定其對應主庫

不支援密碼

第二點理由爆發了.

redis-trib.rb 會報無法連線叢集, 需要修改client.rb檔案

修改原始碼檔案後, 指令碼連線上了叢集,繼續其他操作

測試叢集
./redis-trib.rb check 127.0.0.1:6479
檢視資訊
./redis-trib.rb info 127.0.0.1:6479
平衡節點

根據權重分配, 比較有用的功能之一

./redis-trib.rb rebalance 127.0.0.1:6479
刪除節點

只能刪除沒有分配slot的節點

./redis-trib.rb del-node 127.0.0.1:6480
新增節點

加主庫注意新增的伺服器在前, 加從庫需要主庫id

./redis-trib.rb add-node 127.0.0.1:6488 127.0.0.1:6479

./redis-trib.rb add-node --slave --master-id 77c2a2d5e96d14a4c5b5614cb68ad27d40530f4b 127.0.0.1:6480 127.0.0.1:6479
踩坑

其實上面的操作統統沒有完成.

原因很簡單, yann使用容器搭的redis

會有2個ip, 容器外ip和容器內ip.

使用容器內ip指令碼執行不起來, 而使用容器外ip的話,指令碼完成不了. 原因請自己想一下.

到這一步,算掉到坑裡出不來了, 雖然在容器內部安裝ruby可以解決, 但容器是精簡系統, redis檔案也不全...

redis-trib.rb 對容器的支援不好

茶歇

面試別人還是很有技巧的,比如:

  1. 用容器搭一個redis出來
  2. 你知道redis-trib.rb檔案麼
  3. 如何用redis-trib.rb和剛才搭的redis做一個叢集出來

答案是做不出來, 原因請看昨天的巨坑.


生產操作

搞定了群集之後就可以做一些具體的工作了, 例如:

  • 槽位遷移
  • 再平衡
  • 單機遷移到群集
槽位遷移

為什麼要進行槽位遷移呢?

當然是為了叢集的擴容/縮容啊.

redis的槽位其實是很重要的概念.

槽位不分配掉, 叢集不能使用,

存在槽位的節點不能刪除,

槽位只分配在主節點上...

曹魏: 說我麼?

沒有, 快滾...

線上遷移, 用來完成叢集的線上橫向擴容和縮容

./redis-trib.rb reshard 127.0.0.1:6479# 檢查之後會出現互動資訊, 詢問遷移多少槽位,到哪個節點之類

引數遷移

生產中常用的方式

./redis-trib.rb reshard --from 7fa64d250b595d8ac21a42477af5ac8c07c35d83 --to 5476787f31fa375fda6bb32676a969c8b8adfbc2 --slots 10 127.0.0.1:6479
再平衡

新加入節點後, 槽位變的不平衡,可以用 rebalance 處理.

同樣有密碼的問題, 修改rb檔案或配置檔案上取消密碼看各人愛好.

./redis-trib.rb rebalance 127.0.0.1:6479
單機遷移到叢集

常用來處理歷史問題,

當年需求急, 單槍匹馬上線了12345

redis-trib.rb import--from ip:port:id # 源 單機--copy ip:port # 叢集

密碼問題,同樣需要修改檔案原始碼:

vi redis-trib.rb

承前

三天都在說使用叢集的相關的知識, 今天來說明一下叢集的工作原理.

雖然都是官方提供的東西:

Redis Cluster採用無中心節點方式實現,無需proxy代理

客戶端直接與redis叢集的所有節點連線

根據hash演算法計算出key對應的slot

在slot對應的Redis上執行命令

以CAP角度來看

Redis Cluster 屬於AP

Availability&Partition-Tolerancy

可用並分割槽容錯

安全加固

redis 是系統中的漏洞大戶

yann某次不能關機又需要root許可權,就是從它這裡拿的

私有環境操作, 請勿模仿

總之, 常見的加固如下:

  • 開啟redis密碼認證
  • 禁止使用root使用者啟動
  • 修改預設6379埠
  • 限制redis 配置檔案訪問許可權
  • 禁用或者重新命名危險命令
  • 禁止監聽在公網
  • 開啟保護模式

開啟redis密碼認證

vi redis.conf中 requirepass# 配置強密碼

禁止使用root使用者啟動

useradd -s /sbin/nolog -M redis sudo -u redis /<redis-server-path>/redis-server /<configpath>/redis.conf

修改預設6379埠

vi redis.conf中 port# 80,81,82的兄弟們

限制redis 配置檔案訪問許可權

chmod 600 /<filepath>/redis.conf

禁用或者重新命名危險命令

這條倒不是必要的, 畢竟不方便

vi redis.conf中 rename-command DEL ""# 或rename-command FLUSHALL joYAPNXRPmca

禁止監聽在公網

vi redis.conf中 bind 127.0.0.1 # 或本地ip

開啟保護模式

必要, 沒有密碼和bind只能本地訪問

vi redis.conf中 protected-mode yes

疑問

之前yann有個疑問, 不知道大家會不會有類似的懷疑:

REDIS有主從和哨兵模式了, 要CLUSTER幹嘛.

這個問題今天會解決

持久化相關命令

參考mysql備份機制

rdb相當於物理備份, aof 相當於 邏輯備份

vi redis.conf
  dbfilename
  appendfilename
# 主要引數及上下方類似名字的各種設定引數
從庫換主

做法: 把從庫清空然後從新主庫完整同步一份資料再進行續傳.

重做流程
  • 主庫bgsave自身資料到磁碟
  • 主庫傳送rdb檔案到從庫
  • 從庫開始載入
  • 載入完畢開始續傳,同時開始提供服務
現實資料

一但資料量超過20GB, 複製時間超過20分鐘

多臺從庫, 時間會倍增. 同時需要考慮網絡卡瓶頸

使用記憶體限制

vi redis.conf
maxmemory
從庫擴容

同樣問題, 從庫擴容也會遇到.

當遇到流量暴增,應急性擴容, 以上擴容同樣需要20分以上

如果仍然接受資料, 中斷時間過長同步緩衝區被覆蓋,重新同步

持久化程序阻塞Redis主執行緒, 20G記憶體耗時約為750ms


話題

都知道Redis很快, 但是有多快呢.

QPS測試

環境

i5 CPU 8GB 記憶體

REDIS測試工具
cd src  
./redis-benchmark -n 1000000 -t set,get -q
# SET: 48936.32 requests per second
# GET: 51290.85 requests per second

REDIS 的大致資料約5w

MYSQL測試工具

sysbench

url -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.deb.sh | sudo bash
sudo apt -y install sysbench

mysql -uroot -p -e"create database benchmark"

sysbench /usr/share/sysbench/oltp_read_write.lua --mysql-user=root --mysql-password=root --mysql-db=benchmark --tables=10 --table-size=1000000 --events=100000000 --report-interval=10 --threads=4 --time=300 prepare

sysbench /usr/share/sysbench/oltp_read_write.lua --mysql-user=root --mysql-password=root --mysql-db=benchmark --tables=10 --table-size=1000000 --events=100000000 --report-interval=10 --threads=4 --time=300 run

因為使用的阿里雲RDS, 這個操作我沒有做

相同配置伺服器反饋是4000左右

單執行緒REDIS

資料都在記憶體, 單執行緒去操作效率最高

多執行緒存在上下文的切換

一次CPU上下文的切換約 1500ns

從記憶體中讀取 1MB 的連續資料,耗時大約為 250us

假設多執行緒讀取了1000次,光切換耗時1500ns * 1000 = 1500us

多執行緒REDIS

Redis 6 引入的多執行緒 IO 特性

優化方向

提高網路 IO 效能,使用 DPDK

動用多個核心執行多執行緒, 僅用在處理網路資料和協議解析

實現原理
  • 主執行緒接收建連請求,讀事件放佇列處理
  • 主執行緒將連線分配給其他 IO 執行緒,然後主執行緒等待
  • IO 執行緒將請求資料讀取並解析
  • 主執行緒執行命令並清空佇列
效能對比
vi redis.conf
  io-threads 4
  io-threads-do-reads yes
# 僅記錄, 拿到後測試一下, 需要gcc 5.0+
redis-benchmark -h 192.168.0.49 -a foobared -t set,get -n 1000000 -r 100000000 --threads 4 -d ${datasize} -c 256

週報

Redis也看了一週了, 每天測試,記筆記, 總結...

感覺可以暫停一下了, 明天會有新的東西.

以下以Redis的最後一部分


快取雪崩

所謂快取雪崩就是快取掛掉了,全部請求都跑去找資料庫了

這裡有2個前提:

  • 業務流量大
  • 系統沒有降級限流設定措施

先說流量, Mysql 5.7 預設最大連線數200, 通常會配置成2000, 加上只讀庫可以看作4000. 加上普遍連線池運作, 一般可以抗一段時間, 堅持到redis拉起來.

再說限流, SpringCloud和 Dubbo 都可以有 Hystrix, 比較核心的業務, 建議配置起來.

如何解決快取雪崩?

基於系統的故障總是好解決的

Redis叢集也好, 對其本身的監控或對Mysql的監控也好, 都可提醒有故障了, 去處理.

難的是邏輯上的故障

比如寫死過期時間, 結果同時全部過期,

再比如新功能上線直接沒快取. 需要快取預熱等.


快取穿透

快取穿透就很簡單了, 就是一直請求不存在的引數.

結果 Redis就沒東西提供, 直接交給資料庫處理了.

同樣和數量有關, 少量夠不成危害. 而大量的話, redis命中率偏低, 直接會有報警出來.

解決方式也有二種:

要麼布隆過濾不合理引數, 要麼乾脆快取空物件, 使用哪種方式, 就看自己方便了.


REDIS與MYSQL 一致性

快取什麼都好, 就是一致性讓人頭疼. 不光Redis和Mysql, 瀏覽器的快取, 甚至CDN, 一切快取相關的問題都讓人頭疼.

讀操作

如果資料在快取裡邊有,那麼直接取快取返回。

如果快取裡沒有, 先去查詢資料庫,然後將結果寫到快取中。最後將資料返回給請求。

寫操作

先更新資料庫, 再操作快取失敗.

優雅一點的做法是, 在MySQL端定義CRUD觸發器,或在Redis端解析binlog, 進行操作.

更現實的做法是, 用訊息佇列保證更新操作能夠完成, 甚至對同樣資料的請求放在一個佇列裡, 當然也可能全部卡住.

更新快取

這裡沒有涉及更新快取, 事實上定時更新也好, 邏輯判斷有效性也好, 都是基於業務特性來決定的.

快取演算法

FIFO 先進先出

LRU 近期最少使用, 使用時間差異

LFU 最不經常使用, 次數差異

注: Redis 4.0 才有LFU.


總結

從1號到現在, 蜻蜓點水寫了很多. 大部分都關注於坑和生產應用, 對日常使用Redis的記錄比較少.

沒辦法, 時間還是不夠. 就算強力驅動著自己還是淺嘗輒止. 任何一個應用都可以靜下心來揣摩一個月, 但是沒有時間.

現在只能拼命吃一點, 以求遇到的時候不要沒有思路. 其他只能後續研究了. 共勉!

本文由部落格一文多發平臺 OpenWrite 釋出!
最新內容歡迎關注公眾號: