文章目錄 "Redis的介紹、優缺點、使用場景" Linux中的安裝 常用命令 "Redis各個數據類型及其使用場景" Redis字元串(String) Redis哈希(Hash) Redis列表(List) Redis集合(Set) Redis有序集合(sorted set) "Redis 瑞士軍 ...
文章目錄
- Redis的介紹、優缺點、使用場景
- Linux中的安裝
- 常用命令
- Redis各個數據類型及其使用場景
- Redis字元串(String)
- Redis哈希(Hash)
- Redis列表(List)
- Redis集合(Set)
- Redis有序集合(sorted set)
- Redis - 瑞士軍刀
- 慢查詢
- pipeline流水線
- 發佈訂閱
- bitmap
- HyperLogLog演算法
- GEO
- Redis持久化,數據備份與恢復
- 高可用
- Redis安全如何保證
- Redis性能測試
- 整理自己的RedisUtil https://www.runoob.com/redis/redis-java.html
- Redis面試題彙總
- Redis為什麼那麼快
- Redis常見數據類型及使用場景
- 如何保證Redis和資料庫雙寫一致性
- Redids集群通信原理
- Redis CAS
- Redis如何實現高可用
Redis的介紹、優缺點、使用場景
- Redis是什麼: 開源的,基於鍵值的存儲服務系統,支持多種數據類型,性能高,功能豐富
- 特性(主要有8個特性):
- 速度快:官方給出的結果是10W OPS,每秒10W的讀寫(為什麼是10W,因為記憶體的相應時間是100納秒-10萬分之一秒)。數據存儲在記憶體中;使用C語言開發;Redis使用單線程,減少上下文切換。本質原因是電腦存儲介質的速度,記憶體比硬碟優幾個數量級)。MemoryCache可以使用多核,性能上優於Redis。
- 持久化:Redis所有的數據保持在記憶體中,對數據的更新將非同步地保存到磁碟上。斷掉,宕機? RDB快照/AOF日誌模式來確保。MemoryCache不提供持久化
- 多種數據結構:Redis提供字元串,HashTable, 鏈表,集合,有序集合;另外新版本的redis提供BitMaps點陣圖,HyperLogLog超小記憶體唯一值計數,GEORedis3.2提供的地理位置定位。相比memocache只提供字元串的key-value結構
- 支持多種編程語言:Java,PHP,Ruby,Lua,Node
- 功能豐富: 發佈訂閱,支持Lua腳本,支持簡單事務,支持pipline來提高客戶端的併發效率
- 簡單:單機核心代碼23000行,讓開發者容易吃透和定製化;不依賴外部庫;單線程模型
- 主從複製:主伺服器的數據可以同步到從伺服器上,為高可用提供可能
- 高可用、分散式:2.8版本後提供Redis-Sentinel支持高可用;3.0版本支持分散式
- 典型應用場景:
- 緩存系統:緩存一些數據減少對資料庫的訪問,提高響應速度
- 計數器:類似微博的轉發數,評論數,incr/decr的操作是原子性的不會出錯
- 消息隊列系統:發佈訂閱的模型,消息隊列不是很強
- 排行版: 提供的有序集合能提供排行版的功能,例如粉絲數,關註數
- 實時系統:利用點陣圖實現布隆過濾器,秒殺等
安裝
- Linux中安裝
wget http://download.redis.io/releases/redis-5.0.7.tar.gz tar -zxvf redis-5.0.7.tar.gz mv redis-5.0.7 /usr/local/redis 不需要先創建/usr/local.redis文件夾 cd /usr/local/redis make make install vi redis.conf * bind 0.0.0.0 開發訪問 * daemonize yes 設置後臺運行 redis-server ./redis.conf 啟動 redis-cli 進入命令行,進行簡單的命令操作 vi redis.conf > requirepass password 修改密碼 redis-cli 再次進入cmd > shutdown save 關閉redis,同時持久化當前數據 redis-server ./redis.conf 再次啟動redis redis-cli 進入命令行 > auth password 將redis配置成系統服務,redis/utils中自帶命令,我們只需修改參數 /usr/local/redis/utils/./install_server.sh [root~ utils]# ./install_server.sh Welcome to the redis service installer Please select the redis port for this instance: [6379] 預設埠不管 Selecting default: 6379 Please select the redis config file name [/etc/redis/6379.conf] /usr/local/redis/redis.conf 修改配置文件路徑 Please select the redis log file name [/var/log/redis_6379.log] /usr/local/redis/redis.log 修改日誌文件路徑 Please select the data directory for this instance [/var/lib/redis/6379] /usr/local/redis/data 修改數據存儲路徑 Please select the redis executable path [/usr/local/bin/redis-server] Selected config: Port : 6379 Config file : /usr/local/redis/redis.conf Log file : /usr/local/redis/redis.log Data dir : /usr/local/redis/data Executable : /usr/local/bin/redis-server Cli Executable : /usr/local/bin/redis-cli chkconfig --list | grep redis 查看redis服務配置項 redis_6379 0:off 1:off 2:on 3:on 4:on 5:on 6:off 服務名是redis_6379
- 可執行文件說明
- redis-server: Redis伺服器,啟動Redis的
- redis-cli: Redis命令行客戶端連接
- redis-benchmark: 對Redis做性能測試
- redis-check-aof: AOF文件修複工具
- redis-check-dump: RDB文件檢查工具
- redis-sentinel: Sentinel伺服器(2.8以後)
- 啟動方式
- redis-server: 最簡單的預設啟動,使用redis的預設參數
- 動態參數啟動:redis-server --port yourorderpoint
- 配置文件的方式: redis-server configpath
- 比較:
- 生產環境選擇配置啟動;單機多實例配置文件可以選擇配置文件分開
- Redis客戶端返回值
- 狀態回覆:ping->pong
- 錯誤恢復:執行錯誤的回覆
- 整數回覆:例如incr會返回一個整數
- 字元串回覆: get
- 多行字元串回覆:mget
- 常用配置
- daemonize: 是否是守護進程(y/n)
- port埠:預設是6379
- logfile:Redis系統日誌
- dir:Redis工作目錄
- 常用命令:線上練習http://try.redis.io/
redis-cli -h x.x.x.x -p x 連接 auth "password" 驗證密碼 redis-cli --raw可以避免中文亂碼 exit 退出 select index 切換到指定的資料庫 keys * 顯示所有key,如果鍵值對多不建議使用,keys會遍歷所有key,可以在從節點使用;時間複雜度O(N) dbsize 算出所有的key的數量,只是數量;時間複雜度O(1) exists key key是否存在,存在返回1,不存在返回0;時間複雜度O(1) incr key 將key的值加一,是原子操作 decr key 將key的值加一,會出現複數,是原子操作 del key 刪除key,刪除成功返回1,失敗返回0;時間複雜度O(1) expire key seconds 設置過期時間,過期之後就不存在了;時間複雜度O(1) ttl key 查看key剩餘的過期時間,key不存在返回-2;key存在沒設置過期時間返回-1; persist key 去掉key的過期時間,再查看ttl key,返回值是-1,表示key存在並且沒有設置過期時間 type key 查看類型;時間複雜度O(1) config get * 獲取配置信息 set key value插入值 sadd myset 1 2 3 4 插入set get key獲取值 del key刪除key cat redis.conf | grep -v "#" | grep -v "^$" 查看配置文件,去除所有的#,去除所有的空格 setnx key value #key不存在,才設置 set key value xx #可以存在,才設置 set key value [exporation EX seconds | PX milliseconds] [NX|EX] mget key1 key2 key3 批量獲取 1次mget=1次網路時間+n次命令時間;時間複雜度O(n) mset key1 value1 key2 value2 批量插入;時間複雜度O(n) n次get = n次網路時間 + n次命令時間,mget一次就能完成,省去大量的網路時間 getset key newvalue # set key newvalue並返回舊的value append key value #將value追加到舊的value strlen key #獲取value的長度,中文占2個位元組 incrbyfloat key 3.5 #增加key對應的值 set/get/del, incr(自增1)/decr(自減1)/incrby(incrby key n自增n)/decrby getrange key start end #獲取value從start到end的值 setrange key index value #設置指定下標為一個新的值 hset key field value #給key的field設置值 hget key field #獲取key的field的值 hdel key field #刪除key的field的值 hgetall key #獲取key的所有值 hexists key field # 判斷key的field是否存在 hlen key #獲取key field的數量 hmset key field1 value1 field2 value2 hmget key field1 field2 hsetnx/hincrby/hdecry/hincrbyfloat lpush key value1 value2...valueN #從左邊插入 rpush key value1 value2...valueN #從右邊插入 linsert key before|after value newValue rinsert key before|after value newValue lpop key #從左邊彈出一個item rpop key #從右邊彈出一個item lrem key count value #若count等於0或者不填,表示刪除所有的value值相等的item;若count>0,表示從左到右刪除最多count個value相等的item;若count<0,表示從右到左,刪除最多Math.abs(count)個value相等的項 ltrim key start end #按照索引範圍修剪列表,可以用來慢刪除,因為全刪除可能會阻塞redis lrang key start end #獲取key中從start到end的值 lindex key index #取第index的值 llen key #算出列表的長度 lset key index newValue #修改index的值為newValue blpop key timeout #lpop阻塞版本,timeout是阻塞時間,timeout=0表示死等,lpop會立馬返回,有時候數據更新不那麼及時,或者消息隊列中消息未及時處理,我們可以使用這個 brpop key timeout lpush + LPOP = STACK lpush + RPOP = QUEUE lpush + ltrim = 有序的集合 lpush + rpop = 消息隊列 sadd key value #不支持插入重覆元素,失敗返回0 srem key element #刪除集合中的element元素 smembers key #查看集合元素 sinter key1 key2 #取出相同:交集 sdiff key1 key2 #取出key1中key2沒有的元素:差集 sunion key1 key2 #取出二者所有的元素:並集 sdiff|sinter|sunion store key #將結果存到key中,有時候計算一次耗時 scard key #計算集合大小 sismember key element #判斷element是否在集合中 srandmember #返回所有元素,結果是無序的,小心使用,可能結果很大 smembers key #獲取集合中的所有元素 spop key #從集合中隨機彈出一個元素 scan SADD = Tagging SPOP/SRANDMEMBER = Random item SADD + SINTER = Social Graph zadd key score element #添加score和element O(logN): 使用xx和跳錶的數據結構 zrem key element #刪除元素 zscore key element #返回元素的分數 zincrby key increScore element #增加或減少元素分數 zcard key #返回元素的總個數 zrank key element #獲取element的排名 zrange key start end [withscores] #返回指定索引範圍內的升序元素 zrangebyscore key minScore maxScore [withscore] #返回分數在minScore和maxScore之間的元素 zcount key minScore maxScore #返回有序集合內在指定分數範圍內的個數 zremrangebyrank key start end #刪除指定排名內的元素 zremrangebyscore key minScore maxScore #刪除指定分數內的元素 zrevrang/zrevrange/集合間的操作zsetunion info replication 查看分片,能夠獲取到主從的數量和狀態 config get databases 獲取所有資料庫
數據結構和內部編碼
- Reids支持5中存儲的數據格式: String, Hash, List, Set, Sorted Set
-
string
- redis 的 string 可以包含任何數據。比如jpg圖片或者序列化的對象,最大能存儲 512MB。
- 使用場景:緩存/計數器/分散式鎖...
- 常用命令:
- 實戰:實現分散式的id生成器,可以使用incr的思路,但是實際中會比這複雜
-
hash
- 是一個鍵值(key=>value)對集合。Redis hash 是一個 string 類型的 field 和 value 的映射表,hash 特別適合用於存儲對象。
- 實戰:統計用戶主頁的訪問量, hincrby user:1:info pageview count
-
list
- Redis 列表是簡單的字元串列表,按照插入順序排序。列表最多可存儲 232 - 1 元素 (4294967295, 每個列表可存儲40多億)。
- 實戰:微博按時間順序展示消息
-
set
- 是 string 類型的無序集合,不允許插入重覆元素,插入重覆元素失敗返回0。集合是通過哈希表實現的,所以添加,刪除,查找的複雜度都是 O(1)。
- 實戰:抽獎系統(量不是很大的時候);like,star可以放到集合中;標簽tag
-
zset
- 有序集合:有序且無重覆元素,和 set 一樣也是string類型元素的集合,且不允許重覆的成員。不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。zset的成員是唯一的,但分數(score)卻可以重覆。
- 實戰:排行榜
-
Redis客戶端: Java的Jedis(Socket通信),Python的redis-py
瑞士軍刀
慢查詢
- 生命周期
兩點說明:
- 慢查詢發生在第3階段,比如keys *等這些需要掃描全表的操作
- 客戶端超時不一定慢查詢,但慢查詢是客戶端超時的一個可能因素
- 兩個配置
- slowlog-log-slower-than=n(微秒):命令執行時間超過x微秒,會被丟到一個固定長度的慢查詢queue中;n<0表示不配置
- slowlog-max-len: 先進先出的隊列,固定長度,保存在記憶體中(重啟redis會消失)
- 配置方法
- 預設值
- config get slowlog-max-len=128
- config get slowlog-log-slower-than=10000
- 修改配置文件重啟
- 動態配置
- config set slowlog-max-len 1000
- config set slowlog-log-slower-than 1000
- 預設值
- 常用命令
- slowlog get [n]:獲取慢查詢隊列
- slowlog len: 獲取慢查詢隊列的長度
- slowlog reset: 清空慢查詢隊列
- 運維經驗
- slowlog-max-len不要設置過大,預設10ms,通常設置1ms,根據QPS來設置
- slowlog-log-slower-than不要設置過小,通常設置1000左右
- 定期持久化慢查詢
pipeline流水線(批量操作)
當遇到批量網路命令的時候,n次時間=n次網路時間+n次命令時間。舉個例子,北京到上海的距離是1300公裡,光速是3萬公裡/秒,假設光纖傳輸速度是光速的2/3,也就是萬公裡/秒,那麼一次命令的傳輸時間是 1300/20000*2(來回)=13毫秒,
什麼是pipeline流水線,1次pipeline(n條命令)=1次網路時間+n次命令時間;pipeline命令在redis服務端會被拆分,因此pipeline命令不是一個原子的命令。註意每次pipeline攜帶數據量;pipeline每次只能作用在一個Redis節點上;M操作和pipeline的區別,M(mset)操作是redis的原生命令,是原子操作,pipeline不是原子操作。
for(int i = 0; i < 10000; i++>) {
jedis.hset(key, field, value); //1萬次hset差不多要50秒
for(0->100) {
Pipeline pipeline = jedis.pipelined();
for(0->100) {
pipeline.hset(key,field,value);
}
pipeline.syncAndReturnAll(); //拆分100次,每次100個命令,大概需要0.7秒
}
發佈訂閱:類似生產者消費者模型
- 角色:發佈者(publisher),頻道(channel),訂閱者(subscriber); 發佈者將消息發佈到頻道中,訂閱者訂閱相關的頻道;
- API: publish/subscribe/unsubscribe
- publish channel message : publish sohu:tv "hello world"
- subscribe sohu:tv
- unsubscribe [channel]
- psubscribe [pattern] #訂閱模式 sohu*
bitmap:點陣圖:數據量很大的時候節省存儲記憶體,數據量小了,不節省
hyperloglog(演算法,數據結構):
- 極小空間完成獨立數量統計,本質是個string
- api: pfadd key element[s]:向hyperloglog添加元素 pfcount key[s]:計算hyperloglog的獨立總數 pfmerge key1 key2合併
GEO: 3.2提供的用於計算地理位置信息;數據類型是zset,可以使用zset的刪除命令
-
使用場景:微信搖一搖看附近好友
-
api:
- geo key longitude latitude member #增加地理位置信息
- geopos key member[n] #獲取地理位置信息
- geodist key member1 membe2 [unit] m米 km千米 mi英里 ft尺 獲取兩地位置的距離
- georadius #算出指定範圍內的地址位置信息的集合,語法複雜了點
-
總結下Redis數據結構和類型的常見用法
類型 | 簡介 | 特性 | 使用場景 |
---|---|---|---|
String | 二進位安全 | 可以包含任何數據,比如JPG圖片或者序列化的對象,一個鍵最大能存儲512M | |
Hash | 鍵值對集合,即編程中的Map | 適合存儲對象,並且可以向資料庫中那樣update一個屬性(Memcache中需要將字元串反序列化成對象之後再修改屬性,然後序列化回去) | 存取/讀取/修改 用戶信息 |
List | 雙向鏈表 | 增刪快,提供了操作某一元段元素的API | 1. 最新消息,按照時間線顯示 2. 消息隊列 |
Set | 哈希表實現,元素不重覆 | 添加/刪除/修改的複雜度都是O(1),為集合提供求交集/並集/差集的操作 | 1. 打label/tag,如文章 2. 查找共同好友 3. 抽獎系統 |
Zset | 將Set中的元素增加一個double類型的權重score,按照score排序 | 數據插入集合就好序了 | 排行榜 |
Hyperloglog | 本質是string | 極小空間完成獨立數據量統計 | 統計基數,不完全正確 |
GEO | 數據類型是zset | 存儲地理位置信息,並提供計算距離等操作 | 微信搖一搖查看附近好友 |
Bitmap | 點陣圖 | 數據量很大的時候節省存儲記憶體,數據量小了不節省 | 1. 設置用戶的狀態 2. BitMap解決海量數據尋找重覆、判斷個別元素是否在 |
Redis持久化
-
持久化的作用:redis所有數據保存在記憶體中,對數據的更新將非同步地保存到磁碟上。
-
主流資料庫持久化實現方式:快照(MySQL Dump/Redis RDB),寫日誌(MySQL Binlog/Redis AOF)
-
RDB:
- 創建RDB文件(二進位文件)到硬碟中,啟動後載入RDB文件到記憶體
- 三種觸發機制
-
save(同步) - 會產生阻塞
- 文件策略:如存在老的RDB文件,新的替換老的,新的會先生成到一個臨時文件
-
bgsave(非同步) - 不會阻塞
- 客戶端執行bgsave之後,redis會使用linux的一個fork()命令生成主進程的一個子進程(fork的操作會執行一個記憶體頁的拷貝,使用copy-on-write策略),子進程會創建RDB文件,創建完畢後將成功的消息返回給redis。fork()出來的子進程執行快的話不會阻塞主進程,否則也會阻塞redis,阻塞的實際點就是生成出來這個子進程。由於是非同步,在創建的過程中還有其他命令在執行,如何保證RDB文件是最新的呢?在數據量大的時候bgsave才能突出優點。
命令 save bgsave IO類型 同步 非同步 阻塞 是 是(阻塞發生在fork子進程 複雜度 O(n) O(n) 優點 不會消耗額外記憶體 不阻塞客戶端命令 缺點 阻塞客戶端命令 需要fork,消耗記憶體 -
自動觸發:多少秒內有多少changes會非同步(bgsave)生成一個RDB文件,如60秒內有1W條changes,預設的規則,可以改;不是很好吧,無法控制頻率;另外兩條是900秒內有1條changes, 300秒內有10條changes;
-
配置
- dbfilename dump.rdb
- dir ./
- stop-writes-on-bgsave-error yes 當bgsave發生錯誤是停止寫RDB文件
- rdbcompression yes 採用壓縮格式
- rdbchecksum yes 採用校驗和
-
其他不能忽視的點:
- 全量複製;debug reload;shutdown save會執行rdb文件的生成
-
-
AOF:
- RDB現存問題:耗時,耗性能(fork,IO),不可控(突然宕機)
- AOF:redis中的cmd會先刷新到緩衝區,然後更具配置AOF的策略,非同步存追加到AOF文件中,發生宕機後,可以通過AOF恢復,基本上數據是完整的
- AOF的三種策略(配置的三種屬性)
- always:來一條命令寫一條;不丟失數據,IO開銷較大
- everysec:每秒把緩衝區fsync到AOF文件;丟1秒數據
- no:操作系統決定什麼時候把緩衝區同步到AOF就什麼時候追加;不用配置,但是不可控,取決於操作系統
- AOF重寫
- 如果AOF文件很大的話,恢復會很慢,AOF的重寫是優化一些命名,使其變成1條,對於過期數據沒必要Log,本質是把過期的沒有用的,重覆的過濾掉,以此減少磁碟占用量,加速恢復。極端的例子,1億次incr,實際只需要set counter n就夠了
- 重寫的兩種方式
- bgrewriteaof:非同步執行,redis fork出一個子進程,然後進行AOF重寫
- AOF重寫配置
- auto-aof-rewrite-min-size: AOF文件到達多大的時候才開始重寫
- auto-aof-rewrite-percentage: AOF文件的增長率到達了多大才開始重寫
- 統計
- aof_current_size AOF當前尺寸 位元組
- aof_base_size AOF上次重啟和重寫的尺寸 位元組,方便自動重寫判斷
- 重寫觸發機制(同時滿足如下兩條)
- aof_current_size > auto-aof-rewrite-min-size
- (aof_current_size - aof_base_size) / aof_base_size > auto-aof-rewrite-percentage
- 其他配置
- appendonly yes
- appendfilename ""
- appendfsync everysec
- dir /xx
- no-appendfsync-on-rewrite yes AOF在重啟之後恢復,要權衡是否開啟AOF日誌追加的功能,這個時候IO很大,如果設置為yes,也就意味著在恢復之前的日誌數據會丟失
-
RDB & AOF最佳策略:RDB優先於AOF先啟用
- RDB:建議關掉,集中管理,在從節點開RDB
- AOF:建議開啟,每秒刷盤
- 最佳策略:小分片(log文件分片)
-
常見問題
- fork操作:是一個同步操作,做一個記憶體頁的拷貝;與記憶體量息息相關,記憶體越大,耗時越長;執行info命令,有個latest_fork_usec的值,看下上次fork執行耗時
- 進程外開銷:
- CPU:RDB AOF文件生成,屬於CPU密集型操作(不要和CPU密集型應用部署在一起,減少RDB AOF頻率);記憶體:fork記憶體開銷;硬碟:IO開銷大,選用SSD磁碟
- AOF追加阻塞:主線程將命令刷到AOF緩衝區,同步線程同步命令到硬碟,同時主線程會對比上次fsync的時間,如果大於2秒就阻塞主線程,否則不阻塞,主線程這麼做是為了達到每秒刷盤的目的,讓子線程完成AOF,以此來達到數據同步。AOF發生阻塞怎麼定位:redis日誌/info persistence(aof_delayed_fsync累計阻塞次數,是累計,不好分清什麼時候發生阻塞)
- 單機多實例部署
高可用
Redis主從複製
-
主從複製:單機故障/容量瓶頸/QPS瓶頸;一個master可以有多個slave,一個slave只能有一個master,數據必須是單流向,從master流向slave
-
複製的配置:
- 使用slaeof命令,在從redis中執行slave masterip:port使其成為master的從伺服器,就能從master拉取數據了;執行slaveof no one清除掉不成為從節點,但是數據不清楚;
- 修改配置, slaveof ip port / slave-read-only yes(從節點只做都操作);配置要更改的話,要重啟,所以選擇的時候謹慎
-
全量複製
- run_id(使用info server可以看到run_id),重啟之後run_id就沒有了,當從伺服器去複製主伺服器,主伺服器run_id會在從伺服器上做一個標識,當從伺服器發現主伺服器的run_id發生了變化,說明主伺服器發生了變化(重啟或者什麼的),那麼從伺服器就要把主伺服器的數據都同步過來
- 偏移量:部分複製中的一個依據,後面說
- 解析下上面的全量複製的過程,slave向master發送psync的命令要去master全量複製數據(PSYNC <MASTER_RUN_ID>
,其中?表示我不知道master的runId啊,第一次連嘛,-1表示我都要,這時候slava咱啥也不知道),master大人收到了小弟的請求之後,大方的把自己的runId/offset發了過去,小弟收到後先存下來;在master大人把自個的信息發給小弟之後,立馬投入了創建快照RDB的工作,一個bgsave命令立馬開工,RDB生產了就發給slave;咦,細心的我們發現你這不對啊,你master創建快照到創建完成這之間新增的數據咋辦,master吭吭了兩聲,我在開始快照的那一刻,後期的所有寫命令都額外往buffer中存了一份,來保證我給你的是完整的,當我發送完RDB之後,立馬給你發buffer;slave小弟內心對master大人產生了膜拜之情,收到了RDB/buffer之後,先把自己的老數據flush掉,然後load RDB,把最新的buffer刷一遍,分分鐘讓自己向master看齊。 - 開銷:bgsave時間, RDB文件網路傳輸時間,從節點清空數據時間,從節點載入RDB的時間,可能的AOF重寫時間
-
部分複製:
- 解釋下上面的部分複製的過程,當遇到網路抖動,那這段時間內數據在slave上就會發生丟失,那麼這些數據slave是不知道的,在2.8之前redis會重新做一次全量複製,但是很顯然這樣做開銷很大,2.8之後提出部分複製的功能;當matster發現slave連接不上的時候,master在進行寫操作的時候,也會往緩衝區寫,等到下一次slave連上之後,slave會發送一條pysnc {offset}{runId}的命令,其中offset是slave自己的,相當於告訴master我的偏移量是多少,master判斷slave的offset在緩衝區內(緩衝區有start/end offset)就向slave發送continue命令,然後把這部分數據發送給slave;當master發現slave這個offset偏移量很大的時候,也就意味著slave丟失了很多數據,那麼就進行一次全量複製
-
故障處理:
- master/slave宕機的情況,主從模式沒有實現故障的完全自動轉移
- 常見問題:
- 讀寫分離:讀流量分攤到從節點,可能遇到複製數據延遲,也可能讀到過期的數據,從節點故障怎麼辦
- 主從配置不一致:主從maxmemory不一致,可能會丟失數據;主從記憶體不一致
- 規避全量複製:第一次不可避免;小主節點,低峰處理(夜間);主節點重啟後runId發生了變化
- 規避複製風暴
- 單機主節點複製風暴,如果是1主N從,當master重啟之後,所有的slave都會發生全量複製,可想而知這樣非常容易造成redis服務的不可用
Redis-Sentinel
- 主從複製高可用?
- 手動故障轉移,例如選出新的slave做master;寫能力和存儲能力受限;
- 架構說明
- Redis Sentinel是一個監控redis主從以及實施故障轉移的工具,sentinel不是一個是多個的(會選舉出一個master sentinel),這樣可以保證sentinel的高可用和公平(不是一個sentinel判斷不可用就不可用),可以把Redis Sentinel看成一個redis的額外進程,用來監控reids服務的可用與不可用;客戶端不再記住redis的地址,而是記錄sentinel的地址,sentinel知道誰是真的master;當多個sentinel發現並確認master有問題,sentinel內部會先選出來一個領導,讓這個領導來完成故障的轉移(因為執行slave no noe/new master這些命令只需要一個sentinel就夠了),sentinel從slave中選舉一個作為master,然後通知其他的slave去新的master獲取數據。sentinel可以監控多套master-slave,
- 安裝配置
- 配置開啟主從節點
- 配置開啟sentinel監控主節點(sentinel是特殊的redis):sentinel預設埠是23679
sentinel monitor mymaster 127.0.0.1 7000 2 監控的主節點名字是mymaster,2表示2個sentinel覺得當前master有問題提才發生故障轉移 sentinel down-after-milliseconds mymaster 30000 表示30秒不通之後就停掉master sentinel parallel-syncs mymaster 1 表示每次併發的複製是1個在複製,這樣可以減少master的壓力 sentinel failover-timeout mymaster 180000 故障轉移時間
- 實現原理: redis sentinel做失敗判定和故障轉移
- redis sentinel內部有三個定時任務
-
- 每10秒每個sentinel對master和slave執行info:可以從replication中發現slave節點,確認主從關係
-
- 每2秒每個sentinel通過master節點的channel交換信息(pub/sub): 什麼意思呢,master節點上有個發佈訂閱的頻道用於sentinel節點進行信息交換,利用的原理就是每個sentinel發佈一個信息其他sentinel都可以收到這樣一個原理,這個信息都包含當前sentinel節點的信息,以及它當前對master/slave做出的判斷。這個頻道是啥呢,sentinel:hello,這個名字內部規定的
-
- 每1秒每個sentinel對其他sentinel和redis執行ping操作-心跳檢測,是失敗判斷的依據
-
- redis sentinel內部有三個定時任務
- 主觀下線和客觀下線:
- sentinel monitor
quorum是法定人數,有quorum個sentinel認為master不可用了那麼master就會被客觀下線 - sentinel down-after-milliseconds
一個sentinel如果在timeout毫秒內沒收到master的回覆就做主動下線的操作 - 主觀下線:每個sentinel節點對redis節點失敗都有自己的判斷,這裡的節點可以是master,也可以是slave
- 客觀下線:所有的sentinel節點對某個redis節點認為失敗的個數達到quorum個才下線
- sentinel monitor
- 領導者選舉
- 為啥要選領導者,因為只需要一個sentinel節點就能完成故障轉移。怎麼選舉呢?
- 每個做完主觀下線的sentinel節點(就是發現某個節點不可用了,併發出了自己的判斷的節點)都會向其他sentinel節點發送sentinel is-master-down-by-addr命令,要求將自己設置為領導者。那麼收到這個命令的sentinel如果在之前沒有同意過其他sentinel的話,就會同意這個請求,否則拒絕,換句話說每個sentinel只有一個同意票,這個同意票給第一個問自己的節點。好了,票發完了,如果這個sentinel節點發現自己擁有的票數超過sentinel集合半數並且操作quorum,那麼它將成為領導者;如果此過程有多個sentinel節點成為領導者,那麼過段時間將重新進行一次選舉。領導者選舉使用的是一個Raft演算法,以上是抽象過程。所以sentinel的個數(>=3最好為奇數)和quorum的個數需要合理配置。
- 為啥要選領導者,因為只需要一個sentinel節點就能完成故障轉移。怎麼選舉呢?
- 故障轉移(sentinel領導者節點完成)
-
- 從slave節點中選舉一個“合適的”節點作為新的master節點
-
- 對上面的slave節點執行slaveof no one命令讓其成為master節點
-
- 向剩餘的slave節點發送命令,讓它們成為新的master節點的slave節點,複製規則和parallel-syncs(允許並行複製的個數)參數有關。複製的過程master是做了優化的,只需要一個RDB的生成,然後同時向slave節點發送RDB和buffer,有一定的開銷,特別是網路
-
- 更新對原來master節點配置為slave,並保持對其“關註”,當其恢復後命令它去複製新的master節點
如何選擇合適的slave的節點
-
- 選擇slave-priority(slave節點優先順序)最高的slave節點,如果存在就返回,不存在則繼續。一般不配置這個參數,什麼情況下配置呢,當有一臺slave節點的機器配置很高,我們希望當master掛了之後它能成為新的master時,做這個設置。
-
- 選擇複製偏移量最大的slave節點(複製的最完整),如果存在則返回,不存在則繼續。
-
- 選擇runId最小的slave節點。runId最小就是最早的slave節點,假設它複製的最多。
-
-
運維和開發
- 節點運維:上下和下線
- 機器下線:機器因為不能用了或者過保等什麼原因要換機器
- 機器性能不足:例如CPU、記憶體、硬碟、網路等
- 節點自生保障:例如服務不穩定等
- sentinel failover
主節點主動故障轉移,已經選舉了sentinel領導者,所以上述過程可以省略 - 從節點下線:臨時下線還是永久下線。永久下線可能要清理掉一些配置文件,從節點下線的時候也要考慮讀寫分離的情況,因為這時候有可能正在讀。
- 節點上線: 主節點執行sentinel failover進行替換,從節點slaveof即可,sentinel節點可以感知
- sentinel failover
JedisSentinelPool的實現
看源代碼
-
- switch-master: 切換主節點
-
- convert-to-slave: 切換從節點
-
- sdown:主觀下線
Redis-Cluster
- 為什麼需要集群
- 併發量/
- 數據量: 業務需要500G怎麼辦,機器記憶體是16~256G
- 網路流量
Redis 3.0版本提供了分散式技術
- 數據分佈
- 數據分區
兩種方式
- 順序分區
- 哈希分區
- 節點取餘分區
- 新增節點之後基本所有數據要產生飄逸,一般產生多倍擴容節點,飄逸量少
- 一致性哈希分區
- 解決了哈希分區中新增節點造成書飄逸的問題
- 虛擬槽分區
- 好複雜沒咋懂
- 節點取餘分區
- 搭建集群
- 原生安裝(模擬3主3從)
-
- 配置開啟節點 enable-sync yes
port ${port} daemonize yes dir "/path" dbfilename "dump-${port}.rdb" logfile "${port}.log" cluster-enabled yes cluster-config-file nodes-${port}.conf #redis啟動後這個nodes-port.conf會自動生成
開啟命令 redis-server redis-7000.conf / redis-server redis-7001.conf /...,
此時開啟的節點都是相互孤立的沒有任何通信,
查看當前狀態127.0.0.1:7000> cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:1 cluster_size:0 cluster_current_epoch:0 cluster_my_epoch:0 cluster_stats_messages_sent:0 cluster_stats_messages_received:0
顯示當前是cluster狀態
-
- meet(節點的握手)
redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001 [root@xx cluster]# redis-cli -p 7000 cluster meet 47.x.x.16 7001 OK [root@xx cluster]# redis-cli -p 7000 cluster nodes 862e370d2342cf6bf883421003846e171770234e :7000@17000 myself,master - 0 0 0 connected 886b6e6c29f985f7f85acb1bf548d0937918eca3 4.x.x.6:7001@17001 handshake - 0 0 0 connected redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7002 redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7003 redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7004 redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7005
這地方註意啊,手動配置的時候有個巨坑(坑了我1天):在redis cluster架構中,每個redis要開發兩個埠,比如一個是6379,那麼另一個就是加10000之後的埠號,比如16379。16379埠是用來進行節點間通信的,也就是cluster bus集群匯流排,cluster bus的通信用來進行故障檢測、配置更新、故障轉移授權等操作。
-
- 指派槽
redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0...5461} redis-cli -h 127.0.0.1 -p 7001 cluster addslots {5462...10922} redis-cli -h 127.0.0.1 -p 7002 cluster addslots {10923...16383}
-
- 主從關係的分配
redis-cli -h 127.0.0.1 -p 7003 cluster replicate ${node-id-7000} redis-cli -h 127.0.0.1 -p 7004 cluster replicate ${node-id-7001} redis-cli -h 127.0.0.1 -p 7005 cluster replicate ${node-id-7002}
-
常用命令
cluster notes 查看集群的node幾點情況 cluster slots 查看slots的分佈 cluster info 查看當前集群的狀態,這個命令可以看到集群是否屬於可用狀態 redis-cli -c -p x 註意加-c,表示集群
開始的一串字元是nodeid
, 整行表示的意識是我的nodeId是多少,哪個ip的那個埠,我是主還是從,是從的話從的誰(誰的nodeId),是主的話還能看到slots的分佈情況
-
- 官方工具安裝
- redis-trib.rb實現對redis集群的自動化安裝
- 原生安裝(模擬3主3從)
- 集群伸縮
- 客戶端路由
- 集群原理
- 集群是怎麼通信的:16379 埠號是用來進行節點間通信的,也就是 cluster bus 的東西,cluster bus 的通信,用來進行故障檢測、配置更新、故障轉移授權。cluster bus 用了另外一種二進位的協議,gossip 協議,用於節點間進行高效的數據交換,占用更少的網路帶寬和處理時間。
- 開發運維常見問題
Redis事務
-
Redis 事務可以一次執行多個命令, 並且帶有以下三個重要的保證:
- 批量操作在發送 EXEC 命令前被放入隊列緩存。
- 收到 EXEC 命令後進入事務執行,事務中任意命令執行失敗,其餘的命令依然被執行。
- 在事務執行過程,其他客戶端提交的命令請求不會插入到事務執行命令序列中。
-
Redis事務從開始到執行會經歷以下三個階段:開始事務 -> 命令入隊 -> 執行事務。單個 Redis 命令的執行是原子性的,但 Redis 沒有在事務上增加任何維持原子性的機制,所以 Redis 事務的執行並不是原子性的。事務可以理解為一個打包的批量執行腳本,但批量指令並非原子化的操作,中間某條指令的失敗不會導致前面已做指令的回滾,也不會造成後續的指令不做。這是官網上的說明 From redis docs on transactions: It's important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.
-
Redis 通過監聽一個 TCP 埠或者 Unix socket 的方式來接收來自客戶端的連接,當一個連接建立後,Redis 內部會進行以下一些操作:
- 首先,客戶端 socket 會被設置為非阻塞模式,因為 Redis 在網路事件處理上採用的是非阻塞多路復用模型。
- 然後為這個 socket 設置 TCP_NODELAY 屬性,禁用 Nagle 演算法
- 然後創建一個可讀的文件事件用於監聽這個客戶端 socket 的數據發送
-
Redis 管道技術可以在服務端未響應時,客戶端可以繼續向服務端發送請求,並最終一次性讀取所有服務端的響應。管道技術最顯著的優勢是提高了 redis 服務的性能。
-
Redis 分區
- 分區是分割數據到多個Redis實例的處理過程,因此每個實例只保存key的一個子集。
- 分區的優勢:
- 通過利用多台電腦記憶體的和值,允許我們構造更大的資料庫。
- 通過多核和多台電腦,允許我們擴展計算能力;通過多台電腦和網路適配器,允許我們擴展網路帶寬。
- 分區的不足:
- 涉及多個key的操作通常是不被支持的。舉例來說,當兩個set映射到不同的redis實例上時,你就不能對這兩個set執行交集操作。
- 涉及多個key的redis事務不能使用。
- 當使用分區時,數據處理較為複雜,比如你需要處理多個rdb/aof文件,並且從多個實例和主機備份持久化文件。
- 增加或刪除容量也比較複雜。redis集群大多數支持在運行時增加、刪除節點的透明數據平衡的能力,但是類似於客戶端分區、代理等其他系統則不支持這項特性。然而,一種叫做presharding的技術對此是有幫助的。
- 分區類型:Redis 有兩種類型分區。 假設有4個Redis實例 R0,R1,R2,R3,和類似user:1,user:2這樣的表示用戶的多個key,對既定的key有多種不同方式來選擇這個key存放在哪個實例中。也就是說,有不同的系統來映射某個key到某個Redis服務。
- 範圍分區
- 最簡單的分區方式是按範圍分區,就是映射一定範圍的對象到特定的Redis實例。比如,ID從0到10000的用戶會保存到實例R0,ID從10001到 20000的用戶會保存到R1,以此類推。這種方式是可行的,並且在實際中使用,不足就是要有一個區間範圍到實例的映射表。這個表要被管理,同時還需要各 種對象的映射表,通常對Redis來說並非是好的方法。
- 哈希分區
- 另外一種分區方法是hash分區。這對任何key都適用,也無需是object_name:這種形式,像下麵描述的一樣簡單:用一個hash函數將key轉換為一個數字,比如使用crc32 hash函數。對key foobar執行crc32(foobar)會輸出類似93024922的整數。對這個整數取模,將其轉化為0-3之間的數字,就可以將這個整數映射到4個Redis實例中的一個了。93024922 % 4 = 2,就是說key foobar應該被存到R2實例中。註意:取模操作是取除的餘數,通常在多種編程語言中用%操作符實現。【當分區較多或發生變化的時候需要處理一些額外的情況】
- 範圍分區
其他
- Redis設置port為6379的原因
I/O多路復用技術(multiplexing)
關於I/O多路復用(又被稱為“事件驅動”),首先要理解的是,操作系統為你提供了一個功能,當你的某個socket可讀或者可寫的時候,它可以給你一個通知。這樣當配合非阻塞的socket使用時,只有當系統通知我哪個描述符可讀了,我才去執行read操作,可以保證每次read都能讀到有效數據而不做純返回-1和EAGAIN的無用功。寫操作類似。操作系統的這個功能通過select/poll/epoll/kqueue之類的系統調用函數來使用,這些函數都可以同時監視多個描述符的讀寫就緒狀況,這樣,多個描述符的I/O操作都能在一個線程內併發交替地順序完成,這就叫I/O多路復用,這裡的“復用”指的是復用同一個線程。
下麵舉一個例子,模擬一個tcp伺服器處理30個客戶socket。假設你是一個老師,讓30個學生解答一道題目,然後檢查學生做的是否正確,你有下麵幾個選擇:1. 第一種選擇:按順序逐個檢查,先檢查A,然後是B,之後是C、D。。。這中間如果有一個學生卡住,全班都會被耽誤。這種模式就好比,你用迴圈挨個處理socket,根本不具有併發能力。2. 第二種選擇:你創建30個分身,每個分身檢查一個學生的答案是否正確。 這種類似於為每一個用戶創建一個進程或者線程處理連接。3. 第三種選擇,你站在講臺上等,誰解答完誰舉手。這時C、D舉手,表示他們解答問題完畢,你下去依次檢查C、D的答案,然後繼續回到講臺上等。此時E、A又舉手,然後去處理E和A。。。 這種就是IO復用模型,Linux下的select、poll和epoll就是乾這個的。將用戶socket對應的fd註冊進epoll,然後epoll幫你監聽哪些socket上有消息到達,這樣就避免了大量的無用操作。此時的socket應該採用非阻塞模式。這樣,整個過程只在調用select、poll、epoll這些調用的時候才會阻塞,收發客戶消息是不會阻塞的,整個進程或者線程就被充分利用起來,這就是事件驅動,所謂的reactor模式。
什麼時候適合用緩存
- 數據訪問頻率
- 訪問頻率高,適合用緩存,效果好
- 訪問頻率低,不建議使用,效果不佳
- 數據讀寫比例
- 讀多寫少,適合緩存,效果好
- 讀少寫多,不建議使用,效果不佳
- 數據一致性
- 一致性要求低,適合緩存,效果好
- 一致性要求高,不建議緩存,效果不佳
Redis中的緩存穿透、緩存雪崩、緩存擊穿
- 緩存穿透
緩存穿透,是指查詢一個資料庫一定不存在的數據。正常的使用緩存流程大致是,數據查詢先進行緩存查詢,如果key不存在或者key已經過期,再對資料庫進行查詢,並把查詢到的對象,放進緩存。如果資料庫查詢對象為空,則不放進緩存。
- 參數傳入對象主鍵ID
- 根據key從緩存中獲取對象
- 如果對象不為空,直接返回
- 如果對象為空,進行資料庫查詢
- 如果從資料庫查詢出的對象不為空,則放入緩存(設定過期時間)
想象一下這個情況,如果傳入的參數為-1,會是怎麼樣?這個-1,就是一定不存在的對象。就會每次都去查詢資料庫,而每次查詢都是空,每次又都不會進行緩存。假如有惡意攻擊,就可以利用這個漏洞,對資料庫造成壓力,甚至壓垮資料庫。即便是採用UUID,也是很容易找到一個不存在的KEY,進行攻擊。
小編在工作中,會採用緩存空值的方式,也就是【代碼流程】中第5步,如果從資料庫查詢的對象為空,也放入緩存,只是設定的緩存過期時間較短,比如設置為60秒。
redisTemplate.opsForValue().set(String.valueOf(goodsId), null, 60, TimeUnit.SECONDS); // 設置過期時間
解決方案
-
介面層增加校驗,如用戶鑒權校驗,id做基礎校驗,id<=0的直接攔截;
-
從緩存取不到的數據,在資料庫中也沒有取到,這時也可以將key-value對寫為key-null,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。這樣可以防止攻擊用戶反覆用同一個id暴力攻擊
-
緩存雪崩
緩存雪崩,是指在某一個時間段,緩存集中過期失效。
產生雪崩的原因之一,比如在寫本文的時候,馬上就要到雙十二零點,很快就會迎來一波搶購,這波商品時間比較集中的放入了緩存,假設緩存一個小時。那麼到了凌晨一點鐘的時候,這批商品的緩存就都過期了。而對這批商品的訪問查詢,都落到了資料庫上,對於資料庫而言,就會產生周期性的壓力波峰。
小編在做電商項目的時候,一般是採取不同分類商品,緩存不同周期。在同一分類中的商品,加上一個隨機因數。這樣能儘可能分散緩存過期時間,而且,熱門類目的商品緩存時間長一些,冷門類目的商品緩存時間短一些,也能節省緩存服務的資源。
其實集中過期,倒不是非常致命,比較致命的緩存雪崩,是緩存伺服器某個節點宕機或斷網。因為自然形成的緩存雪崩,一定是在某個時間段集中創建緩存,那麼那個時候資料庫能頂住壓力,這個時候,資料庫也是可以頂住壓力的。無非就是對資料庫產生周期性的壓力而已。而緩存服務節點的宕機,對資料庫伺服器造成的壓力是不可預知的,很有可能瞬間就把資料庫壓垮。
解決方案:
-
緩存數據的過期時間設置隨機,防止同一時間大量數據過期現象發生。
-
如果緩存資料庫是分散式部署,將熱點數據均勻分佈在不同搞得緩存資料庫中。
-
設置熱點數據永遠不過期。
-
redis 持久化,一旦重啟,自動從磁碟上載入數據,快速恢復緩存數據。
-
緩存擊穿
緩存擊穿,是指存在hot key,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就穿破緩存,直接請求資料庫,就像在一個屏障上鑿開了一個洞。
小編在做電商項目的時候,把這貨就成為“爆款”。
其實,大多數情況下這種爆款很難對資料庫伺服器造成壓垮性的壓力。達到這個級別的公司沒有幾家的。所以,務實主義的小編,對主打商品都是早早的做好了準備,讓緩存永不過期。即便某些商品自己發酵成了爆款,也是直接設為永不過期就好了。
解決方案
- 設置熱點數據永遠不過期。
- 加互斥鎖,互斥鎖參考代碼如下:
說明:
1)緩存中有數據,直接走上述代碼13行後就返回結果了
2)緩存中沒有數據,第1個進入的線程,獲取鎖並從資料庫去取數據,沒釋放鎖之前,其他並行進入的線程會等待100ms,再重新去緩存取數據。這樣就防止都去資料庫重覆取數據,重覆往緩存中更新數據情況出現。
3)當然這是簡化處理,理論上如果能根據key值加鎖就更好了,就是線程A從資料庫取key1的數據並不妨礙線程B取key2的數據,上面代碼明顯做不到這點。
百問
Redis番外篇
Redis 最開始的設計可能就是想做一個緩存來用。但是分散式環境複雜,暴露的問題可能比較多,所以 Redis 就要做集群。做集群後,可能和 Memcahed 效果類似了,我們要超越它,所以可能就有了多數據類型的存儲結構。光做緩存,如何已宕機數據就丟失了。我們的口號是超越 Memcahed,所以我們要支持數據持久化。於是可能就有了 AOF 和 RDB,就可以當資料庫來用來。這樣 Redis 的高效可靠的設計,所以它又可以用來做消息中間件。這就是 Redis 的三大特點,可以用來做:緩存、資料庫和消息中間件。
再來說說,Redis 如何設計成但進程單線程的?
根據官方的測試結果《How fast is Redis?》來看,在操作記憶體的情況下,CPU 並不能起到決定性的作用,反而可能帶來一些其他問題。比如鎖,CPU 切換帶來的性能開銷等。這一點我們可以根據官方的測試報告,提供的數據來證明。而且官方提供的數據是可以達到100000+的QPS(每秒內查詢次數),這個數據並不比採用單進程多線程 Memcached 差!所以在基於記憶體的操作,CPU不是 Redis 瓶頸的情況下,官方採用來單進程單線程的設計。
Redis單線程為什麼這麼快?
快的原因有主要三點:
- 純記憶體操作:Redis是基於記憶體的,所有的命令都在記憶體中完成,記憶體的響應速度相比硬碟是非常快的,記憶體的響應時間大約是100納秒,Redis官方給出的OPS是10W
- 編程語言:Redis採用C語言編寫,不依賴第三方類庫,執行速度快
- 線程模型:Redis使用單線程操作,避免了線程的切換和競態消耗
- 採用了非阻塞IO多路復用機制:多路I/O復用模型是利用 select、poll、epoll 可以同時監察多個流的 I/O 事件的能力,在空閑的時候,會把當前線程阻塞掉,當有一個或多個流有 I/O 事件時,就從阻塞態中喚醒,於是程式就會輪詢一遍所有的流(epoll 是只輪詢那些真正發出了事件的流),並且只依次順序的處理就緒的流,這種做法就避免了大量的無用操作。這裡“多路”指的是多個網路連接,“復用”指的是復用同一個線程。加上Redis自身的事件處理模型將epoll中的連接,讀寫,關閉都轉換為了事件,不在I/O上浪費過多的時間
- 由於是單線程,所以就存在一個順序讀寫的問題,順序讀寫比隨機讀寫的速度快。
- Redis的數據結構是經過專門的研究和設計的,所以操作起來簡單且快。
最後,再說一點,Redis 是單進程和單線程的設計,並不是說它不能多進程多線程。比如備份時會 fork 一個新進程來操作;再比如基於 COW 原理的 RDB 操作就是多線程的。
Redis如何處理過期數據?Slave不能處理數據,那數據過期了怎麼辦?
1主2從的模式中,當master掛掉之後怎麼辦?
這種典型的模式就不上圖了,master讀寫,slave1/2只讀,當master掛掉之後,redis服務不可用,需要立馬手動處理。兩種處理方式,第一種是把master重新啟動起來,不用改變現有的主從結構,缺點是什麼呢,master重新啟動並完成RDB/AOF的恢復是個耗時的過程,另外會造成slave1/2發生全量複製;第二種就是重新選舉新的master,具體怎麼做呢?選折其中一個slave,執行命令 slave no one來解除自己是從伺服器的身份,使其稱為一個master,註意的點是這個slave要改成讀寫模式;連到另一個slave,執行slave new master,讓它去找master。整個過程是一個手動的過程,Redis Sentinel就是這樣一個功能,自動完成切換,帥的一比。
Redis分散式鎖你真的會用嗎?
https://www.xttblog.com/?p=4598
Redis使用註意的點
- 由於是單線程模型,因此一次只運行一條命令
- 拒絕長(慢)命令:keys, flushall,flushdb, slow lua script, mutil/exec, operate big value(collection)
Redis Sentinel和Redis Cluster的區別
- sentinel
實現高可用,但是沒有分區
監控,能持續監控Redis的主從實例是否正常工作;
通知,當被監控的Redis實例出問題時,能通過API通知系統管理員或其他程式;
自動故障恢復,如果主實例無法正常工作,Sentinel將啟動故障恢復機制把一個從實例提升為主實例,其他的從實例將會被重新配置到新的主實例,且應用程式會得到一個更換新地址的通知。
Redis Sentinel是一個分散式系統,可以部署多個Sentinel實例來監控同一組Redis實例,它們通過Gossip協議來確定一個主實例宕機,通過Agreement協議來執行故障恢復和配置變更。 - Redis Cluster特點如下:
- 所有的節點相互連接;
- 集群消息通信通過集群匯流排通信,,集群匯流排埠大小為客戶端服務埠+10000,這個10000是固定值;
- 節點與節點之間通過二進位協議進行通信;
- 客戶端和集群節點之間通信和通常一樣,通過文本協議進行;
- 集群節點不會代理查詢;
redis3.0以後推出了cluster,具有Sentinel的監控和自動Failover能力,同時提供一種官方的分區解決方案
Redis分散式鎖/Redis的setnx命令如何設置key的失效時間(同時操作setnx和expire
Redis的setnx命令是當key不存在時設置key,但setnx不能同時完成expire設置失效時長,不能保證setnx和expire的原子性。我們可以使用set命令完成setnx和expire的操作,並且這種操作是原子操作。
下麵是set命令的可選項:
set key value [EX seconds] [PX milliseconds] [NX|XX]
EX seconds:設置失效時長,單位秒
PX milliseconds:設置失效時長,單位毫秒
NX:key不存在時設置value,成功返回OK,失敗返回(nil)
XX:key存在時設置value,成功返回OK,失敗返回(nil)
案例:設置name=p7+,失效時長100s,不存在時設置
1.1.1.1:6379> set name p7+ ex 100 nx
假如Redis裡面有1億個key,其中有10w個key是以某個固定的已知的首碼開頭的,如何將它們全部找出來?
可以使用keys [pattern]來列舉出來,由於Redis是單線程的,在使用keys命令的時候會導致線程阻塞一段時間,我們也可以使用scan指令以無阻塞的方式取出來,但會有一定重覆的概率,客戶端去重就可以了。如果是主從模式的話,可以在從伺服器執行keys命令,儘量不影響現有業務。
使用過Redis做非同步隊列麽,你是怎麼用的?
Redis如何實現延時隊列?
RDB的原理是什麼?
你給出兩個辭彙就可以了,fork和cow。fork是指redis通過創建子進程來進行RDB操作,cow指的是copy on write,子進程創建後,父子進程共用數據段,父進程繼續提供讀寫服務,寫髒的頁面數據會逐漸和子進程分離開來。
Pipeline有什麼好處,為什麼要用pipeline?
可以將多次IO往返的時間縮減為一次,前提是pipeline執行的指令之間沒有因果相關性。使用redis-benchmark進行壓測的時候可以發現影響redis的QPS峰值的一個重要因素是pipeline批次指令的數目。
是否使用過Redis集群,集群的高可用怎麼保證,集群的原理是什麼?
- Redis Sentinal 著眼於高可用,在master宕機時會自動將slave提升為master,繼續提供服務。
- Redis Cluster 著眼於擴展性,在單個redis記憶體不足時,使用Cluster進行分片存儲。
Redis集群是如何通信的?
Redis集群採用gossip(流言)協議來通信,Gossip協議的主要職責就是信息交換。信息交換的載體就是節點彼此發送的Gossip消息,常用的Gossip消息可分為:ping消息、pong消息、meet消息、fail消息。集群中的每個節點都會單獨開闢一個TCP通道,用於節點之間的彼此通信,通信埠是在基礎埠的基礎上加上1萬,每個節點在固定周期內通過特定規則選擇幾個節點發送ping消息,接收到ping消息的節點用pong消息作為響應。ping消息發送封裝了自身節點和部分其他節點的狀態數據,pong消息:當接收到ping、meet消息時,作為響應消息回覆給發送方確認消息正常通信。pong消息內部封裝了自身狀態數據。節點也可以向集群內廣播自身的pong消息來通知整個集群對自身狀態進行更新,一段時間後整個集群達到了狀態的一致性。
PS:定時任務預設每秒執行10次,每秒會隨機選取5個節點找出最久沒有通信的節點發送ping消息,用於保證Gossip信息交換的隨機性
Reids Key是如何定址的