Redis从入门到入土
Redis從入門到入土
- 概述
- 應用場景
- redis相關知識
- 常用五大數據類型
- Redis-key
- String
- List
- list數據結構
- Set
- Set數據結構
- Zset
- 命令
- Zset數據結構
- Redis的發布和訂閱
- 概述
- 發布和訂閱
- Redis新數據類型
- Bitmaps
- HyperLogLog
- Geospatial
- Redis事務處理
- Multi、Exec、discard
- 事務的錯誤處理
- 為什么要做成事務
- 事務沖突的問題
- 悲觀鎖
- 樂觀鎖
- Redis事務三特性
- Redis持久化之RDB
- RDB介紹
- 備份是如何執行的
- Fork
- RDB持久化流程
- RDB配置信息
- RDB優點
- RDB缺點
- 停止RDB
- RDB總結
- Redis持久化之AOF
- AOF介紹
- AOF持久化流程
- AOF模式
- AOF默認不開啟
- AOF和RDB同時開啟,redis會執行誰
- AOF啟動/修復/恢復
- AOF優點
- AOF缺點
- AOF總結
- AOF和RDB的選擇使用
- 官方建議
- Redis主從復制
- 介紹
- 作用
- 主從復制操作
- Redis哨兵機制
- 主從復制的缺陷
- 概念
- 作用
- 多哨兵模式
- 概念
- 故障切換的過程
- 操作步驟
- 故障恢復
- Redis集群
- 問題
- 什么是集群
- 部署redis集群
- slots
- Redis集群的不足
- Redis應用問題解決
- 緩存穿透
- 緩存擊穿
- 緩存雪崩
概述
Redis是一個開源的key-value存儲系統。
和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。
這些數據類型都支持push/pop、add/remove及取交集并集和差集及更豐富的操作,而且這些操作都是原子性的。
在此基礎上,Redis支持各種不同方式的排序。
與memcached一樣,為了保證效率,數據都是緩存在內存中。
區別的是Redis會周期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件。
并且在此基礎上實現了master-slave(主從)同步。
應用場景
1.配合關系型數據庫做高速緩存
? 高頻次,熱門訪問的數據,降低數據庫IO
? 分布式架構,做session共享
2.多樣的數據結構儲存持久化數據
redis相關知識
端口6379
默認16個數據庫,類似數組下標從零開始,初始默認使用0號庫
使用命令 select 來切換數據庫。如: select 8
統一密碼管理,所有庫同樣密碼。
dbsize查看當前數據庫的key的數量
flushdb清空當前庫
flushall通殺全部庫
127.0.0.1:6379> config get databases #命令行查看數據庫數量 1) "databases" 2) "16" 127.0.0.1:6379> select 0 #切換數據庫 db 0 (integer) 0 127.0.0.1:6379> dbsize #查看數據庫大小 OKRedis是單線程+多路IO復用技術
多路復用是指使用一個線程來檢查多個文件描述符(Socket)的就緒狀態,比如調用select和poll函數,傳入多個文件描述符,如果有一個文件描述符就緒,則返回,否則阻塞直到超時。得到就緒狀態后進行真正的操作可以在同一個線程里執行,也可以啟動線程執行(比如使用線程池)
常用五大數據類型
Redis-key
127.0.0.1:6379> set name liutianzhu #set key OK 127.0.0.1:6379> get name #獲取當前key的值 "liutianzhu" 127.0.0.1:6379> keys * #查看所有的key 1) "name" 127.0.0.1:6379> set age 20 OK 127.0.0.1:6379> keys * 1) "name" 2) "age" 127.0.0.1:6379> Exists name #判斷當前的key是否存在 存在則返回1,不存在則返回0 (integer) 1 127.0.0.1:6379> Exists name1 (integer) 0 127.0.0.1:6379> move name 1 #將該鍵從當前庫移動到另外一個數據庫 (integer) 1 127.0.0.1:6379> expire name 20 #設置key的過期時間,單位是秒 (integer) 1 127.0.0.1:6379> ttl name #查看當前key的剩余時間 (integer) 14 127.0.0.1:6379> ttl name (integer) (nil) 127.0.0.1:6379> type name #查看當前key類型 string 127.0.0.1:6379> del age #刪除鍵值對 (integer) 1 #刪除個數關于ttl命令
Redis的key,通過TTL命令返回key的過期時間,一般來說有3種:
關于重命名rename 和 renamenx
127.0.0.1:6379> rename name stuname #修改key的名稱 OK 127.0.0.1:6379> renamenx stuname name #僅當 newkey 不存在時,將 key 改名為 newkey (integer) 1更多詳細命令請看官方文檔:https://www.redis.net.cn/order/
String
127.0.0.1:6379> set key1 v1 #設置值 OK 127.0.0.1:6379> get key1 #獲得值 "v1" 127.0.0.1:6379> exists key1 #判斷某一個key是否存在 (integer) 1 127.0.0.1:6379> append key1 "hello" #追加字符串,如果當前key不存在,就相當于setkey (integer) 7 127.0.0.1:6379> get key1 "v1hello" 127.0.0.1:6379> strlen key1 #獲取字符串長度 (integer) 7 127.0.0.1:6379> append key1 ",liuzhicheng" (integer) 19 127.0.0.1:6379> get key1 "v1hello,liuzhicheng" 127.0.0.1:6379> strlen key1 (integer) 19 127.0.0.1:6379> setnx phone 13930618486 #只有在key不存在時,設置key的值 (integer) 1 127.0.0.1:6379> set k2 100 OK 127.0.0.1:6379> incr k2 #將 key 中儲存的數字值增1,只能對數字值操作,如果為空,新增值為1 (integer) 101 127.0.0.1:6379> decr k2 #將 key 中儲存的數字值減1,只能對數字值操作,如果為空,新增值為-1 (integer) 100 127.0.0.1:6379> incrby k2 20 #將 key 中儲存的數字值增。自定義步長。 (integer) 121 127.0.0.1:6379> decrby k2 20 #將 key 中儲存的數字值減。自定義步長。 (integer) 101 127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 #同時設置一個或多個鍵值對 OK 127.0.0.1:6379> keys * 1) "k1" 2) "k2" 3) "k3" 127.0.0.1:6379> mget k1 k2 k3 #同時獲取一個或多個值 1) "v1" 2) "v2" 3) "v3" 127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 OK 127.0.0.1:6379> keys * 1) "k1" 2) "k2" 3) "k3" 127.0.0.1:6379> mget k1 k2 k3 1) "v1" 2) "v2" 3) "v3" #同時設置一個或多個 key-value 對,當且僅當所有給定 key 都不存在。 127.0.0.1:6379> msetnx a1 v1 a2 v2 (integer) 1 127.0.0.1:6379> set name lucymary OK 127.0.0.1:6379> getrange name 0 2 #獲得值的范圍,類似java中的substring,前包,后包 "luc" 127.0.0.1:6379> set name 10 20 #設置鍵值的同時,設置過期時間,單位秒 OK 127.0.0.1:6379> ttl name (integer) -2 127.0.0.1:6379> getset name 50 #以新換舊,設置了新值同時獲得舊值 "30"List
lpush/rpush <key><value1><value2><value3> .... 從左邊/右邊插入一個或多個值。 lpop/rpop <key>從左邊/右邊吐出一個值。值在鍵在,值光鍵亡。 rpoplpush <key1><key2>從<key1>列表右邊吐出一個值,插到<key2>列表左邊。 lrange <key><start><stop> 按照索引下標獲得元素(從左到右)lrange mylist 0 -1 0左邊第一個,-1右邊第一個,(0-1表示獲取所有) lindex <key><index>按照索引下標獲得元素(從左到右) llen <key>獲得列表長度 linsert <key> before <value><newvalue>在<value>的后面插入<newvalue>插入值 lrem <key><n><value>從左邊刪除n個value(從左到右) lset<key><index><value>將列表key下標為index的值替換成valuelist數據結構
首先在列表元素較少的情況下會使用一塊連續的內存存儲,這個結構是ziplist,也即是壓縮列表。
它將所有的元素緊挨著一起存儲,分配的是一塊連續的內存。
當數據量比較多的時候才會改成quicklist。
因為普通的鏈表需要的附加指針空間太大,會比較浪費空間。比如這個列表里存的只是int類型的數據,結構上還需要兩個額外的指針prev和next。
Redis將鏈表和ziplist結合起來組成了quicklist。也就是將多個ziplist使用雙向指針串起來使用。這樣既滿足了快速的插入刪除性能,又不會出現太大的空間冗余。
Set
Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在于set是可以自動排重的,當你需要存儲一個列表數據,又不希望出現重復數據時,set是一個很好的選擇,并且set提供了判斷某個成員是否在一個set集合內的重要接口,這個也是list所不能提供的。
Redis的Set是string類型的無序集合。它底層其實是一個value為null的hash表,所以添加,刪除,查找的復雜度都是O(1)
一個算法,隨著數據的增加,執行時間的長短,如果是O(1),數據增加,查找數據的時間不變
sadd <key><value1><value2> ..... 將一個或多個 member 元素加入到集合 key 中,已經存在的 member 元素將被忽略smembers <key>取出該集合的所有值。sismember <key><value>判斷集合<key>是否為含有該<value>值,有1,沒有0scard<key>返回該集合的元素個數。srem <key><value1><value2> .... 刪除集合中的某個元素。spop <key>***\*隨機從該集合中吐出一個值。\****srandmember <key><n>隨機從該集合中取出n個值。不會從集合中刪除 。smove <source><destination>value把集合中一個值從一個集合移動到另一個集合sinter <key1><key2>返回兩個集合的交集元素。sunion <key1><key2>返回兩個集合的并集元素。sdiff <key1><key2>返回兩個集合的***\*差集\****元素(key1中的,不包含key2中的)Set數據結構
Set數據結構是dict字典,字典是用哈希表實現的。
Java中HashSet的內部實現使用的是HashMap,只不過所有的value都指向同一個對象。Redis的set結構也是一樣,它的內部也使用hash結構,所有的value都指向同一個內部值
Redis hash 是一個鍵值對集合。
Redis hash是一個string類型的field和value的映射表,hash特別適合用于存儲對象。
類似Java里面的Map<String,Object>
用戶ID為查找的key,存儲的value用戶對象包含姓名,年齡,生日等信息,如果用普通的key/value結構來存儲
主要有以下2種存儲方式:
每次修改用戶的某個屬性需要,先反序列化改好后再序列化回去。開銷較大。 |
用戶ID數據冗余
通過key(用戶ID) + field(屬性標簽就可以操作對應屬性數據了,既不需要重復存儲數據,也不會帶來序列化和并發修改控制的問題
Zset
Redis有序集合zset與普通集合set非常相似,是一個沒有重復元素的字符串集合。
不同之處是有序集合的每個成員都關聯了一個評分(score),這個評分(score)被用來按照從最低分到最高分的方式排序集合中的成員。集合的成員是唯一的,但是評分可以是重復了 。
因為元素是有序的, 所以你也可以很快的根據評分(score)或者次序(position)來獲取一個范圍的元素。
訪問有序集合的中間元素也是非常快的,因此你能夠使用有序集合作為一個沒有重復成員的智能列表。
命令
zadd <key><score1><value1><score2><value2>…將一個或多個 member 元素及其 score 值加入到有序集 key 當中。***\*zrange <key><start><stop> [WITHSCORES]\**** 返回有序集 key 中,下標在<start><stop>之間的元素帶WITHSCORES,可以讓分數一起和值返回到結果集。zrangebyscore key minmax [withscores] [limit offset count]返回有序集 key 中,所有 score 值介于 min 和 max 之間(包括等于 min 或 max )的成員。有序集成員按 score 值遞增(從小到大)次序排列。 zrevrangebyscore key maxmin [withscores] [limit offset count] 同上,改為從大到小排列。 zincrby <key><increment><value> 為元素的score加上增量zrem <key><value>刪除該集合下,指定值的元素 zcount <key><min><max>統計該集合,分數區間內的元素個數 zrank <key><value>返回該值在集合中的排名,從0開始。Zset數據結構
SortedSet(zset)是Redis提供的一個非常特別的數據結構,一方面它等價于Java的數據結構Map<String, Double>,可以給每一個元素value賦予一個權重score,另一方面它又類似于TreeSet,內部的元素會按照權重score進行排序,可以得到每個元素的名次,還可以通過score的范圍來獲取元素的列表。
zset底層使用了兩個數據結構
(1)hash,hash的作用就是關聯元素value和權重score,保障元素value的唯一性,可以通過元素value找到相應的score值。
(2)跳躍表,跳躍表的目的在于給元素value排序,根據score的范圍獲取元素列表。
Redis的發布和訂閱
概述
Redis 發布訂閱 (pub/sub) 是一種消息通信模式:發送者 (pub) 發送消息,訂閱者 (sub) 接收消息。
Redis 客戶端可以訂閱任意數量的頻道。
發布和訂閱
1.客戶端可以 訂閱頻道
2.當給這個頻道發布消息后,消息就會發送給訂閱的客戶端
發布訂閱命令實現
1).
? 打開一個客戶端訂閱channel1
127.0.0.1:6379> subscribe channel1 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "channel1" 3) (integer) 1 1) "message" 2) "channel1"2).打開另一個客戶端
127.0.0.1:6379> publish channel1 hello (integer) 13).打開第一個客戶端可以看到發送的消息
127.0.0.1:6379> subscribe channel1 Reading messages... (press Ctrl-C to quit) 1) "subscribe" 2) "channel1" 3) (integer) 1 1) "message" 2) "channel1" 3) "hello"注:發布的消息沒有持久化。如果在訂閱的客戶端收不到hello,只能收到訂閱后發布的消息
Redis新數據類型
Bitmaps
現代計算機用二進制(位) 作為信息的基礎單位, 1個字節等于8位, 例如“abc”字符串是由3個字節組成, 但實際在計算機存儲時將其用二進制表示, “abc”分別對應的ASCII碼分別是97、 98、 99, 對應的二進制分別是01100001、 01100010和01100011,如下圖
合理地使用操作位能夠有效地提高內存使用率和開發效率。
Redis提供了Bitmaps這個“數據類型”可以實現對位的操作:
(1) Bitmaps本身不是一種數據類型, 實際上它就是字符串(key-value) , 但是它可以對字符串的位進行操作。
(2) Bitmaps單獨提供了一套命令, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一個以位為單位的數組, 數組的每個單元只能存儲0和1, 數組的下標在Bitmaps中叫做偏移量。
命令
1.setbit
setbit<key><offset><value> 設置Bitmaps中某個偏移量的值(0或1)*offset:偏移量從0開始2.getbit
getbit<key><offset>獲取Bitmaps中某個偏移量的值3.bitcount
統計字符串被設置為1的bit數。一般情況下,給定的整個字符串都會被進行計數,通過指定額外的 start 或 end 參數,可以讓計數只在特定的位上進行。start 和 end 參數的設置,都可以使用負數值:比如 -1 表示最后一個位,而 -2 表示倒數第二個位,start、end 是指bit組的字節的下標數,二者皆包含。
bitcount<key>[start end] 統計字符串從start字節到end字節比特值為1的數量4.bitop
bitop and(or/not/xor) <destkey> [key…] bitop是一個復合操作, 它可以做多個Bitmaps的and(交集) 、 or(并集) 、 not(非) 、 xor(異或) 操作并將結果保存在destkey中。5.Bitmaps與set對比
假設網站有1億用戶, 每天獨立訪問的用戶有5千萬, 如果每天用集合類型和Bitmaps分別存儲活躍用戶可以得到表
? set和Bitmaps存儲一天活躍用戶對比
| 集合類型 | 64位 | 50000000 | 64位*50000000 = 400MB |
| Bitmaps | 1位 | 100000000 | 1位*100000000 = 12.5MB |
很明顯,這種情況下使用Bitmaps能節省很多的內存空間,尤其隨著時間推移節省的內存還是非常可觀的
? set和Bitmaps儲存獨立用戶空間對比
| 集合類型 | 400MB | 12GB | 144GB |
| Bitmaps | 12.5MB | 375MB | 4.5GB |
但Bitmaps并不是萬金油, 假如該網站每天的獨立訪問用戶很少, 例如只有10萬(大量的僵尸用戶) , 那么兩者的對比如下表所示, 很顯然, 這時候使用Bitmaps就不太合適了, 因為基本上大部分位都是0。
? set和Bitmaps存儲一天活躍用戶對比(獨立用戶比較少)
| 集合類型 | 64位 | 100000 | 64位*100000 = 800KB |
| Bitmaps | 1位 | 100000000 | 1位*100000000 = 12.5MB |
HyperLogLog
在工作當中,我們經常會遇到與統計相關的功能需求,比如統計網站PV(PageView頁面訪問量),可以使用Redis的incr、incrby輕松實現。
但像UV(UniqueVisitor,獨立訪客)、獨立IP數、搜索記錄數等需要去重和計數的問題如何解決?這種求集合中不重復元素個數的問題稱為基數問題。
解決基數問題有很多種方案:
(1)數據存儲在MySQL表中,使用distinct count計算不重復個數
(2)使用Redis提供的hash、set、bitmaps等數據結構來處理
以上的方案結果精確,但隨著數據不斷增加,導致占用空間越來越大,對于非常大的數據集是不切實際的。
能否能夠降低一定的精度來平衡存儲空間?Redis推出了HyperLogLog
Redis HyperLogLog 是用來做基數統計的算法,HyperLogLog 的優點是,在輸入元素的數量或者體積非常非常大時,計算基數所需的空間總是固定的、并且是很小的。
在 Redis 里面,每個 HyperLogLog 鍵只需要花費 12 KB 內存,就可以計算接近 2^64 個不同元素的基數。這和計算基數時,元素越多耗費內存就越多的集合形成鮮明對比。
但是,因為 HyperLogLog 只會根據輸入元素來計算基數,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣,返回輸入的各個元素。
什么是基數?
比如數據集 {1, 3, 5, 7, 5, 7, 8}, 那么這個數據集的基數集為 {1, 3, 5 ,7, 8}, 基數(不重復元素)為5。 基數估計就是在誤差可接受的范圍內,快速計算基數。
命令
1.pfadd
pfadd <key>< element> [element ...] 添加指定元素到 HyperLogLog 中2.pfcount
pfcount<key> [key ...] 計算HLL的近似基數,可以計算多個HLL3.pfmerge
pfmerge<destkey><sourcekey> [sourcekey ...] 將一個或多個HLL合并后的結果存儲在另一個HLL中Geospatial
redis 3.2 中增加了對GEO類型的支持。GEO,Geographic,地理信息的縮寫。該類型,就是元素的2維坐標,在地圖上就是經緯度。redis基于該類型,提供了經緯度設置,查詢,范圍查詢,距離查詢,經緯度Hash等常見操作。
命令
1.geoadd
geoadd<key>< longitude><latitude><member> [longitude latitude member...] 添加地理位置(經度,緯度,名稱)2.geopos
geopos <key><member> [member...] 獲得指定地區的坐標值3.geodist
geodist<key><member1><member2> [m|km|ft|mi ] 獲取兩個位置之間的直線距離4.georadius
georadius<key>< longitude><latitude>radius m|km|ft|mi 以給定的經緯度為中心,找出某一半徑內的元素Redis事務處理
Redis事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行。事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
Redis事務的主要作用就是串聯多個命令防止別的命令插隊。
Multi、Exec、discard
Multi 組隊階段 Exec 執行階段 discard 放棄從輸入Multi命令開始,輸入的命令都會依次進入命令隊列中,但不會執行,直到輸入Exec后,Redis會將之前的命令隊列中的命令依次執行。組隊的過程中可以通過discard來放棄組隊。
事務的錯誤處理
組隊中某個命令出現了報告錯誤,執行時整個的所有隊列都會被取消。
如果執行階段某個命令報出了錯誤,則只有報錯的命令不會被執行,而其他的命令都會執行,不會回滾。
為什么要做成事務
有很多有你的賬戶,同時去參加618搶購
事務沖突的問題
例子: 一個請求想給金額減8000 一個請求想給金額減5000 一個請求想給金額減1000悲觀鎖
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。樂觀鎖
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用于多讀的應用類型,這樣可以提高吞吐量。Redis就是利用這種check-and-set機制實現事務的。命令
WATCH key [key ...] 在執行multi之前,先執行watch key1 [key2],可以監視一個(或多個) key , 如果在事務執行之前這個(或這些) key 被其他命令所改動,那么事務將被打斷。 unwatch 取消 WATCH 命令對所有 key 的監視。 如果在執行 WATCH 命令之后,EXEC 命令或DISCARD 命令先被執行了的話,那么就不需要再執行UNWATCH 了。Redis事務三特性
- 單獨的隔離操作
- 事務中的所有命令都會序列化、按順序的執行/事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
- 沒有隔離級別的概念
- 隊列中的命令沒有提交之前都不會實際被執行
- 不保證原子性
- 事務中如果一條命令執行失敗,其他的命令仍然會被執行,沒有回滾
Redis持久化之RDB
Redis提供兩個不同形式的持久化方式
- RDB(Redis DataBase)
- AOF(Append Of File)
RDB介紹
在指定的時間間隔內將內存中的數據集快照寫入磁盤, 也就是行話講的Snapshot快照,它恢復時是將快照文件直接讀到內存里
備份是如何執行的
Redis會單獨創建(fork)一個子進程來進行持久化,會先將數據寫入到 一個臨時文件中,待持久化過程都結束了,再用這個臨時文件替換上次持久化好的文件。 整個過程中,主進程是不進行任何IO操作的,這就確保了極高的性能 如果需要進行大規模數據的恢復,且對于數據恢復的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺點是最后一次持久化后的數據可能丟失。
Fork
- Fork的作用是復制一個與當前進程一樣的進程。新進程的所有數據(變量、環境變量、程序計數器等) 數值都和原進程一致,但是是一個全新的進程,并作為原進程的子進程
- 在Linux程序中,fork()會產生一個和父進程完全相同的子進程,但子進程在此后多會exec系統調用,出于效率考慮,Linux中引入了“寫時復制技術”
- 一般情況父進程和子進程會共用同一段物理內存,只有進程空間的各段的內容要發生變化時,才會將父進程的內容復制一份給子進程。
RDB持久化流程
RDB配置信息
在redis.conf中配置文件名稱,默認為dump.rdb
rdb文件的保存路徑,也可以修改。默認為Redis啟動時命令行所在的目錄下
dir "/myredis/"配置觸發RDB快照:保持策略
配置文件中默認的快照設置
-
save和bgsave的區別
- save:save時只管保存,其它不管,全部阻塞。手動保存。不建議。
- bgsave:Redis會在后臺異步進行快照操作, 快照同時還可以響應客戶端請求。
可以通過lastsave 命令獲取最后一次成功執行快照的時間
-
flushall:執行flushall命令,也會產生dump.rdb文件,但里面是空的,無意義
-
Save:格式:save 秒鐘 寫操作次數
- RDB是整個內存的壓縮過的Snapshot,RDB的數據結構,可以配置復合的快照觸發條件,
默認是1分鐘內改了1萬次,或5分鐘內改了10次,或15分鐘內改了1次。
禁用
不設置save指令,或者給save傳入空字符串
- RDB是整個內存的壓縮過的Snapshot,RDB的數據結構,可以配置復合的快照觸發條件,
-
stop-writes-on-bgsave-error
-
? 當Redis無法寫入磁盤的話,直接關掉Redis的寫操作。推薦yes.
-
-
rdbcompression 壓縮文件
-
對于存儲到磁盤中的快照,可以設置是否進行壓縮存儲。如果是的話,redis會采用LZF算法進行壓縮。
如果你不想消耗CPU來進行壓縮的話,可以設置為關閉此功能。推薦yes.
-
rdbchecksum 檢查完整性
-
在存儲快照后,還可以讓redis使用CRC64算法來進行數據校驗,
但是這樣做會增加大約10%的性能消耗,如果希望獲取到最大的性能提升,可以關閉此功能
推薦yes.
-
rdb的備份
- 先通過config get dir 查詢rdb文件的目錄
- 將*.rdb的文件拷貝到別的地方
- rdb的恢復
- 關閉Redis
- 先把備份的文件拷貝到工作目錄下 cp dump2.rdb dump.rdb
- 啟動Redis, 備份數據會直接加載
RDB優點
-
適合大規模的數據恢復
-
對數據完整性和一致性要求不高更適合使用
-
節省磁盤空間
-
恢復速度快
RDB缺點
- Fork的時候,內存中的數據被克隆了一份,大致2倍的膨脹性需要考慮
- 雖然Redis在fork時使用了寫時拷貝技術,但是如果數據龐大時還是比較消耗性能。
- 在備份周期在一定間隔時間做一次備份,所以如果Redis意外down掉的話,就會丟失最后一次快照后的所有修改。
停止RDB
動態停止RDB:redis-cli config set save ""#save后給空值,表示禁用保存策略RDB總結
Redis持久化之AOF
AOF介紹
? 以日志的形式來記錄每個寫操作(增量保存),將Redis執行過的所有寫指令記錄下來(讀操作不記錄), 只許追加文件但不可以改寫文件,redis啟動之初會讀取該文件重新構建數據,換言之,redis 重啟的話就根據日志文件的內容將寫指令從前到后執行一次以完成數據的恢復工作。
AOF持久化流程
- 客戶端的請求寫命令會被append追加到AOF緩沖區內;
- AOF緩沖區根據AOF持久化策略[always,everysec,no]將操作sync同步到磁盤的AOF文件中;
- AOF文件大小超過重寫策略或手動重寫時,會對AOF文件rewrite重寫,壓縮AOF文件容量;
- Redis服務重啟時,會重新load加載AOF文件中的寫操作達到數據恢復的目的;
AOF模式
AOF默認不開啟
? 可以在redis.conf中配置文件名稱,默認為 appendonly.aof
? AOF文件的保存路徑,同RDB的路徑一致。
AOF和RDB同時開啟,redis會執行誰
? AOF和RDB同時開啟,系統默認取AOF的數據(數據不會存在丟失)
AOF啟動/修復/恢復
- AOF的備份機制和性能雖然和RDB不同, 但是備份和恢復的操作同RDB一樣,都是拷貝備份文件,需要恢復時再拷貝到Redis工作目錄下,啟動系統即加載。
- 正常恢復
- 修改默認的appendonly no,改為yes
- 將有數據的aof文件復制一份保存到對應目錄(查看目錄:config get dir)
- 異常恢復
- 修改默認的appendonly no,改為yes
- 如遇到AOF文件損壞,通過/usr/local/bin/redis-check-aof–fix appendonly.aof進行恢復
- 備份被寫壞的AOF文件
- 恢復:重啟redis,然后重新加載
AOF優點
- 備份機制更穩健,丟失數據概率更低
- 可讀的日志文本,通過操作AOF穩健,可以處理誤操作
AOF缺點
- 比起RDB占用更多的磁盤空間
- 恢復備份速度要慢
- 每次讀寫同步的話,有一定的性能壓力
- 存在個別Bug,造成恢復不能
AOF總結
AOF和RDB的選擇使用
官方推薦兩個都啟用。
如果對數據不敏感,可以選單獨用RDB。
不建議單獨用 AOF,因為可能會出現Bug。
如果只是做純內存緩存,可以都不用。
官方建議
- RDB持久化方式能夠在指定的時間間隔能對你的數據進行快照存儲
- AOF持久化方式記錄每次對服務器寫的操作,當服務器重啟的時候會重新執行這些命令來恢復原始的數據,AOF命令以redis協議追加保存每次寫的操作到文件末尾.
- Redis還能對AOF文件進行后臺重寫,使得AOF文件的體積不至于過大
- 只做緩存:如果你只希望你的數據在服務器運行的時候存在,你也可以不使用任何持久化方式.
- 同時開啟兩種持久化方式
- 在這種情況下,當redis重啟的時候會優先載入AOF文件來恢復原始的數據, 因為在通常情況下AOF文件保存的數據集要比RDB文件保存的數據集要完整.
- RDB的數據不實時,同時使用兩者時服務器重啟也只會找AOF文件。那要不要只使用AOF呢?
- 建議不要,因為RDB更適合用于備份數據庫(AOF在不斷變化不好備份), 快速重啟,而且不會有AOF可能潛在的bug,留著作為一個萬一的手段。
- 性能建議
Redis主從復制
介紹
主機數據更新后根據配置和策略, 自動同步到備機的master/slaver機制,Master以寫為主,Slave以讀為主
作用
-
讀寫分離,性能擴展
-
能對數據進行快速恢復
主從復制操作
1.準備環境(1)兩臺裝有redis的主機(2)分析 主服務是用于寫的,從服務是用于讀2.主服務配置
? (1) 打開主服務的conf文件
Vi /usr/local/redis/etc/redis.conf? (2) 注釋bind掉,讓服務可以遠程訪問
? (3) 開啟守護進程
? (4)設置密碼
requirepass 123456? (5)設置客戶端最大連接數(maxclients),根據需求改動,默認10000
maxclients 10? (6)設置內存(最好不要超過本機的最大內存,默認是不收限制的,超出宿主機內存報錯)
maxmemory 1048576? (7)設置內存策略如果內存足夠用則不用管,如果內存不夠用,建議設置最近最少使用策略(LRU),默認是內存不夠則報錯
maxmemory-policy noeviction? (8)重啟redis服務
3.配置從服務
? (1) 關閉redis服務
? (2) 配置所屬主機的ip和端口號
slaveof 192.168.56.110 6379? (3) 配置所屬主服務的密碼
masterauth 123456? (4) 從服務只讀
需要注意的是,從服務器通常是只讀,所以要配置只讀(默認是只讀,不要更改即可) slave-serve-stale-data yes? (5)重啟從redis服務
Redis哨兵機制
主從復制的缺陷
當主服務宕機時,redis不能自動切換從服務為主服務,只能通過人工手動的形式,中間會出現服務斷檔
哨兵模式解決redis主服務切換的問題
概念
哨兵模式是一種特殊的模式,首先Redis提供了哨兵的命令,哨兵是一個獨立的進程,作為進程,它會獨立運行。其原理是哨兵通過發送命令,等待Redis服務器響應,從而監控運行的多個Redis實例。
作用
- 通過發送命令,讓Redis服務器返回監控其運行狀態,包括主服務器和從服務器。
- 當哨兵監測到master宕機,會自動將slave切換成master,然后通過發布訂閱模式通知其他的從服務器,修改配置文件,讓它們切換主機。
多哨兵模式
概念
? 然而一個哨兵進程對Redis服務器進行監控,可能會出現問題,為此,我們可以使用多個哨兵進行監控。各個哨兵之間還會進行監控,這樣就形成了多哨兵模式;
故障切換的過程
假設主服務器宕機,哨兵1先檢測到這個結果,系統并不會馬上進行failover過程,僅僅是哨兵1主觀的認為主服務器不可用,這個現象成為主觀下線。當后面的哨兵也檢測到主服務器不可用,并且數量達到一定值時,那么哨兵之間就會進行一次投票,投票的結果由一個哨兵發起,進行failover操作。切換成功后,就會通過發布訂閱模式,讓各個哨兵把自己監控的從服務器實現切換主機,這個過程稱為客觀下線。這樣對于客戶端而言,一切都是透明的。
| Redis | 是 | 192.168.56.110 | 6379 | Linux |
| Redis | 否 | 192.168.56.110 | 6380 | Linux |
| Redis | 否 | 192.168.56.110 | 6381 | Linux |
| Sentinel | 192.168.56.110 | Linux | ||
| Sentinel | 192.168.56.110 | Linux | ||
| Sentinel | 192.168.56.110 | Linux |
操作步驟
1.調整為一主二仆模式,6379、6380、6381
2.自定義的/myredis目錄下新建sentinel.conf文件,名字絕不能錯
3.配置哨兵,填寫內容
sentinel monitor mymaster 127.0.0.1 6379 1 其中mymaster為監控對象起的服務器名稱, 1 為至少有多少個哨兵同意遷移的數量。4.啟動哨兵
/usr/local/bin redis做壓測可以用自帶的redis-benchmark工具 執行redis-sentinel /myredis/sentinel.conf5.當主機掛掉,從機選舉中產生新的主機
(大概10秒左右可以看到哨兵窗口日志,切換了新的主機) 哪個從機會被選舉為主機呢?根據優先級別:slave-priority 原主機重啟后會變為從機。6.復制延時
? 由于所有的寫操作都是先在Master上操作,然后同步更新到Slave上,所以從Master同步到Slave機器有一定的延遲,當系統很繁忙的時候,延遲問題會更加嚴重,Slave機器數量的增加也會使這個問題更加嚴重。
故障恢復
優先級在redis.conf中默認:slave-priority 100,值越小優先級越高
偏移量是指獲得原主機數據最全的
每個redis實例啟動后都會隨機生成一個40位的runid
Redis集群
問題
容量不夠,redis如何進行擴容?
并發寫操作, redis如何分攤?
另外,主從模式,薪火相傳模式,主機宕機,導致ip地址發生變化,應用程序中配置需要修改對應的主機地址、端口等信息。
之前通過代理主機來解決,但是redis3.0中提供了解決方案。就是無中心化集群配置。
什么是集群
Redis 集群實現了對Redis的水平擴容,即啟動N個redis節點,將整個數據庫分布存儲在這N個節點中,每個節點存儲總數據的1/N。
Redis 集群通過分區(partition)來提供一定程度的可用性(availability): 即使集群中有一部分節點失效或者無法進行通訊, 集群也可以繼續處理命令請求。
部署redis集群
1.刪除持久化數據 rdb.aof文件刪除
2.制作6個實例,6379,6380,6381,6389,6390,6391
3.配置基本信息
開啟daemonize yes Pid文件名字 指定端口 Log文件名字 Dump.rdb名字 Appendonly 關掉或者換名字4.redis cluster配置修改
cluster-enabled yes 打開集群模式 cluster-config-file nodes-6379.conf 設定節點配置文件名 cluster-node-timeout 15000 設定節點失聯時間,超過該時間(毫秒),集群自動進行主從切換。 include /home/bigdata/redis.conf port 6379 pidfile "/var/run/redis_6379.pid" dbfilename "dump6379.rdb" dir "/home/bigdata/redis_cluster" logfile "/home/bigdata/redis_cluster/redis_err_6379.log" cluster-enabled yes cluster-config-file nodes-6379.conf cluster-node-timeout 150005.修改好redis6379.conf文件,拷貝多個redis.conf文件
redis6379.conf redis6380.conf redis6381.conf redis6389.conf redis6390.conf redis6391.conf6.使用查找替換修改另外5個文件
7.啟動6個redis服務
8.將六個節點合成一個集群
組合之前,請確保所有redis實例啟動后,nodes-xxxx.conf文件都生成正常
- 合體
此處不要用127.0.0.1,請用真實地址
–replicas 1 采用最簡單的方式配置集群,一臺主機,一臺從機,正好三組。
- 普通方式登錄
可能直接進入讀主機,存儲數據時,會出現MOVED重定向操作。所以,應該以集群方式登錄。
- 采用集群策略連接,設置數據會自動切換到相應的寫主機
-
通過 cluster nodes 命令查看集群信息
-
redis cluster 如何分配這六個節點?
一個集群至少要有三個主節點。
選項 --cluster-replicas 1 表示我們希望為集群中的每個主節點創建一個從節點。
分配原則盡量保證每個主數據庫運行在不同的IP地址,每個從庫和主庫不在一個IP地址上。
slots
一個 Redis 集群包含 16384 個插槽(hash slot), 數據庫中的每個鍵都屬于這 16384 個插槽的其中一個,
集群使用公式 CRC16(key) % 16384 來計算鍵 key 屬于哪個槽, 其中 CRC16(key) 語句用于計算鍵 key 的 CRC16 校驗和 。
集群中的每個節點負責處理一部分插槽。 舉個例子, 如果一個集群可以有主節點, 其中:
節點 A 負責處理 0 號至 5460 號插槽。
節點 B 負責處理 5461 號至 10922 號插槽。
節點 C 負責處理 10923 號至 16383 號插槽。
-
在集群中錄入值
在redis-cli每次錄入、查詢鍵值,redis都會計算出該key應該送往的插槽,如果不是該客戶端對應服務器的插槽,redis會報錯,并告知應前往的redis實例地址和端口。
redis-cli客戶端提供了 –c 參數實現自動重定向。
如 redis-cli -c –p 6379 登入后,再錄入、查詢鍵值對可以自動重定向。
不在一個slot下的鍵值,是不能使用mget,mset等多鍵操作。
? 可以通過{}來定義組的概念,從而使key中{}內相同內容的鍵值對放到一個slot中去。
?
-
查詢集群中的值
CLUSTER GETKEYSINSLOT <slot><count> 返回 count 個 slot 槽中的鍵。 -
故障恢復
如果主節點下線?從節點能否自動升為主節點?注意:15秒超時
主節點恢復后,主從關系會如何?主節點回來變成從機。
如果所有某一段插槽的主從節點都宕掉,redis服務是否還能繼續?
如果某一段插槽的主從都掛掉,而cluster-require-full-coverage 為yes ,那么 ,整個集群都掛掉
如果某一段插槽的主從都掛掉,而cluster-require-full-coverage 為no ,那么,該插槽數據全都不能使用,也無法存儲。
redis.conf中的參數 cluster-require-full-coverage
Redis集群的不足
多鍵操作是不被支持的
多鍵的Redis事務是不被支持的。lua腳本不被支持
由于集群方案出現較晚,很多公司已經采用了其他的集群方案,而代理或者客戶端分片的方案想要遷移至redis cluster,需要整體遷移而不是逐步過渡,復雜度較大
Redis應用問題解決
緩存穿透
-
問題描述:
- key對應的數據在數據源并不存在,每次針對此key的請求從緩存獲取不到,請求都會壓到數據源,從而可能壓垮數據源。比如用一個不存在的用戶id獲取用戶信息,不論緩存還是數據庫都沒有,若黑客利用此漏洞進行攻擊可能壓垮數據庫。
-
解決方案
一個一定不存在緩存及查詢不到的數據,由于緩存是不命中時被動寫的,并且出于容錯考慮,如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。
解決方案
-
(1) **對空值緩存:**如果一個查詢返回的數據為空(不管是數據是否不存在),我們仍然把這個空結果(null)進行緩存,設置空結果的過期時間會很短,最長不超過五分鐘
-
(2) 設置可訪問的名單(白名單):
使用bitmaps類型定義一個可以訪問的名單,名單id作為bitmaps的偏移量,每次訪問和bitmap里面的id進行比較,如果訪問id不在bitmaps里面,進行攔截,不允許訪問。
-
(3) 采用布隆過濾器:(布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量(位圖)和一系列隨機映射函數(哈希函數)。
布隆過濾器可以用于檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是有一定的誤識別率和刪除困難。)
將所有可能存在的數據哈希到一個足夠大的bitmaps中,一個一定不存在的數據會被 這個bitmaps攔截掉,從而避免了對底層存儲系統的查詢壓力。
-
(4) 進行實時監控:當發現Redis的命中率開始急速降低,需要排查訪問對象和訪問的數據,和運維人員配合,可以設置黑名單限制服務
-
緩存擊穿
-
問題描述
- key對應的數據存在,但在redis中過期,此時若有大量并發請求過來,這些請求發現緩存過期一般都會從后端DB加載數據并回設到緩存,這個時候大并發的請求可能會瞬間把后端DB壓垮。
-
解決方案
key可能會在某些時間點被超高并發地訪問,是一種非常“熱點”的數據。這個時候,需要考慮一個問題:緩存被“擊穿”的問題。
解決問題:
-
**(1) 預先設置熱門數據:**在redis高峰訪問之前,把一些熱門數據提前存入到redis里面,加大這些熱門數據key的時長
-
**(2) 實時調整:**現場監控哪些數據熱門,實時調整key的過期時長
-
(3) 使用鎖:
- 就是在緩存失效的時候(判斷拿出來的值為空),不是立即去load db。
- 先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX)去set一個mutex key
- 當操作返回成功時,再進行load db的操作,并回設緩存,最后刪除mutex key;
- 當操作返回失敗,證明有線程在load db,當前線程睡眠一段時間再重試整個get緩存的方法。
-
緩存雪崩
-
問題描述
key對應的數據存在,但在redis中過期,此時若有大量并發請求過來,這些請求發現緩存過期一般都會從后端DB加載數據并回設到緩存,這個時候大并發的請求可能會瞬間把后端DB壓垮。
緩存雪崩與緩存擊穿的區別在于這里針對很多key緩存,前者是某一個key
正常訪問
緩存失效瞬間
-
解決方案
緩存失效時的雪崩效應對底層系統的沖擊非常可怕
方法
-
(1) 構建多級緩存架構: nginx緩存 + redis緩存 +其他緩存(ehcache等)
-
(2) 使用鎖或隊列:
用加鎖或者隊列的方式保證來保證不會有大量的線程對數據庫一次性進行讀寫,從而避免失效時大量的并發請求落到底層存儲系統上。不適用高并發情況
-
(3) 設置過期標志更新緩存:
記錄緩存數據是否過期(設置提前量),如果過期會觸發通知另外的線程在后臺去更新實際key的緩存
-
(4) 將緩存失效時間分散開:
比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重復率就會降低,就很難引發集體失效的事件。
-
總結
以上是生活随笔為你收集整理的Redis从入门到入土的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: NMF算法python源代码
- 下一篇: python3对接mysql数据库