一.背景
1.1 分佈式數據庫的原理CAP+Base
1.1.1 CAP原理
- C-Consistent 一致性
- A-Availability 可用性
- P-Partition tolerance 分區容忍性
分佈式系統的節點往往都是在不同的機器上進行網絡隔離開的,這意味着必然會有網絡斷開的風險,這個網絡斷開的場景的專業詞彙叫着網絡分區。
在網絡分區發生時,兩個分佈式節點之間無法進行通信,我們對一個節點進行的修改操作無法同步到另外一個節點,所以數據的一致性將無法滿足,因爲兩個分佈式節點的數據不再保持一致。除非我們犧牲了可用性,也就是暫停分佈式節點服務,在網絡分區發生時,不再 提供修改數據的功能,直到網絡狀況完全恢復正常再繼續對外提供服務。
分區容忍性是我們必須需要實現的。一句話概括CAP原理就是--網絡分區發生時,一致性和可用性兩難全。
CAP理論的核心是:一個分佈式系統不可能同時很好的滿足一致性,可用性和分區容錯性這三個需求,最多隻能同時較好的滿足兩個。因此,根據 CAP 原理將 NoSQL 數據庫分成了滿足 CA 原則、滿足 CP 原則和滿足 AP 原則三 大類:
- CA - 單點集羣,滿足一致性,可用性的系統,通常在可擴展性上不太強大。
- CP - 滿足一致性,分區容忍必的系統,通常性能不是特別高。
- AP - 滿足可用性,分區容忍性的系統,通常可能對一致性要求低一些。
1.1.2 BASE
BASE就是爲了解決關係數據庫強一致性引起的問題而引起的可用性降低而提出的解決方案。BASE其實是下面三個術語的縮寫:
- 基本可用(Basically Available)
- 軟狀態(Soft state)
- 最終一致(Eventually consistent)
它的思想是通過讓系統放鬆對某一時刻數據一致性的要求來換取系統整體伸縮性和性能上改觀。爲什麼這麼說呢,緣由就在於大型系統往往由於地域分佈和極高性能的要求,不可能採用分佈式事務來完成這些指標,要想獲得這些指標,我們必須採用另外一種方式來完成,這裏BASE就是解決這個問題的辦法
1.2 Redis簡介
1.2.1 概述
Redis(REmote DIctionary Server,遠程字典服務器),是完全開源免費的,用C語言編寫的,遵守BSD協議,是一個高性能的(key/value)分佈式內存數據庫,基於內存運行並支持持久化的NoSQL數據庫,是當前最熱門的NoSQL數據庫之一,也被人們稱爲數據結構服務器。
1.2.2 特點(其他key-value緩存產品也具有)
- Redis支持數據的持久化,可以將內存中的數據保存在磁盤中,重啓的時候可以再次加載使用
- Redis不僅僅支持簡單的key-value類型的數據,同時還提供list,set,zset,hash等數據結構的存儲
- Redis支持數據的備份,即master-slave模式的數據備份
1.2.3 Redis的功能
- 內存存儲和持久化:redis支持異步將內存中的數據寫到硬盤上,同時不影響繼續服務
- 取最新N個數據的操作,如:可以將最新的10條評論的ID放在Redis的List集合裏面
- 模擬類似於HttpSession這種需要設置過期時間的功能
- 發佈,訂閱消息系統
- 定時器,計數器
- ...
1.2.4 爲什麼要使用Redis?
主要從“高性能”和“高併發”這兩點來看待這個問題。
高性能:
假如用戶第一次訪問數據庫中的某些數據。這個過程會比較慢,因爲是從硬盤上讀取的。將該用戶訪問的數據存在緩存中,這樣下一次再訪問這些數據的時候就可以直接從緩存中獲取了。操作緩存就是直接操作內存,所以速度相當快。如果數據庫中的對應數據改變的之後,同步改變緩存中相應的數據即可!
高併發:
直接操作緩存能夠承受的請求是遠遠大於直接訪問數據庫的,所以我們可以考慮把數據庫中的部分數據轉移到緩存中去,這樣用戶的一部分請求會直接到緩存這裏而不用經過數據庫。
1.2.5 Redis的線程模型
參考地址:https://www.javazhiyin.com/22943.html
redis 內部使用文件事件處理器 file event handler
,這個文件事件處理器是單線程的,所以 redis 才叫做單線程的模型。它採用 IO 多路複用機制同時監聽多個 socket,根據 socket 上的事件來選擇對應的事件處理器進行處理。
文件事件處理器的結構包含 4 個部分:
- 多個 socket
- IO 多路複用程序
- 文件事件分派器
- 事件處理器(連接應答處理器、命令請求處理器、命令回覆處理器)
多個 socket 可能會併發產生不同的操作,每個操作對應不同的文件事件,但是 IO 多路複用程序會監聽多個 socket,會將 socket 產生的事件放入隊列中排隊,事件分派器每次從隊列中取出一個事件,把該事件交給對應的事件處理器進行處理。
1.2.4 安裝和使用
分爲Linux和Windows安裝,可以在網上進行搜索獲得安裝方法。
- 啓動命令:redis-server /目錄/redis.conf(一般爲了安全性起見,將Redis安裝目錄下的redis.conf進行拷貝,並進行修改,將daemonize no改爲yes)
- 連接客戶端:redis-cli
- 指定端口連接:redis-cli -p 6379
- 單實例關閉:redis-cli shutdown
- 多實例關閉:指定端口關閉 redis-cli -p 6379 shutdown
- 默認16個數據庫,默認數據庫爲0,可以使用SELECT <dbid>命令在連接上指定數據庫id
- Dbsize查看當前數據庫key的數量
- Flushdb清空當前數據庫
- Flushall清空所有的庫
- 統一的密碼管理
- Redis索引都是從0開始的
二.Redis數據類型及常用的命令
Redis支持五種數據類型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)
2.1 String(字符串)
string是Redis最基本的類型,一個key對應一個value。
string類型是二進制安全的,意味着Redis的string可以包含任何數據,比如JPG圖片或者序列化的對象。
string類型是Redis最基本的數據類型,Redis所有的數據結構都是以唯一的key字符串作爲名稱,然後通過這個唯一的key來獲取相應的value數據。不同類型的數據結構的差異就是在於value的結構不一樣。
Redis的字符串是動態字符串,是可以修改的字符串,內部結構類似於Java的ArrayList,採用預分配冗餘空間的方式來減少內存的頻繁分配,如上圖所示,內部爲當前字符串實際分配的空間capacity一般要高於實際字符串長度len。當字符串長度小於1M時,擴容都是加倍現有的空間,如果超過1M,擴容時一次只會多擴1M的空間,需要注意的是string類型的值最大能存儲512MB。
字符串結構的使用非常廣泛,一個常見的用途就是緩存用戶信息。將用戶信息結構體使用JSON序列化成字符串,然後將序列化的字符串塞入Redis來緩存。同樣,取用戶信息會經過一次反序列化的過程。
字符串常用的命令如下:
序號 | 命令及描述 |
---|---|
1 | SET key value 設置指定 key 的值 |
2 | GET key 獲取指定 key 的值。 |
3 | GETRANGE key start end 返回 key 中字符串值的子字符 |
4 | GETSET key value 將給定 key 的值設爲 value ,並返回 key 的舊值(old value)。 |
5 | GETBIT key offset 對 key 所儲存的字符串值,獲取指定偏移量上的位(bit)。 |
6 | MGET key1 [key2..] 獲取所有(一個或多個)給定 key 的值。 |
7 | SETBIT key offset value 對 key 所儲存的字符串值,設置或清除指定偏移量上的位(bit)。 |
8 | SETEX key seconds value 將值 value 關聯到 key ,並將 key 的過期時間設爲 seconds (以秒爲單位)。 |
9 | SETNX key value 只有在 key 不存在時設置 key 的值。 |
10 | SETRANGE key offset value 用 value 參數覆寫給定 key 所儲存的字符串值,從偏移量 offset 開始。 |
11 | STRLEN key 返回 key 所儲存的字符串值的長度。 |
12 | MSET key value [key value ...] 同時設置一個或多個 key-value 對。 |
13 | MSETNX key value [key value ...] 同時設置一個或多個 key-value 對,當且僅當所有給定 key 都不存在。 |
14 | PSETEX key milliseconds value 這個命令和 SETEX 命令相似,但它以毫秒爲單位設置 key 的生存時間,而不是像 SETEX 命令那樣,以秒爲單位。 |
15 | INCR key 將 key 中儲存的數字值增一。 |
16 | INCRBY key increment 將 key 所儲存的值加上給定的增量值(increment) 。 |
17 | INCRBYFLOAT key increment 將 key 所儲存的值加上給定的浮點增量值(increment) 。 |
18 | DECR key 將 key 中儲存的數字值減一。 |
19 | DECRBY key decrement key 所儲存的值減去給定的減量值(decrement) 。 |
20 | APPEND key value 如果 key 已經存在並且是一個字符串, APPEND 命令將指定的 value 追加到該 key 原來值(value)的末尾。 |
2.2 Hash(哈希)
hash是一個鍵值對集合。是一個string類型的filed和value的映射表,hash特別適合存儲對象。
Redis的hash相當於Java語言中的HashMap,它是無序字段,內部實現結構同Java的HashMap也是一致的,同樣是數據+鏈表的二維結構。第一維hash的數組位置碰撞時,就會將碰撞的元素使用鏈表串接起來。
不同的是,Redis的hash的值只能是字符串,另外它們的rehash方式不一樣,因爲Java的HashMap在字典很大的時,rehash是個耗時的操作,需要一次性全部rehash。Redis爲了提高性能,不堵塞服務,所以採用了漸進式rehash策略。
漸進式rehash會在rehash的同時,保留新舊兩個hash結構,查詢時會同時查詢兩個hash結構,然後在後續的定時任務中以及hash的子指令中,循序漸進地將舊hash的內容一點點遷移到新的hash結構中。
當hash移除了最後一個元素之後,該數據結構自動被刪除,內存被回收。
hash結構也可以用來存儲用戶信息,不同於字符串一次性需要全部序列化整個對象,hash可以對用戶結構的每個字段單獨存儲。這樣需要獲取用戶信息可以進行部分獲取。而以整個字符串的形式去保存用戶信息只能一次性全部讀完,這樣就會比較浪費網絡流量。
hash也有缺點,hash結構存儲消耗要高於單個字符串。
hash常用的命令如下:
序號 | 命令及描述 |
---|---|
1 | HDEL key field1 [field2] 刪除一個或多個哈希表字段 |
2 | HEXISTS key field 查看哈希表 key 中,指定的字段是否存在。 |
3 | HGET key field 獲取存儲在哈希表中指定字段的值。 |
4 | HGETALL key 獲取在哈希表中指定 key 的所有字段和值 |
5 | HINCRBY key field increment 爲哈希表 key 中的指定字段的整數值加上增量 increment 。 |
6 | HINCRBYFLOAT key field increment 爲哈希表 key 中的指定字段的浮點數值加上增量 increment 。 |
7 | HKEYS key 獲取所有哈希表中的字段 |
8 | HLEN key 獲取哈希表中字段的數量 |
9 | HMGET key field1 [field2] 獲取所有給定字段的值 |
10 | HMSET key field1 value1 [field2 value2 ] 同時將多個 field-value (域-值)對設置到哈希表 key 中。 |
11 | HSET key field value 將哈希表 key 中的字段 field 的值設爲 value 。 |
12 | HSETNX key field value 只有在字段 field 不存在時,設置哈希表字段的值。 |
13 | HVALS key 獲取哈希表中所有值 |
14 | HSCAN key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的鍵值對。 |
2.3 List(列表)
Redis 列表是簡單的字符串列表,按照插入順序排序。你可以添加一個元素到列表的頭部(左邊)或者尾部(右邊)。
Redis的列表相當於Java語言裏面的LinkedList,注意它是鏈表而不是數組。這意味着list的插入和刪除操作非常快,時間複雜度爲o(1),但是索引定位很慢,時間複雜度爲o(n)。
如果在深入一點,你會發現Redis底層存儲還不是一個簡單的LinkedList,而是稱之爲快速鏈表quicklist的一個結構。
首先在列表元素較少的情況下會使用一塊連續的內存存儲,這個結構是ziplist,也即是壓縮列表,它將所有的元素緊挨在一起存儲,分配的是一塊連續的內存。當數據量比較多的時候纔會改成quicklist。因爲普通的列表需要的附加指針空間太大了,會比較浪費空間,而且會加重內存的碎片化。比如這個列表裏存的只是int類型的數據,結構上還需要兩個額外的指針prev和next。所以Redis將鏈表和ziplist結合起來組成了quicklist。也就是將ziplist使用雙向指針串起來使用。這樣既滿足可快速的插入刪除性能,又不會出現太大的空間冗餘。
當列表彈出最後一個元素之後,該數據結構自動被刪除,內存被回收。
Redis的列表結構常用來做異步隊列使用。將需要延後處理的任務結構體序列化字符串塞進Redis列表,另一個線程從列表中輪詢數據進行處理。
list常用的命令如下:
序號 | 命令及描述 |
---|---|
1 | BLPOP key1 [key2 ] timeout 移出並獲取列表的第一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。 |
2 | BRPOP key1 [key2 ] timeout 移出並獲取列表的最後一個元素, 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。 |
3 | BRPOPLPUSH source destination timeout 從列表中彈出一個值,將彈出的元素插入到另外一個列表中並返回它; 如果列表沒有元素會阻塞列表直到等待超時或發現可彈出元素爲止。 |
4 | LINDEX key index 通過索引獲取列表中的元素 |
5 | LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者後插入元素 |
6 | LLEN key 獲取列表長度 |
7 | LPOP key 移出並獲取列表的第一個元素 |
8 | LPUSH key value1 [value2] 將一個或多個值插入到列表頭部 |
9 | LPUSHX key value 將一個值插入到已存在的列表頭部 |
10 | LRANGE key start stop 獲取列表指定範圍內的元素 |
11 | LREM key count value 移除列表元素 |
12 | LSET key index value 通過索引設置列表元素的值 |
13 | LTRIM key start stop 對一個列表進行修剪(trim),就是說,讓列表只保留指定區間內的元素,不在指定區間之內的元素都將被刪除。 |
14 | RPOP key 移除列表的最後一個元素,返回值爲移除的元素。 |
15 | RPOPLPUSH source destination 移除列表的最後一個元素,並將該元素添加到另一個列表並返回 |
16 | RPUSH key value1 [value2] 在列表中添加一個或多個值 |
17 | RPUSHX key value 爲已存在的列表添加值 |
2.4 set(集合)
Redis的Set是string類型的無序集合。它是通過HashTable實現實現的。
Redis的集合相當於Java語言裏面的HashSet,它內部的鍵值對是無序的唯一的。它的內部實現相當於一個特殊的字段,字典中的所有一個value值都是一個值NULL。
當集合中的最後一個元素移除之後,數據結構自動刪除,內存被回收。
set結構可以用來存儲活動中獎的用戶的ID,因爲有去重功能,可以保證同一個用戶不會中獎兩次。
set常用的命令如下:
序號 | 命令及描述 |
---|---|
1 | SADD key member1 [member2] 向集合添加一個或多個成員 |
2 | SCARD key 獲取集合的成員數 |
3 | SDIFF key1 [key2] 返回給定所有集合的差集 |
4 | SDIFFSTORE destination key1 [key2] 返回給定所有集合的差集並存儲在 destination 中 |
5 | SINTER key1 [key2] 返回給定所有集合的交集 |
6 | SINTERSTORE destination key1 [key2] 返回給定所有集合的交集並存儲在 destination 中 |
7 | SISMEMBER key member 判斷 member 元素是否是集合 key 的成員 |
8 | SMEMBERS key 返回集合中的所有成員 |
9 | SMOVE source destination member 將 member 元素從 source 集合移動到 destination 集合 |
10 | SPOP key 移除並返回集合中的一個隨機元素 |
11 | SRANDMEMBER key [count] 返回集合中一個或多個隨機數 |
12 | SREM key member1 [member2] 移除集合中一個或多個成員 |
13 | SUNION key1 [key2] 返回所有給定集合的並集 |
14 | SUNIONSTORE destination key1 [key2] 所有給定集合的並集存儲在 destination 集合中 |
15 | SSCAN key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素 |
2.5 zset(有序集合)
Redis zset 和 set 一樣也是string類型元素的集合,且不允許重複的成員。不同的是每個元素都會關聯一個double類型的分數。redis正是通過分數來爲集合中的成員進行從小到大的排序。zset的成員是唯一的,但分數(score)卻可以重複。
zset類似於Java的SortedSet和HashMap的結合體,一方面它是一個set,保證內部value的唯一性,另一方面它可以給一個value賦予一個score,代表這個value的排序權重。它的內部實現用的是一種叫做跳躍列表的數據結構。
zset中最後一個value被移除後,數據結構自動刪除,內存被回收。
zset可以用來存粉絲列表,value值是粉絲用戶ID,score是關注時間,可以對粉絲列表按關注時間來排序。還可以存儲學生的成績,value值是學生ID,score是他的考試成績,可以對成績按分數進行排序就可以得到他的名次。
zset常用命令如下:
跳躍列表
zset內部的排序功能是通過跳躍列表數據結構來實現的,它的結構非常特殊,也比較複雜。
因爲zset要支持隨機的插入和刪除,所以不好使用數組來表示。先來看一個普通的鏈表結構:
我們需要這個鏈表根據score值進行排序。這意味着當有新元素需要插入時,要定位到特定位置的插入點,這樣纔可能保持鏈表是有序的。通常我們會通過二分查找來找到插入點,但是二分查找的對象必須是數組,只有數組纔可以支持快速位置定位,鏈表做不到,那該怎麼辦?
跳躍列表的做法是:最下面一層的所有的元素會串起來,然後每隔幾個元素挑選出一個代表來,再將這幾個代表使用另外一級指針串起來。然後在這些代表裏在挑選二級代表,再串起來。最終形成金字塔結構。
跳躍列表之所以跳躍,是因爲內部的元素可能身兼數職,可以同時處於不同的層中。
定位插入點時,現在頂層進行定位,然後下潛到下一級定位,一直下潛到最底層找到合適位置,將新元素插進去。跳躍列表採取一個隨機策略來決定新元素可以兼職到第幾層。首先L0層肯定是100%,L1層只有50%的概覽,L2層只有25%的概覽,L3層只有12.5層概覽,一直隨機到最頂層。絕大數元素都過不了幾層,只有極少數元素可以深入到頂層。列表中的元素越多,能夠深入的層次就越深,能進入到頂層的概覽就會越大。
容器型數據結構的通用規則
list/set/hash/zset這四種數據結構是容器型數據結構,它們共享下面兩條通用規則:
- create if not exists:如果容器不存在,那就創建一個,再進行操作。
- drop if not elements:如果容器裏的元素沒有了,那麼立即刪除元素,釋放內存。
過期時間
Redis所有的數據結構都可以設置過期時間,時間到了,Redis就會自動刪除相應的對象。需要注意的是過期是以對象爲單位的,比如一個hash結構的過期是整個hash對象的過期。
還有一個需要特別注意的地方是如果一個字符串已經設置了過期時間,然後調用了set方法修改了它,它的過期時間會消失。
三.解析配置文件redis.conf
在Redis的解壓目錄下有個很重要的配置文件 redis.conf ,關於Redis的很多功能的配置都在此文件中完成的,一般爲了不破壞安裝的文件,出廠默認配置最好不要去改,所以可以將此文件複製一下,在複製文件中進行更改。
3.1 Units單位
# Redis configuration file example
# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.
配置大小單位,開頭定義了一些基本的度量單位,只支持bytes,不支持bit。對大小寫不敏感。
3.2 INCLUDES包含
################################## INCLUDES ###################################
# Include one or more other config files here. This is useful if you
# have a standard template that goes to all Redis servers but also need
# to customize a few per-server settings. Include files can include
# other files, so use this wisely.
#
# Notice option "include" won't be rewritten by command "CONFIG REWRITE"
# from admin or Redis Sentinel. Since Redis always uses the last processed
# line as value of a configuration directive, you'd better put includes
# at the beginning of this file to avoid overwriting config change at runtime.
#
# If instead you are interested in using includes to override configuration
# options, it is better to use include as the last line.
#
# include .\path\to\local.conf
# include c:\path\to\other.conf
Redis只有一個配置文件,如果多個人進行開發維護,那麼就需要多個這樣的配置we年,這時候多個配置文件就可以在此通過include\path\to\local.conf配置進來,而原來的redis.conf配置文件就作爲一個總閥。
需要注意的是,如果將此配置寫在redis.conf文件的開頭,那麼後面的配置會覆蓋引入文件的配置,如果想以引入文件的配置爲主,那麼需要將include配置寫在redis.conf文件的末尾。
3.2 MODULES
################################## MODULES #####################################
# Load modules at startup. If the server is not able to load modules
# it will abort. It is possible to use multiple loadmodule directives.
#
# loadmodule /path/to/my_module.so
# loadmodule /path/to/other_module.so
redis3.0的爆炸功能是新增了集羣,而redis4.0就是在3.0的基礎上新增了許多功能,其中這裏的 自定義模塊配置就是其中之一。通過這裏的 loadmodule 配置將引入自定義模塊來新增一些功能。
3.4 NETWORK
################################## NETWORK #####################################
# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only into
# the IPv4 loopback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
#
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST COMMENT THE FOLLOWING LINE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 127.0.0.1
# Protected mode is a layer of security protection, in order to avoid that
# Redis instances left open on the internet are accessed and exploited.
#
# When protected mode is on and if:
#
# 1) The server is not binding explicitly to a set of addresses using the
# "bind" directive.
# 2) No password is configured.
#
# The server only accepts connections from clients connecting from the
# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain
# sockets.
#
# By default protected mode is enabled. You should disable it only if
# you are sure you want clients from other hosts to connect to Redis
# even if no authentication is configured, nor a specific set of interfaces
# are explicitly listed using the "bind" directive.
protected-mode yes
# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379
# TCP listen() backlog.
#
# In high requests-per-second environments you need an high backlog in order
# to avoid slow clients connections issues. Note that the Linux kernel
# will silently truncate it to the value of /proc/sys/net/core/somaxconn so
# make sure to raise both the value of somaxconn and tcp_max_syn_backlog
# in order to get the desired effect.
tcp-backlog 511
# Unix socket.
#
# Specify the path for the Unix socket that will be used to listen for
# incoming connections. There is no default, so Redis will not listen
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# unixsocketperm 700
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0
# TCP keepalive.
#
# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence
# of communication. This is useful for two reasons:
#
# 1) Detect dead peers.
# 2) Take the connection alive from the point of view of network
# equipment in the middle.
#
# On Linux, the specified value (in seconds) is the period used to send ACKs.
# Note that to close the connection the double of the time is needed.
# On other kernels the period depends on the kernel configuration.
#
# A reasonable value for this option is 300 seconds, which is the new
# Redis default starting with Redis 3.2.1.
tcp-keepalive 300
介紹些比較重要的配置,如下:
(1)bind:綁定redis服務器網卡IP,默認爲127.0.0.1,即本地迴環地址。訪問redis服務只能通過本機的客戶端連接,而無法通過遠程連接。如果bind選項爲空的話,那麼會接收所有賴在可用網絡接口的連接。
(2)port:指定redis運行的端口,默認是6379。由於Redis是單線程模型,因此單機開多個Redis進程的時候會修改端口。
(3)timeout:設置客戶端連接時的超時時間,單位爲秒。當客戶端在這段時間內沒有發出任何指令,那麼關閉該連接。默認值爲0,表示不關閉。
(4)tcp-keepalive:單位是秒,表示將週期的使用SO_KEEPALIVE檢測客戶端是否還處於健康狀態,避免服務器一直堵塞,官方給出的建議是300s,如果設置爲0,則不會週期性的檢測。
3.5 GENERAL
################################# GENERAL #####################################
# By default Redis does not run as a daemon. Use 'yes' if you need it.
# Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
daemonize no
# If you run Redis from upstart or systemd, Redis can interact with your
# supervision tree. Options:
# supervised no - no supervision interaction
# supervised upstart - signal upstart by putting Redis into SIGSTOP mode
# supervised systemd - signal systemd by writing READY=1 to $NOTIFY_SOCKET
# supervised auto - detect upstart or systemd method based on
# UPSTART_JOB or NOTIFY_SOCKET environment variables
# Note: these supervision methods only signal "process is ready."
# They do not enable continuous liveness pings back to your supervisor.
supervised no
# If a pid file is specified, Redis writes it where specified at startup
# and removes it at exit.
#
# When the server runs non daemonized, no pid file is created if none is
# specified in the configuration. When the server is daemonized, the pid file
# is used even if not specified, defaulting to "/var/run/redis.pid".
#
# Creating a pid file is best effort: if Redis is not able to create it
# nothing bad happens, the server will start and run normally.
pidfile /var/run/redis_6379.pid
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
# Specify the log file name. Also the empty string can be used to force
# Redis to log on the standard output. Note that if you use standard
# output for logging but daemonize, logs will be sent to /dev/null
logfile ""
# To enable logging to the system logger, just set 'syslog-enabled' to yes,
# and optionally update the other syslog parameters to suit your needs.
# syslog-enabled no
# Specify the syslog identity.
# syslog-ident redis
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
# syslog-facility local0
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
databases 16
# By default Redis shows an ASCII art logo only when started to log to the
# standard output and if the standard output is a TTY. Basically this means
# that normally a logo is displayed only in interactive sessions.
#
# However it is possible to force the pre-4.0 behavior and always show a
# ASCII art logo in startup logs by setting the following option to yes.
always-show-logo yes
具體配置解析如下:
(1)daemonize:設置爲yes表示指定Redis以守護進程的方式啓動(後臺啓動),默認值爲no。
(2)pidfile:配置PID文件路徑。
(3)loglelevel:定義日誌級別。默認值爲notice,有以下四種取值:
①debug:記錄大量日誌信息,適用於測試,開發階段。
②verbose:較多的日誌信息
③notice:適量日誌信息,使用於生成環境
④warning:僅有部分重要,關鍵信息纔會被記錄
(4)logfile:配置log文件地址,默認打印在命名行終端的窗口上
(5)databases:設置數據庫的數量,默認的數據庫是DB 0。可以在每個連接上使用selec <dbid>命令選擇一個不同的數據庫,dbid是一個介於0到databases-1的數值。默認值是16,也就是說默認Redis有16個數據庫。
3.6 SNAPSHOTTING
這裏的配置主要用來做持久化操作,有關持久化的操作後面會介紹。
################################ SNAPSHOTTING ################################
#
# Save the DB on disk:
#
# save <seconds> <changes>
#
# Will save the DB if both the given number of seconds and the given
# number of write operations against the DB occurred.
#
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
save 900 1
save 300 10
save 60 10000
# By default Redis will stop accepting writes if RDB snapshots are enabled
# (at least one save point) and the latest background save failed.
# This will make the user aware (in a hard way) that data is not persisting
# on disk properly, otherwise chances are that no one will notice and some
# disaster will happen.
#
# If the background saving process will start working again Redis will
# automatically allow writes again.
#
# However if you have setup your proper monitoring of the Redis server
# and persistence, you may want to disable this feature so that Redis will
# continue to work as usual even if there are problems with disk,
# permissions, and so forth.
stop-writes-on-bgsave-error yes
# Compress string objects using LZF when dump .rdb databases?
# For default that's set to 'yes' as it's almost always a win.
# If you want to save some CPU in the saving child set it to 'no' but
# the dataset will likely be bigger if you have compressible values or keys.
rdbcompression yes
# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
# This makes the format more resistant to corruption but there is a performance
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
# for maximum performances.
#
# RDB files created with checksum disabled have a checksum of zero that will
# tell the loading code to skip the check.
rdbchecksum yes
# The filename where to dump the DB
dbfilename dump.rdb
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
(1)seve:這裏是用來配置觸發Redis持久化條件,也就是什麼時候將內存中的數據保存在硬盤中。默認如下配置:
save 900 1 表示900秒內如果至少有1個key的值變化,則保存
save 300 10 表示300秒內如果至少有10個key的值變化,則保存
save 60 10000 表示60秒內如果至少有10000個key的值變化,則保存
當然只是使用Redis的緩存功能,不需要持久化,可以註釋掉所有的save行來停用保存功能。可以直接使用一個空字符串來實現停用。
(2)stop-writes-on-bgsave-errror:默認值是yes。當啓用了RDB且最後一次後臺數據失敗,Redis是否停止接受數據。這會讓用戶意識到數據沒有正確持久化到磁盤上,否則沒有人會注意到災難發生了。如果Redis重啓了,那麼又可以開始接受數據了。如果配置成no,表示你不在乎數據不一致或者有其他的手段發現和控制。
(3)rdbcompression :默認值是yes。對於存儲到磁盤中的快照,可以設置是否進行壓縮存儲。如果是的話,redis可以採用LZF算法進行壓縮。如果你不想消耗CPU來進行壓縮的話,可以設置爲關閉此功能,但是存儲在磁盤上的快照會非常大。
(4)rdbchecksum:默認值是yes。在存儲快照後,還可以讓Redis使用CRC64算法來進行數據校驗,但是這樣做會增加大約10%的性能消耗,如果希望可獲取最大的性能提升,可以關閉此功能。
(5)dbfilename:設置快照的文件名,默認是dump.rdb。
(6)dir:設置快照文件的存放位置,這個配置項一定是一個目錄,而不是文件名。使用上面的dbfilename作爲保存的文件名。
3.7 REPLICATION
################################# REPLICATION #################################
# Master-Replica replication. Use replicaof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
# +------------------+ +---------------+
# | Master | ---> | Replica |
# | (receive writes) | | (exact copy) |
# +------------------+ +---------------+
#
# 1) Redis replication is asynchronous, but you can configure a master to
# stop accepting writes if it appears to be not connected with at least
# a given number of replicas.
# 2) Redis replicas are able to perform a partial resynchronization with the
# master if the replication link is lost for a relatively small amount of
# time. You may want to configure the replication backlog size (see the next
# sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
# network partition replicas automatically try to reconnect to masters
# and resynchronize with them.
#
# replicaof <masterip> <masterport>
# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the replica request.
#
# masterauth <master-password>
# When a replica loses its connection with the master, or when the replication
# is still in progress, the replica can act in two different ways:
#
# 1) if replica-serve-stale-data is set to 'yes' (the default) the replica will
# still reply to client requests, possibly with out of date data, or the
# data set may just be empty if this is the first synchronization.
#
# 2) if replica-serve-stale-data is set to 'no' the replica will reply with
# an error "SYNC with master in progress" to all the kind of commands
# but to INFO, replicaOF, AUTH, PING, SHUTDOWN, REPLCONF, ROLE, CONFIG,
# SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, PUBSUB,
# COMMAND, POST, HOST: and LATENCY.
#
replica-serve-stale-data yes
# You can configure a replica instance to accept writes or not. Writing against
# a replica instance may be useful to store some ephemeral data (because data
# written on a replica will be easily deleted after resync with the master) but
# may also cause problems if clients are writing to it because of a
# misconfiguration.
#
# Since Redis 2.6 by default replicas are read-only.
#
# Note: read only replicas are not designed to be exposed to untrusted clients
# on the internet. It's just a protection layer against misuse of the instance.
# Still a read only replica exports by default all the administrative commands
# such as CONFIG, DEBUG, and so forth. To a limited extent you can improve
# security of read only replicas using 'rename-command' to shadow all the
# administrative / dangerous commands.
replica-read-only yes
# Replication SYNC strategy: disk or socket.
#
# -------------------------------------------------------
# WARNING: DISKLESS REPLICATION IS EXPERIMENTAL CURRENTLY
# -------------------------------------------------------
#
# New replicas and reconnecting replicas that are not able to continue the replication
# process just receiving differences, need to do what is called a "full
# synchronization". An RDB file is transmitted from the master to the replicas.
# The transmission can happen in two different ways:
#
# 1) Disk-backed: The Redis master creates a new process that writes the RDB
# file on disk. Later the file is transferred by the parent
# process to the replicas incrementally.
# 2) Diskless: The Redis master creates a new process that directly writes the
# RDB file to replica sockets, without touching the disk at all.
#
# With disk-backed replication, while the RDB file is generated, more replicas
# can be queued and served with the RDB file as soon as the current child producing
# the RDB file finishes its work. With diskless replication instead once
# the transfer starts, new replicas arriving will be queued and a new transfer
# will start when the current one terminates.
#
# When diskless replication is used, the master waits a configurable amount of
# time (in seconds) before starting the transfer in the hope that multiple replicas
# will arrive and the transfer can be parallelized.
#
# With slow disks and fast (large bandwidth) networks, diskless replication
# works better.
repl-diskless-sync no
# When diskless replication is enabled, it is possible to configure the delay
# the server waits in order to spawn the child that transfers the RDB via socket
# to the replicas.
#
# This is important since once the transfer starts, it is not possible to serve
# new replicas arriving, that will be queued for the next RDB transfer, so the server
# waits a delay in order to let more replicas arrive.
#
# The delay is specified in seconds, and by default is 5 seconds. To disable
# it entirely just set it to 0 seconds and the transfer will start ASAP.
repl-diskless-sync-delay 5
# Replicas send PINGs to server in a predefined interval. It's possible to change
# this interval with the repl_ping_replica_period option. The default value is 10
# seconds.
#
# repl-ping-replica-period 10
# The following option sets the replication timeout for:
#
# 1) Bulk transfer I/O during SYNC, from the point of view of replica.
# 2) Master timeout from the point of view of replicas (data, pings).
# 3) Replica timeout from the point of view of masters (REPLCONF ACK pings).
#
# It is important to make sure that this value is greater than the value
# specified for repl-ping-replica-period otherwise a timeout will be detected
# every time there is low traffic between the master and the replica.
#
# repl-timeout 60
# Disable TCP_NODELAY on the replica socket after SYNC?
#
# If you select "yes" Redis will use a smaller number of TCP packets and
# less bandwidth to send data to replicas. But this can add a delay for
# the data to appear on the replica side, up to 40 milliseconds with
# Linux kernels using a default configuration.
#
# If you select "no" the delay for data to appear on the replica side will
# be reduced but more bandwidth will be used for replication.
#
# By default we optimize for low latency, but in very high traffic conditions
# or when the master and replicas are many hops away, turning this to "yes" may
# be a good idea.
repl-disable-tcp-nodelay no
# Set the replication backlog size. The backlog is a buffer that accumulates
# replica data when replicas are disconnected for some time, so that when a replica
# wants to reconnect again, often a full resync is not needed, but a partial
# resync is enough, just passing the portion of data the replica missed while
# disconnected.
#
# The bigger the replication backlog, the longer the time the replica can be
# disconnected and later be able to perform a partial resynchronization.
#
# The backlog is only allocated once there is at least a replica connected.
#
# repl-backlog-size 1mb
# After a master has no longer connected replicas for some time, the backlog
# will be freed. The following option configures the amount of seconds that
# need to elapse, starting from the time the last replica disconnected, for
# the backlog buffer to be freed.
#
# Note that replicas never free the backlog for timeout, since they may be
# promoted to masters later, and should be able to correctly "partially
# resynchronize" with the replicas: hence they should always accumulate backlog.
#
# A value of 0 means to never release the backlog.
#
# repl-backlog-ttl 3600
# The replica priority is an integer number published by Redis in the INFO output.
# It is used by Redis Sentinel in order to select a replica to promote into a
# master if the master is no longer working correctly.
#
# A replica with a low priority number is considered better for promotion, so
# for instance if there are three replicas with priority 10, 100, 25 Sentinel will
# pick the one with priority 10, that is the lowest.
#
# However a special priority of 0 marks the replica as not able to perform the
# role of master, so a replica with priority of 0 will never be selected by
# Redis Sentinel for promotion.
#
# By default the priority is 100.
replica-priority 100
# It is possible for a master to stop accepting writes if there are less than
# N replicas connected, having a lag less or equal than M seconds.
#
# The N replicas need to be in "online" state.
#
# The lag in seconds, that must be <= the specified value, is calculated from
# the last ping received from the replica, that is usually sent every second.
#
# This option does not GUARANTEE that N replicas will accept the write, but
# will limit the window of exposure for lost writes in case not enough replicas
# are available, to the specified number of seconds.
#
# For example to require at least 3 replicas with a lag <= 10 seconds use:
#
# min-replicas-to-write 3
# min-replicas-max-lag 10
#
# Setting one or the other to 0 disables the feature.
#
# By default min-replicas-to-write is set to 0 (feature disabled) and
# min-replicas-max-lag is set to 10.
# A Redis master is able to list the address and port of the attached
# replicas in different ways. For example the "INFO replication" section
# offers this information, which is used, among other tools, by
# Redis Sentinel in order to discover replica instances.
# Another place where this info is available is in the output of the
# "ROLE" command of a master.
#
# The listed IP and address normally reported by a replica is obtained
# in the following way:
#
# IP: The address is auto detected by checking the peer address
# of the socket used by the replica to connect with the master.
#
# Port: The port is communicated by the replica during the replication
# handshake, and is normally the port that the replica is using to
# listen for connections.
#
# However when port forwarding or Network Address Translation (NAT) is
# used, the replica may be actually reachable via different IP and port
# pairs. The following two options can be used by a replica in order to
# report to its master a specific set of IP and port, so that both INFO
# and ROLE will report those values.
#
# There is no need to use both the options if you need to override just
# the port or the IP address.
#
# replica-announce-ip 5.5.5.5
# replica-announce-port 1234
(1)slave-serve-stale-data:默認值是yes。當一個salve與master失去聯繫,或者複製正在進行的時候,slave可能會有兩種表現:
①如果是yes,slave仍會應答客戶端請求,但是返回的數據可能是過時的,或者數據可能是空的在第一次同步的時候。
②如果是no,在你執行除了info he slaveof之外的命令時,slave會返回一個"SYNC with master in progress" 的錯誤。
(2)slave-read-only:配置Redis的Salve實例是否接受寫操作,即Slave是否爲只讀Redis。默認值爲yes。
(3)repl-diskless-sync:主從複製是否使用無硬盤複製功能。默認值爲no。
(4)repl-diskless-sync-delay:當啓用無硬盤備份,服務器等待一段時間後纔會通過套接字向從站傳送RDB文件,這個等待時間是可配置的。這一點很重要,因爲一旦傳送開始,就不可能再爲一個新到達的從站服務。從站則要排隊等待下一次RDB傳送。因此服務器等待一段時間以期更多的從站到達。延遲時間以秒爲單位,默認爲5秒。要關閉這一功能,只需將它設置爲0秒,傳送會立即啓動。
(5)repl-disable-tcp-nodelay:同步之後是否禁用從站上的TCP_NODELAY,如果你選擇yes,redis則會使用較少量的TCP包和帶寬向從站發送數據。但這會導致在從站增加一點數據的延時。 Linux內核默認配置情況下最多40毫秒的延時。如果選擇no,從站的數據延時不會那麼多,但備份需要的帶寬相對較多。默認情況下我們將潛在因素優化,但在高負載情況下或者在主從站都跳的情況下,把它切換爲yes是個好主意。默認值爲no。
3.8 SECURITY
################################## SECURITY ###################################
# Require clients to issue AUTH <PASSWORD> before processing any other
# commands. This might be useful in environments in which you do not trust
# others with access to the host running redis-server.
#
# This should stay commented out for backward compatibility and because most
# people do not need auth (e.g. they run their own servers).
#
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
# requirepass foobared
# Command renaming.
#
# It is possible to change the name of dangerous commands in a shared
# environment. For instance the CONFIG command may be renamed into something
# hard to guess so that it will still be available for internal-use tools
# but not available for general clients.
#
# Example:
#
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
#
# It is also possible to completely kill a command by renaming it into
# an empty string:
#
# rename-command CONFIG ""
#
# Please note that changing the name of commands that are logged into the
# AOF file or transmitted to replicas may cause problems.
(1)requirepass:設置redis連接密碼
(2)rename-command:命名重命名,對於一些危險的命令例如:
flushdb 清空數據庫
flushall 清空所有記錄
config 客戶端連接後可配置服務器
keys 客戶端連接後可查看所有存在的鍵
作爲服務端redis-server,常常需要禁用以上命令來使服務器更加安全,禁用的具體做法是:
remand-comamand FLUSHALL ""
也可以保留命名,但是不能輕易使用,重命名這個命名即可:
remand-command FLUSHALL adcde
這樣重啓服務器後則需要使用新命令來執行操作,否則服務器會報錯unknown command。
3.9 CLIENTS
################################### CLIENTS ####################################
# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
#
# maxclients 10000
(1)maxclients :設置客戶端最大併發連接數,默認無限制,Redis可以同時打開的客戶端連接數爲Redis進程可以打開的最大文件。 描述符數-32(redis server自身會使用一些),如果設置 maxclients爲0 。表示不作限制。當客戶端連接數到達限制時,Redis會關閉新的連接並向客戶端返回max number of clients reached錯誤信息。
3.10 MEMORY MANAGEMENT
############################## MEMORY MANAGEMENT ################################
# Set a memory usage limit to the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# according to the eviction policy selected (see maxmemory-policy).
#
# If Redis can't remove keys according to the policy, or if the policy is
# set to 'noeviction', Redis will start to reply with errors to commands
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
#
# This option is usually useful when using Redis as an LRU or LFU cache, or to
# set a hard memory limit for an instance (using the 'noeviction' policy).
#
# WARNING: If you have replicas attached to an instance with maxmemory on,
# the size of the output buffers needed to feed the replicas are subtracted
# from the used memory count, so that network problems / resyncs will
# not trigger a loop where keys are evicted, and in turn the output
# buffer of replicas is full with DELs of keys evicted triggering the deletion
# of more keys, and so forth until the database is completely emptied.
#
# In short... if you have replicas attached it is suggested that you set a lower
# limit for maxmemory so that there is some free RAM on the system for replica
# output buffers (but this is not needed if the policy is 'noeviction').
#
# maxmemory <bytes>
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
# is reached. You can select among five behaviors:
#
# volatile-lru -> Evict using approximated LRU among the keys with an expire set.
# allkeys-lru -> Evict any key using approximated LRU.
# volatile-lfu -> Evict using approximated LFU among the keys with an expire set.
# allkeys-lfu -> Evict any key using approximated LFU.
# volatile-random -> Remove a random key among the ones with an expire set.
# allkeys-random -> Remove a random key, any key.
# volatile-ttl -> Remove the key with the nearest expire time (minor TTL)
# noeviction -> Don't evict anything, just return an error on write operations.
#
# LRU means Least Recently Used
# LFU means Least Frequently Used
#
# Both LRU, LFU and volatile-ttl are implemented using approximated
# randomized algorithms.
#
# Note: with any of the above policies, Redis will return an error on write
# operations, when there are no suitable keys for eviction.
#
# At the date of writing these commands are: set setnx setex append
# incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd
# sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby
# zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby
# getset mset msetnx exec sort
#
# The default is:
#
# maxmemory-policy noeviction
# LRU, LFU and minimal TTL algorithms are not precise algorithms but approximated
# algorithms (in order to save memory), so you can tune it for speed or
# accuracy. For default Redis will check five keys and pick the one that was
# used less recently, you can change the sample size using the following
# configuration directive.
#
# The default of 5 produces good enough results. 10 Approximates very closely
# true LRU but costs more CPU. 3 is faster but not very accurate.
#
# maxmemory-samples 5
# Starting from Redis 5, by default a replica will ignore its maxmemory setting
# (unless it is promoted to master after a failover or manually). It means
# that the eviction of keys will be just handled by the master, sending the
# DEL commands to the replica as keys evict in the master side.
#
# This behavior ensures that masters and replicas stay consistent, and is usually
# what you want, however if your replica is writable, or you want the replica to have
# a different memory setting, and you are sure all the writes performed to the
# replica are idempotent, then you may change this default (but be sure to understand
# what you are doing).
#
# Note that since the replica by default does not evict, it may end using more
# memory than the one set via maxmemory (there are certain buffers that may
# be larger on the replica, or data structures may sometimes take more memory and so
# forth). So make sure you monitor your replicas and make sure they have enough
# memory to never hit a real out-of-memory condition before the master hits
# the configured maxmemory setting.
#
# replica-ignore-maxmemory yes
(1)maxmemory:設置客戶端最大併發連接數,默認無限制,Redis可以同時打開的客戶端連接數爲Redis進程可以打開的最大文件。 描述符數-32(redis server自身會使用一些),如果設置 maxclients爲0 。表示不作限制。當客戶端連接數到達限制時,Redis會關閉新的連接並向客戶端返回max number of clients reached錯誤信息。
(2)maxmemory-policy:當使用內存達到最大值時,redis使用的清除策略,有以下選擇:
① volatile-lru:利用LRU算法移除設置過過期時間的KEY(LRU:最近使用)。
② allkeys-lru:利用LRU算法移除任何key。
③volatile-random:移除設置過過期時間的隨機key。
④allkeys-random:移除隨機key。
⑤noeviction:不移除任何key,只是返回一個錯誤,默認選項。
(3)maxmemory-samples:LRU 和 minimal TTL 算法都不是精準的算法,但是相對精確的算法(爲了節省內存)。隨意你可以選擇樣本大小進行檢,redis默認選擇3個樣本進行檢測,你可以通過maxmemory-samples進行設置樣本數。
3.11 APPEND ONLY MODE
############################## APPEND ONLY MODE ###############################
# By default Redis asynchronously dumps the dataset on disk. This mode is
# good enough in many applications, but an issue with the Redis process or
# a power outage may result into a few minutes of writes lost (depending on
# the configured save points).
#
# The Append Only File is an alternative persistence mode that provides
# much better durability. For instance using the default data fsync policy
# (see later in the config file) Redis can lose just one second of writes in a
# dramatic event like a server power outage, or a single write if something
# wrong with the Redis process itself happens, but the operating system is
# still running correctly.
#
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
appendonly no
# The name of the append only file (default: "appendonly.aof")
appendfilename "appendonly.aof"
# The fsync() call tells the Operating System to actually write data on disk
# instead of waiting for more data in the output buffer. Some OS will really flush
# data on disk, some other OS will just try to do it ASAP.
#
# Redis supports three different modes:
#
# no: don't fsync, just let the OS flush the data when it wants. Faster.
# always: fsync after every write to the append only log. Slow, Safest.
# everysec: fsync only one time every second. Compromise.
#
# The default is "everysec", as that's usually the right compromise between
# speed and data safety. It's up to you to understand if you can relax this to
# "no" that will let the operating system flush the output buffer when
# it wants, for better performances (but if you can live with the idea of
# some data loss consider the default persistence mode that's snapshotting),
# or on the contrary, use "always" that's very slow but a bit safer than
# everysec.
#
# More details please check the following article:
# http://antirez.com/post/redis-persistence-demystified.html
#
# If unsure, use "everysec".
# appendfsync always
appendfsync everysec
# appendfsync no
# When the AOF fsync policy is set to always or everysec, and a background
# saving process (a background save or AOF log background rewriting) is
# performing a lot of I/O against the disk, in some Linux configurations
# Redis may block too long on the fsync() call. Note that there is no fix for
# this currently, as even performing fsync in a different thread will block
# our synchronous write(2) call.
#
# In order to mitigate this problem it's possible to use the following option
# that will prevent fsync() from being called in the main process while a
# BGSAVE or BGREWRITEAOF is in progress.
#
# This means that while another child is saving, the durability of Redis is
# the same as "appendfsync none". In practical terms, this means that it is
# possible to lose up to 30 seconds of log in the worst scenario (with the
# default Linux settings).
#
# If you have latency problems turn this to "yes". Otherwise leave it as
# "no" that is the safest pick from the point of view of durability.
no-appendfsync-on-rewrite no
# Automatic rewrite of the append only file.
# Redis is able to automatically rewrite the log file implicitly calling
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
#
# This is how it works: Redis remembers the size of the AOF file after the
# latest rewrite (if no rewrite has happened since the restart, the size of
# the AOF at startup is used).
#
# This base size is compared to the current size. If the current size is
# bigger than the specified percentage, the rewrite is triggered. Also
# you need to specify a minimal size for the AOF file to be rewritten, this
# is useful to avoid rewriting the AOF file even if the percentage increase
# is reached but it is still pretty small.
#
# Specify a percentage of zero in order to disable the automatic AOF
# rewrite feature.
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# An AOF file may be found to be truncated at the end during the Redis
# startup process, when the AOF data gets loaded back into memory.
# This may happen when the system where Redis is running
# crashes, especially when an ext4 filesystem is mounted without the
# data=ordered option (however this can't happen when Redis itself
# crashes or aborts but the operating system still works correctly).
#
# Redis can either exit with an error when this happens, or load as much
# data as possible (the default now) and start if the AOF file is found
# to be truncated at the end. The following option controls this behavior.
#
# If aof-load-truncated is set to yes, a truncated AOF file is loaded and
# the Redis server starts emitting a log to inform the user of the event.
# Otherwise if the option is set to no, the server aborts with an error
# and refuses to start. When the option is set to no, the user requires
# to fix the AOF file using the "redis-check-aof" utility before to restart
# the server.
#
# Note that if the AOF file will be found to be corrupted in the middle
# the server will still exit with an error. This option only applies when
# Redis will try to read more data from the AOF file but not enough bytes
# will be found.
aof-load-truncated yes
# When rewriting the AOF file, Redis is able to use an RDB preamble in the
# AOF file for faster rewrites and recoveries. When this option is turned
# on the rewritten AOF file is composed of two different stanzas:
#
# [RDB file][AOF tail]
#
# When loading Redis recognizes that the AOF file starts with the "REDIS"
# string and loads the prefixed RDB file, and continues loading the AOF
# tail.
aof-use-rdb-preamble yes
(1)appendonly:默認redis使用的是rdb方式持久化,這種方式在許多應用中已經足夠用了。但是redis如果中途宕機的話,會導致可能會有幾分鐘的數據丟失,根據save策略來進行持久化,Append Only File 是另一種持久化方式,可以提供更好的持久化特性。Redis會把每次寫入的數據在接收後都寫入appendonly.aof文件,每次啓動的時redis會把這個文件的數據讀入內存中,先忽略RDB文件,默認值爲no。
(2)appendfilename:aof文件名,默認是“appendonly.aof”。
(3)appendsync:aof持久化策略的配置。
①no表示不執行fsync,由操作系統保證數據同步到磁盤,速度最快。
②always表示每次執行都要fsync,以保證數據同步到磁盤。
③ everysec表示每秒執行一次fsync,可能會導致丟失這1s數據。
(4)no-appendfsync-on-rewrite:在aof重寫或者寫入rdb文件的時候,會執行大量IO,此時對於everysec和always的aof模式來說,執行fsync會造成阻塞過長時間,no-appendfsync-on-rewrite字段設置爲默認設置爲no。如果對延遲要求很高的應用,這個字段可以設置爲yes,否則還是設置爲no,這樣對持久化特性來說這是更安全的選擇。 設置爲yes表示rewrite期間對新寫操作不fsync,暫時存在內存中,等rewrite完成後再寫入,默認爲no,建議yes。Linux的默認fsync策略是30秒。可能丟失30秒數據。默認值爲no。
(5)auto-aof-rewrite-percentage:默認值爲100。aof自動重寫配置,當目前aof文件大小超過上一次重寫的aof文件大小的百分之多少進行重寫,即當aof文件增長到一定大小的時候,Redis能夠調用bgrewriteaof對日誌文件進行重寫。當前AOF文件大小是上次日誌重寫得到AOF文件大小的二倍(設置爲100)時,自動啓動新的日誌重寫過程。
(6)auto-aof-rewrite-min-size:64mb。設置允許重寫的最小aof文件大小,避免了達到約定百分比但尺寸仍然很小的情況還要重寫。
(7)aof-load-truncated:aof文件可能在尾部是不完整的,當redis啓動的時候,aof文件的數據被載入內存。重啓可能發生在redis所在的主機操作系統宕機後,尤其在ext4文件系統沒有加上data=ordered選項,出現這種現象 redis宕機或者異常終止不會造成尾部不完整現象,可以選擇讓redis退出,或者導入儘可能多的數據。如果選擇的是yes,當截斷的aof文件被導入的時候,會自動發佈一個log給客戶端然後load。如果是no,用戶必須手動redis-check-aof修復AOF文件纔可以。默認值爲 yes。
3.12 LUA SCRIPTING
################################ LUA SCRIPTING ###############################
# Max execution time of a Lua script in milliseconds.
#
# If the maximum execution time is reached Redis will log that a script is
# still in execution after the maximum allowed time and will start to
# reply to queries with an error.
#
# When a long running script exceeds the maximum execution time only the
# SCRIPT KILL and SHUTDOWN NOSAVE commands are available. The first can be
# used to stop a script that did not yet called write commands. The second
# is the only way to shut down the server in the case a write command was
# already issued by the script but the user doesn't want to wait for the natural
# termination of the script.
#
# Set it to 0 or a negative value for unlimited execution without warnings.
lua-time-limit 5000
(1)lua-time-limit:一個lua腳本執行的最大時間,單位爲ms。默認值爲5000。
3.13 Redis CLUSTER
################################ REDIS CLUSTER ###############################
# Normal Redis instances can't be part of a Redis Cluster; only nodes that are
# started as cluster nodes can. In order to start a Redis instance as a
# cluster node enable the cluster support uncommenting the following:
#
# cluster-enabled yes
# Every cluster node has a cluster configuration file. This file is not
# intended to be edited by hand. It is created and updated by Redis nodes.
# Every Redis Cluster node requires a different cluster configuration file.
# Make sure that instances running in the same system do not have
# overlapping cluster configuration file names.
#
# cluster-config-file nodes-6379.conf
# Cluster node timeout is the amount of milliseconds a node must be unreachable
# for it to be considered in failure state.
# Most other internal time limits are multiple of the node timeout.
#
# cluster-node-timeout 15000
# A replica of a failing master will avoid to start a failover if its data
# looks too old.
#
# There is no simple way for a replica to actually have an exact measure of
# its "data age", so the following two checks are performed:
#
# 1) If there are multiple replicas able to failover, they exchange messages
# in order to try to give an advantage to the replica with the best
# replication offset (more data from the master processed).
# Replicas will try to get their rank by offset, and apply to the start
# of the failover a delay proportional to their rank.
#
# 2) Every single replica computes the time of the last interaction with
# its master. This can be the last ping or command received (
(1)cluser-enabled:集羣開關,默認是不開啓集羣模式。
(2)cluster-config-file:集羣配置文件的名稱,每個節點都有一個集羣相關的配置文件,持久化保存集羣的信息。 這個文件並不需要手動配置,這個配置文件有Redis生成並更新,每個Redis集羣節點需要一個單獨的配置文件。請確保與實例運行的系統中配置文件名稱不衝突。默認配置爲nodes-6379.conf。
(3)cluster-node-timeout :可以配置值爲15000。節點互連超時的閥值,集羣節點超時毫秒數。
(4)cluster-slave-validity-factor :可以配置值爲10。在進行故障轉移的時候,全部slave都會請求申請爲master,但是有些slave可能與master斷開連接一段時間了, 導致數據過於陳舊,這樣的slave不應該被提升爲master。該參數就是用來判斷slave節點與master斷線的時間是否過長。判斷方法是:比較slave斷開連接的時間和(node-timeout * slave-validity-factor) + repl-ping-slave-period 如果節點超時時間爲三十秒, 並且slave-validity-factor爲10,假設默認的repl-ping-slave-period是10秒,即如果超過310秒slave將不會嘗試進行故障轉移。
(5)cluster-migration-barrier :可以配置值爲1。master的slave數量大於該值,slave才能遷移到其他孤立master上,如這個參數若被設爲2,那麼只有當一個主節點擁有2 個可工作的從節點時,它的一個從節點會嘗試遷移。
(6)cluster-require-full-coverage:默認情況下,集羣全部的slot有節點負責,集羣狀態才爲ok,才能提供服務。 設置爲no,可以在slot沒有全部分配的時候提供服務。不建議打開該配置,這樣會造成分區的時候,小分區的master一直在接受寫請求,而造成很長時間數據不一致。
四.持久化
很多時候我們需要持久化數據也就是將內存中的數據寫入到硬盤裏面,大部分原因是爲了之後重用數據(比如重啓機器、機器故障之後恢復數據),或者是爲了防止系統故障而將數據備份到一個遠程位置。
Redis提供了兩種不同的持久化方法來講數據存儲到硬盤裏面。一種方法叫快照(snapshotting),它可以將存在於某一時刻的所有數據都寫入硬盤中。另一種方法叫只追加文件(append-only file ,AOF),它會在執行寫命令時,將被執行的命令複製到硬盤裏面。快照是一次全量備份,AOF日誌是連續的增量備份。快照是內存數據的二進制序列化形式,在存儲上非常緊湊,而AOF日誌記錄的是內存數據修改的指令記錄文本。AOF日誌在長期運行過程中會變得無比龐大,數據庫重啓時需要加載AOF日誌進行指令重放,這個事件就會無比漫長,所以需要定期進行AOF重寫,給AOF日誌瘦身。
這兩種持久化方法既可以同時使用,又可以單獨使用,在某些情況下設置可以兩種方法都不使用,具體選擇哪種持久化方法需要根據用戶的數據以及應用來決定。
4.1 快照持久化
Redis可以通過創建快照來獲得存儲在內存裏面的數據在某個時間點上的副本。在創建快照之後,用戶可以對快照備份,可以將快照複製到其他服務器從而創建具有相同數據的服務器副本,還可以將快照留在原地以便重啓服務器使用。
4.1.1 快照原理
我們知道Redis是單線程程序,這個線程要同時負責多個客戶端套接字的併發讀寫操作和內存結構的邏輯讀寫。
在服務線上請求的同時,Redis還需要進行內存快照,內存快照要求Redis必須進行文件IO操作,可文件IO操作是不能使用多路複用API。這意味着單線程同時在服務線上的請求還要進行文件IO操作,文件IO操作會嚴重拖垮服務器請求的性能。還有個重要的問題是爲了不阻塞線上的業務,就需要邊持久化邊響應客戶端的請求。持久化的同時,內存數據結構還在改變,比如一個大型的hash字典正在持久化,結果一個請求過來把它給刪掉了,還沒持久化完,這要怎麼辦?
Redis使用操作系統的COW(Copy On Write)機制來實現快照持久化。
Redis在持久化時會調用fork函數(這個函數有兩個返回值,將子進程的PID返回給父進程,在子進程中返回0)產生一個子進程,快照持久化完全交給子進程來處理,父進程繼續處理客戶端請求。子進程剛剛產生時,它會父進程共享內存裏面的代碼段和數據段。子進程做數據持久化,它不會改變現存的內存數據結構,它只是對數據結構進行遍歷,然後序列化寫到磁盤中。但父進程不一樣,它必須持續服務客戶端請求,然後對內存結構進行不間斷的修改。這個時候就會使用操作系統的COW機制來進行數據段頁面的分離。數據段是由很多操作系統的頁面組合而成,當父進程對其中的一個頁面進行修改時,會將被共享的頁面複製一份分離出來,然後對這個複製頁面進行修改。這時的子進程相應的頁面是沒有變化的,還是進程產生時那一瞬間的數據。
隨着父進程修改操作的持續進行,越來越多的共享頁面被分離出來,內存就會持續增長。但是也不會超過原有數據內存的2倍大小,另外一個Redis實例裏的冷數據佔得比例往往是比較高的,所以很少會出現所有的頁面被分離,被分離的往往只有其中一部分的頁面。
子進程因爲數據沒有變化, 它能看到的內存裏的數據再進程產生的一瞬間就凝固了,再也不會改變什麼了。這也是爲什麼Redis的持久化叫快照的原因。接下來子進程就可以安心遍歷數據了進行序列化寫磁盤。
Redis會單獨創建一個子進程來進行持久化,會先將數據寫到一個臨時文件,待持久化結束後,用這個臨時文件去替換上次持久化好的文件。整個過程中,主進程是不進行任何IO操作,這就確保了極高的性能。如果需要進行大規模數據的恢復,且對數據恢復的完整性不是很敏感的話,RDB比AOF方式更加高效。
4.1.2 配置位置
查看3.6節的配置文件說明。
4.1.3 如何觸發RDB快照
- 配置文件中默認的快照配置。
- 客戶端可以通過向Redis發送SAVE命令來創建一個快照,接到Save的Redis服務器在快照創建完畢之前將不再響應任何其他命令。Save命令並不常用,我們通常只會在沒有足夠內存去執行BGSAVE命令的情況下,又或者即使等待持久化操作執行完畢也無所謂的情況下,纔會執行這個命令。
- 客戶端可以通過向Redis發送BGSAVE命令來創建一個快照。對於支持BGSAVE命令的平臺來說,Redis會調用fork來創建一個子進程,然後子進程負責將快照寫入硬盤,而父進程則繼續處理命令請求。
- 當Redis通過SHUTDOWM命令接收到關閉服務器請求時,或者接收到標準的TERM信號時,會執行一個SAVE命令。阻塞所有客戶端,不再執行客戶端發送的任何請求,並在SAVE命令執行完畢之後關閉服務器。
- 當一個服務器連接另一個服務器,並向對方發送SYNC命令開始一次複製的時候,如果主服務器目前沒有在執行BGSAVE操作,或者主服務器並非剛剛執行完BGSAVE操作,那麼主服務器將會執行BGSAVE命令。
- 當執行flushall命令,也會產生dump.rdb文件,但是文件是空的,沒有意義。
4.1.4 如何恢復RDB快照
將備份文件dump.rdb文件移動到redis安裝目錄下並啓動服務即可(CONFIG GET dir獲取目錄)。
4.1.5 如何停止快照
動態停止RDB保存規則的方法:redis-cli config set save ""
4.1.6 快照的優缺點
優點:
- 適合大規模數據恢復
- 對數據完整性和一致性要求不是很高
缺點:
- 在一定間隔時間做一次備份,如果Redis意外的關掉的話,就會丟失最後一次快照後的修改
- 創建子進程的時候,內存中的數據被克隆了一份,大致2倍的膨脹性需要考慮。
4.1.7 小總結
4.2 AOF持久化
AOF持久化就是以日誌的形式來記錄每個寫操作,將Redis執行過的所有寫操作記錄下來,只允許追加文件不可以更改文件,Redis啓動之初會讀取該文件來重構數據。換言之,Redis只要從頭到尾重新執行一次AOF文件包含的所有寫命令,就可以恢復AOF文件所記錄的數據集。
4.2.1 AOF原理
AOF日誌存儲的是Redis服務器順序指令序列,AOF日誌只記錄對內存進行修改的指令記錄。
假設AOF日誌記錄了自Redis實例創建以來的所有修改性指令序列,那麼既可以通過對一個空的Redis實例順序執行所有的指令,也就是重放,來恢復Redis當前實例的內存數據結構的狀態。
Redis會在收到客戶端修改指令後,先進行參數校驗,如果沒有問題,就立即將該指令文件存儲到AOF日誌中,也就是存儲到磁盤中,然後再執行指令。這樣就是遇到了突發宕機,已經存儲到AOF日誌的指令進行重放以下就可以恢復到宕機前的狀態。
4.2.2 配置位置
參考3.11配置文件說明
4.2.3 AOF正常狀態和異常狀態下的啓動恢復
1.正常狀態下:
- 啓動:修改3.11配置文件的appendonly爲yes
- 將有數據的aof文件複製一份保存到對應的目錄(config get dir)
- 恢復:重啓Redis重新加載服務
2.異常狀態下:
- 啓動:修改3.11配置文件的appendonly爲yes
- 備份被寫壞的aof文件
- 修復:Redis -check-aof--fix進行修復
- 恢復:重啓Redis重新加載服務
4.2.4 重寫/壓縮AOF文件
Redis會不斷地將被執行的寫命令記錄到AOF文件裏面,所以隨着Redis的不斷運行,AOF文件的體積也在不斷的增長。在極端情況下,體積不斷增大的AOF文件甚至可能會用完硬盤裏面的所有可用空間。還有一個問題是,因爲Redis在重啓之後需要重新執行AOF文件的所有寫命令來還原數據集,所以如果AOF文件的體積非常大,那麼還原操作的時間就可能會非常長。
爲了解決AOF文件體積不斷增大的問題用戶可以向Redis發送BGREWRITEAOF命令,這個命令會通過移除AOF文件中冗餘命令來重寫AOF文件,使AOF文件的體積變得儘可能的小。
Redis提供了BGREWRITEAOF指令用於對AOF日誌進行瘦身。其原理就是開闢一個子進程對內存進行遍歷轉換成一系列redis操作指令,序列化到一個新的AOF日誌文件中。序列化完畢後再將操作期間發生的增量AOF日誌追加到新的AOF日誌文件中,追加完畢後就立即代替舊的AOF日誌文件,瘦身工作就完成了。
在執行 BGREWRITEAOF 命令時,Redis 服務器會維護一個 AOF 重寫緩衝區,該緩衝區會在子進程創建新AOF文件期間,記錄服務器執行的所有寫命令。當子進程完成創建新AOF文件的工作之後,服務器會將重寫緩衝區中的所有內容追加到新AOF文件的末尾,使得新舊兩個AOF文件所保存的數據庫狀態一致。最後,服務器用新的AOF文件替換舊的AOF文件,以此來完成AOF文件重寫操作。
AOF日誌是以文件的形式存在的,當程序對AOF日誌文件進行寫操作時,實際上是將內容寫到了內核爲文件描述符分配的一個內存緩存中,然後內核會異步將髒數據刷回到磁盤中的。這就意味這如果機器突然宕機,AOF日誌內容還沒有來得及刷到磁盤中,這個時候就會出現日誌丟失,那麼該怎麼辦?
Linux的glibc提供了fsync(int fd)函數可以將指定we年的內容強制從內核緩存中刷到磁盤。只要Redis進程實時調用fsync函數就可以保證aof日誌不丟失。但是fsync是一個磁盤IO操作,運行十分的慢。所以在生產環境的服務器中,Redis通常是每隔1s左右執行一次fsync操作,週期1s是可以配置的。這是在數據安全性和性能之間做了一個折中,在保持高性能的同時,儘可能使得數據少丟失。Redis同樣也提供了兩種策略,一種是永不fsync-讓操作系統來決定合適同步磁盤,很不安全。另一個是來一次指令就fsync一次-非常慢,但是在生產環境中基本不會使用。
重寫觸發的時機是Redis會記錄上次重寫AOF時文件的大小,默認配置是當AOF文件大小是上次重寫後大小的一倍且文件大於64M時觸發。(AOF持久化可以通過設置auto-aof-rewrite-percentage選項和auto-rewrite-min-size選項。上面說的默認配置就是auto-aof-rewrite-percentage 100和auto-rewrite-min-size 64)如果AOF重寫執行得太過頻繁的話,用戶可以考慮將auto-aof-rewrite-percentage選項的值設置到100以上,這樣的做法可以讓Redis在AOF文件體積變得更大之後才執行重寫操作,不過也會讓Redis啓動還原數據集所需的時間變得更長。
4.2.5 AOF的優缺點
缺點:
- 相同數據集的數據而言aof文件遠遠大於rdb文件,恢復速度遠慢與rdb
- AOF運行的速率要慢與RDB,每秒同步策略效率更好,不同步效率與rdb相同
4.2.6小總結
4.3 運維
快照是通過開啓子進程方法進行的,它是一個比較耗資源的操作。
1.遍歷整個內存,大塊寫磁盤會加重系統負載
2.AOF的fsync是一個耗時的IO操作,它會降低Redis性能,同時會增加系統IO負擔。
所以通常Redis的主節點是不會進行持久化操作,持久化操作主要是在從節點進行。從節點是備份節點,沒有來自客戶端請求的壓力,它的操作系統資源往往是比較充沛。
但是如果出現網絡分區,從節點長期連不上主節點,就會出現數據不一致的問題,特別是在網絡分區出現的情況下又不小心主節點宕機了,那麼數據就會丟失,所以在生產環境中要做好實時監控工作,保證網絡暢通或者能快速修復。另外還應該增加一個節點以降低網絡分區的概率,只要有一個從節點數據同步正常,數據也就不會輕易丟失。
4.4 總結
4.4.1同時開啓兩種持久化
在這種情況下,當redis重啓時會優先載入AOF文件恢復原始數據。因爲在這種情況下,AOF文件保存的數據要比RDB文件保存的數據完整。RDB數據不實時,同時使用兩者時服務器重啓時也只會找AOF文件。
那麼可以只用AOF文件嗎?建議是不要,RDB更適合備份數據庫(AOF在不斷變化不好備份)。萬一AOF存在bug,有個備份也是好的。
性能建議:因爲RDB文件只用作後備用途,建議只在Slave上持久化RDB文件,而且只要15分鐘備份一次就夠了,只保留save 900 1這條規則。如果Enalbe AOF,好處是在最惡劣情況下也只會丟失不超過兩秒數據,啓動腳本較簡單隻load自己的AOF文件就可以了。代價一是帶來了持續的IO,二是AOF rewrite的最後將rewrite過程中產生的新數據寫到新文件造成的阻塞幾乎是不可避免的。只要硬盤許可,應該儘量減少AOF rewrite的頻率,AOF重寫的基礎大小默認值64M太小了,可以設到5G以上。默認超過原大小100%大小時重寫可以改到適當的數值。如果不Enable AOF ,僅靠Master-Slave Replication 實現高可用性也可以。能省掉一大筆IO也減少了rewrite時帶來的系統波動。代價是如果Master/Slave同時倒掉,會丟失十幾分鐘的數據,啓動腳本也要比較兩個Master/Slave中的RDB文件,載入較新的那個。新浪微博就選用了這種架構。
4.4.2 混合持久化
我們知道,在重啓Redis時,很少使用rdb來恢復內存狀態,因爲會丟失大量數據。我們通常使用AOF日誌重放,這樣在Redis實例很大的情況下,啓動需要花費很長的時間。
Redis4.0爲了解決這個問題,帶來了一個新的持久化選項--混合持久化。將rdb文件的內容和增量的AOF日誌文件放在一起。這裏的AOF日誌不再是全量的日誌,而是自持久化開始到持久化結束這段時間發生的增量AOF日誌,通常這部分AOF日誌很小。
於是在Redis重啓的時候,可以先加載rdb的內容,然後再重放增量AOF日誌就可以完全替代之前的AOF全量文件重放,重啓效率得到了大幅提升。
五.事務
Redis的事務可以一次性執行多個命令,本質上是一組命令的集合。一個事務中的所有命令都會被序列化,按順序串行執行而不會被其他命令插入,不許加塞。
5.1. 基本命令和階段
有三個 階段:
1.開啓:以MULTI開啓一個事務
2.入隊:將多個命令入隊到事務中,接收到這些命令並不會立即執行,而是放到等待執行的事務隊列中
3.執行:以EXEC命令觸發事務執行
所有的指令在exec之前不執行,而是緩存在服務器中的一個事務隊列中,服務器一旦受到exec指令,纔開始執行整個事務隊列,執行完畢後一次性返回所有執行的運行結果。因爲Redis的單線程特性,它不用擔心自己在執行隊列的時候被其他指令打攪,可以保證他們得到“原子性”執行。
Redis爲事務提供了一個discard指令,用於丟棄事務緩存隊列中的所有指令,在exec之前。
5.2 原子性
事務的原子性指的是事務要麼去全部成功,要麼全部失敗,那麼Redis事務執行時原子性的嗎?
上面的例子是事務執行到中間遇到了失敗,因爲我們不能對一個字符串進行數學運算,事務在遇到指令執行失敗後,後面的指令還繼續執行。
通過上面的例子,應該明白Redis的事務根本不能算原子性,僅僅滿足了事務的隔離性,隔離性中的串行化--當前執行的事務有着不被其他事務打斷的權利。
5.3 Watch
5.3.1 悲觀鎖/樂觀鎖/CAS
悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數據的時候都認爲別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關係型數據庫裏邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量。樂觀鎖策略:提交版本必須大於記錄當前版本才能執行更新。
樂觀鎖的一種典型實現機制(CAS):樂觀鎖主要就是兩個步驟:衝突檢測和數據更新。當多個線程嘗試使用CAS同時更新同一個變量時,只有一個線程可以更新變量的值,其他的線程都會失敗,失敗的線程並不會掛起,而是告知這次競爭中失敗了,並可以再次嘗試。
5.3.2 業務背景
考慮到一個業務背景,Redis中存儲了我們信用卡,這張信用卡有初始額度和欠額。我們需要知道的是,這兩個操作一定是在一個事務內進行的,操作如下:
但是在這個事務開始之前就請求對這個balance進行改變,那麼進行事務成功,這個數額也是具有差異的。
5.3.3 watch的使用
watch會在事務開始之前盯住1個或多個關鍵變量,當事務執行時,也就是服務器收到exec指令要順序執行緩存事務隊列時,Redis會檢查關鍵變量自watch之後,是否被修改了(包括當前事務所在的客戶端)。如果關鍵變量被人動過了,exec指令就會返回null回覆客戶端事務執行失敗,這個時候客戶端一般會選擇重試。
下面先來看無加塞篡改,先監控在開啓multi:
有加塞篡改:
監控了key,key一旦被修改,後面的事務就失效了。當服務器給exec指令返回一個null回覆時,客戶端知道了事務執行時失敗的,通常客戶端(redis-py)都會拋出一個WatchError這種錯誤,不過也有些語言(Jedis)不會拋出異常,而是通過在exec方法裏返回一個null,這樣客戶端需要檢查一個返回結果是否爲null來確定事務時候執行失敗。
注意事項:
- Redis禁止在multi和exec之間執行watch指令,而必須在multi之前做好盯住關鍵變量,否則會出錯。
- 一旦執行了exec,之前加的監控鎖都會被去掉
5.3.4 unwatch
5.4 特性
- 單獨的隔離操作:事務的所有命令都會序列化,按順序執行。事務在執行的過程中,不會被其他客戶端發送來的請求所打動。
- 沒有隔離級別的概念:隊列中命令沒有提交之前都不會實際的被執行,因爲事務提交前任何的命令都不會實際被執行,也就不存在“事務內的查詢要看到事務的更新,在事務外查詢不能被看到”這個問題。
- 不保證原子性:Redis同一個事務中有一個命令執行失敗,其他命令仍會被執行,沒有回滾。
5.5 爲什麼Redis不支持回滾?
Redis 命令只會因爲錯誤的語法而失敗(並且這些問題不能在入隊時發現),或是命令用在了錯誤類型的鍵上面:這也就是說,從實用性的角度來說,失敗的命令是由編程錯誤造成的,而這些錯誤應該在開發的過程中被發現,而不應該出現在生產環境中。因爲不需要對回滾進行支持,所以 Redis 的內部可以保持簡單且快速。
六.複製(主從複製)
複製可以讓其他服務器擁有一個不斷地更新的數據副本,從而使得擁有數據副本的服務器可以用於處理客戶端發送的請求。關係數據庫通常會使用一個主服務器向多個從服務器發送更新,並使用從服務器來處理所有讀請求。Redis也採用了同樣的方法來實現自己複製特性,並將其用作擴展性能的一種手段。
在需要擴展讀請求的時候,或者需要寫入臨時數據的時候,用戶可以通過設置額外的Redis從服務器來保存數據集的副本。在接收到主服務器發送的數據初始副本之後,客戶端每次想主服務器進行寫入,從服務器都會實時得到更新。在部署好主服務器之後,客戶端就可以向任意一個從服務發送請求了,而不必再像之前一樣,總是把每個讀請求發送給主服務器(客戶端通常會隨機選擇使用哪個從服務器,從而將負載平均分配到各個從服務器上)。
6.1 原理
6.1.1 最終一致
之前瞭解了CAP原理和BASE原理,在此基礎上,Redis的主從複製是異步同步的, 所以分佈式的Redis系統並不滿足一致性要求,當客戶端在Redis的主節點修改了數據後,立即返回,即使在主從網絡斷開的情況下,主節點依舊可以正常對外提供修改服務,所以Redis滿足可用性。
Redis保證最終一致性,從節點會努力追趕主節點,最終從節點的狀態會和主節點的狀態保持一致。如果網絡斷開了,主從節點的數據將會出現大量不一致,一旦網絡恢復,從節點會採用多種策略努力追趕上落後的數據,繼續盡力保持和主節點一致。
6.1.2 主從同步
Redis同步支持主從同步和從從同步。salve啓動成功連接到master會發送一個sync命令。
1.全量複製
Master接到命令啓動後臺的存盤進程,同時收集所有接受到用於修改數據集的命令,在後臺進程執行完畢之後,master將傳送整個數據文件給slave,以完成一次完全同步(這個過程會在下面從服務器連接主服務時步驟會詳細講解),而slave服務接收到數據庫文件數據後,將其存盤並加載到內存中。但只要是重新連接master,一次完全同步(全量複製)將被自動執行。
2.增量同步:Master繼續將新的所有收集到的修改命令依次傳給slave,完成同步。
Redis同步的是指令流,主節點會將那些對自己的狀態產生修改性影響的指令記錄在本地的內存buffer中,然後異步將buffer中的指令同步到從節點,從節點一邊執行同步的指令流達到和主節點一樣的狀態,一邊向主節點反饋自己同步到哪裏了(偏移量)。
因爲內存的buffer是有限的,所以Redis主庫不能將所有的指令都記錄在內存buffer中。Redis的複製內存buffer是一個定長的環形數組,如果數組內容滿了,就會從頭開始覆蓋前面的內容。如果因爲網絡狀況不好,從節點在短時間無法和主節點進行同步,那麼當網絡狀況恢復時,Redis的主節點那些沒有同步的指令在buffer中有可能已經被後續的指令覆蓋掉,從節點將無法直接通過指令流來進行同步,這個時候就需要更加複雜的同步機制--快照同步。
3.快照同步
快照同步是一個非常耗費資源的操作,它首先需要在主庫進行一次BGSAVE將當前內存的數據全部快照到磁盤文件中,然後再將快照文件的內容全部傳送給從節點。從節點將快照文件接收完畢後,立即執行一次全量加載,加載之前首先先將當前內存的數據情況,加載完畢後通知主節點繼續進行增量同步(使用配置文件的SLAVEOF配置選項的同步)。
在整個快照同步進行的過程中,主節點的複製buffer還在不停的往前移動,如果快照同步的時間過長或者複製buffer太小,都會導致同步期間的增量指令在複製buffer中被覆蓋,這樣就會導致快照同步完成後無法進行增量複製,然後會再次發起快照同步,如此極有可能會陷入快照同步的死循環。
所以務必配置一個合適的複製buffer大小參數,避免快照複製的死循環。
當從節點剛剛加入到集羣時,它必須先要進行一次快照同步,同步完成後再繼續進行增量同步
4.無盤複製
主節點在進行快照同步時,會進行很重的文件IO操作,特別是對於非SSD磁盤進行存儲時,快照會對系統的負載產生較大影響。。特別是當系統正在進行AOF的fsync操作時如果發生快照,fsync將會被推遲執行,這就會嚴重影響主節點的服務效率。
所以從Redis2.8.18版開始支持無盤複製。所謂無盤複製是指主服務器直接通過套接字將快照內容發送到從節點,生成快照是一個遍歷的過程,主節點會一邊遍歷內存,一邊將序列化的內容發送給從節點,從節點還是跟之前一樣,先將接收到的內容存儲到磁盤文件中,再進行一次性加載。
6.2 基本操作
6.2.1 實驗
1.配從庫不配主庫(拷貝多個redis.conf文件,並修改daemonize爲yes,然後修改PID名,端口號,log文件名字和Dumb.rdb名字)。
2.從庫配置:slaveof 主庫IP 主庫端口 (要注意的是每次與主庫斷開後,都需要重新連接,除非你配置redis.conf文件)
3.Info replication
主機:
從機1:
從機2:
6.2.2 從服務器連接主服務器時的步驟
步驟 | 主服務器操作 | 從服務器操作 |
1 | 等待命令進入 | 連接(或者重連接)主服務器,發送sync命令 |
2 | 開始執行BGSAVE,並用緩衝區記錄BGSAVE之後執行的所有寫命令 | 根據配置選項來決定是繼續使用現有的數據(如果的有的話)來處理客戶端的命令請求,還是向發送請求的客戶端返回錯誤 |
3 | BGSAVE執行完畢,向從服務器發送快照文件,並在發送期間繼續使用緩衝區記錄被執行的寫命令 | 丟失所有舊數據(如果有的話),開始載入主服務器發來的快照文件 |
4 | 快照文件發送完畢,開始向服務器發送存儲在緩衝區裏面的寫命令 | 完成對快照文件的解釋操作,像往常一樣接收命令請求 |
5 | 緩衝區的寫操作發送完畢;從現在開始每執行一個寫命令,就會向服務器發送相同的寫命令 | 執行主服務發來的所有存儲在緩衝區的寫命令;並從現在開始,接收並執行主服務器傳來的每個寫命令 |
通過使用上表中的方法,Redis在複製期間也會盡可能處理接收到的命令請求,但是,如果主從服務器之間的網絡帶寬不足,或者主服務器沒有足夠的內存來創建子進程和創建記錄寫命令的緩衝區,那麼Redis處理命令請求的效率就會受到影響。因此,儘管這並不是必須的,但在實際中最好還是讓主服務只使用50%~65%的內存,留下30%~45%的內存用於執行BGSAVE命令和創建寫命令的緩衝區。
設置從服務器的步驟非常簡單,用戶既可以通過配置選項SLAVEOF host port來將一個Redis服務器設置爲從服務器,又可以通過向運行中的Redis服務器發送SLAVEOF命令來將其設置爲從服務器。如果用戶使用的是SLAVEOF配置選項,那麼Redis在啓動時首先會載入當前可用的任何快照文件或者AOF文件,然後連接主服務器並執行上表的複製過程。如果用戶使用的是SLAVEOF命令,那麼Redis會立即嘗試連接主服務器,並在連接成功之後,開始執行上表所示的複製過程。
注意事項:
- 從服務器在進行同步時,會清空自己的所有數據。
- Redis不支持主主複製
6.2.3 一主二僕
基本的設置如上圖的實驗,6370模擬主機,6380和6381模擬從機
1.切入點問題,slave1、slave2是從頭開始複製還是從切入點開始複製?
在設置從機前,主機先存放一個數據:
然後設置從機連接主機,然後獲得從機數據:
從上面的實驗就可以知道,從機是從頭開始複製主機數據。
2 從機是否可以寫?set可否?
從機只能讀取主機數據,不能寫。
3 主機shutdown後情況如何?從機是上位還是原地待命
主機突然宕機,從機原地待命。
4 主機又回來了後,主機新增記錄,從機還能否順利複製?
重啓主機,在主機中設置數據,從機仍然可以獲得。
5 其中一臺從機down後情況如何?依照原有它能跟上大部隊嗎?
從機宕機,重啓後,從機將變成與之間主機無關的服務器。在上面說過,從機於主機斷開,都需要重新連接。
6.2.4 薪火相傳
上一個slave可以是下一個slave的master,slave同樣可以接受其他slave的連接和同步請求,那麼slave作爲下一個master,可以減輕master的寫壓力。
從服務器對從服務器進行復制在操作上和從服務器對主服務器進行復制的唯一區別在於,如果從服務器X擁有從服務器Y,那麼當從服務器執行從服務器連接主服務器時的步驟4時,它將斷開從服務器Y的連接,導致從服務器Y需要重新連接並重新同步。
當讀請求的重要性明顯高過寫請求的重要性,並且讀請求的數量遠遠超於寫請求的重要性,並且讀請求的數量遠遠超過一臺Redis服務器可以處理的範圍時,用戶就需要添加新的從服務器來處理請求。隨着負載不斷上升,主服務器可能會無法快速地更新所有從服務器,或者因爲重新連接和重新同步從服務器而導致系統超載。爲了緩解這個問題,用戶可以創建一個由Redis主從節點組成的中間層來分擔主服務器的複製工作,如下圖所示。
儘管主從服務器之間並不一定像圖中所示的一個樹狀結構,但是理解這種樹狀結構對Redis複製是可行的並且是合理的。在之前的持久化節中介紹過,AOF持久化的同步選項可以控制數據丟失的時間長度:通過將每個寫命令同步到硬盤裏面,用戶可以不損失任何數據(除非系統崩潰或者硬盤驅動器損壞),但這種做法會對服務器的性能造成影響;另一方面,如果用戶將同步的頻率設置爲每秒一次,那麼服務器的性能將回到正常水平,但故障可能會造成1s的數據丟失。通過同時使用複製和AOF持久化,可以將數據持久化到多臺機器上。
爲了將數據保存到多臺機器上面,用戶首先需要爲主服務器設置多個從服務器,然後對每個從服務器設置appendonly yes選項和appendfsync everysec選項(如果有需要的話,也可以對主服務進行相同的設置),這樣的話,用戶就可以讓多臺服務器以每秒一次的頻率將數據同步在硬盤上。但這還只是第一步:因爲用戶還必須等待主服務器發送的寫命令達到從服務器,並在執行後續操作之前,檢查數據是否已經被同步到硬盤裏面。
6.2.5 反客爲主
slaveof no one:使當前數據庫停止與其他數據庫的同步,轉爲主數據庫。
6.3 哨兵模式Sentinel
目前講的Redis還只是主從方案,最終一致性。如果主節點凌晨2點突發宕機怎麼辦?等運維手工進行從主切換,然後再通知所有的程序吧地址統統改一遍再重新上線?毫無疑問,這樣的人工效率太低了,所以我們必須有一個高可用的方案來抵抗節點故障,當故障發生時可以自動進行從主切換,程序可以不用重啓。Redis官方提供了這樣的一種方案--Redis Sentinel(哨兵)。
Redis Sentinel集羣一般是由3~5個節點組成,這樣掛了個別節點集羣還可以正常運行。
它負責持續監控主從節點的健康,當主節點掛掉時,自動選擇一個最優的從節點轉換爲主節點。客戶端來連接集羣時,會首先連接Sentinel,通過sentinel來查詢主節點的地址,然後再去連接主節點記性數據交互。當主節點發生故障時,客戶端會重新向Sentinel要地址,sentinel會將最新的主節點地址告訴客戶端。如此應用程序將無需重啓即可自動完成節點切換。比如上圖的主節點掛掉後,集羣將可能自動調整爲下圖所示結構。
從這張圖中我們可以看到主節點掛掉了,原先的主從複製也斷開了,客戶端和損壞的主節點也斷開了。從節點被提升爲新的主節點,其他從節點開始和新的主節點建立複製關係。客戶端通過新的主節點繼續進行交互。Sentinel會持續監控已經掛掉了主節點,待它恢復後,集羣會調整爲下面這張圖。
此時原先掛掉的主節點現在變成了從節點,從新的主節點那裏建立複製關係。
6.3.1 消息丟失
Redis主從採用異步複製,意味着當主節點掛掉時,從節點可能沒有收到全部的同步消息,這部分的未同步消息就丟失了。如果主從延遲特別大,那麼丟失的數據就可能會特別多。Sentinel無法保證數據完全不丟失,但是也儘可能保證數據少丟失。它有兩個選項可以限制主從延遲過大。
min-slaves-to-write 1
min-slaves-max-lag 10
第一個參數表示主節點必須有一個從節點在進行正常複製,否則就停止對外寫服務,喪失可用性。
何爲正常複製,何爲異常複製?這個就是由第二個參數控制的,它的單位是秒,表示10s沒有收到從節點的反饋,就意味着從節點同步不正常,要麼網絡斷開,要麼一直沒有給反饋。
6.3.2 Sentinel的使用
上面舉例是Sentinel集羣,在下面的基本使用的是單個哨兵。
1.主從複製,還是上面的實驗一樣,6379爲主機,6380和6381爲從機。
2.在redis安裝目錄下(或者是自定義的Redis目錄下)新建sentinel.conf問價,這個文件名絕對不能寫錯
3.配置哨兵,填寫內容:sentinel monitor 被監控的數據庫名字(自己起名字)127.0.0.1 6379 1(這裏的1表示主機掛掉後slave投票看誰稱爲成爲主機)。
4.啓動哨兵:Redis-sentinel /目錄/sentinel.conf
問題:如果之前的master重啓回來,會發生雙Master衝突?
從圖中可以看出,原Master重啓回來會變成現任Master的從機。
6.4 複製的缺點
複製延時:由於所有的寫操作都是先在Master上操作,然後同步更新到Slave上,所以從Master同步到Slave機器有一定的延遲,當系統很繁忙的時候,延遲問題會更加嚴重,Slave機器數量的增加也會使這個問題更加嚴重。
七.發佈訂閱
進程的一種消息通信模式:發送者(pub)發送消息,訂閱者(sub)接收消息。一般來說,發佈和訂閱的特點是訂閱者(listener)負責訂閱頻道(channel),發送者(publisher)負責向頻道發送二進制字符串消息。每當有消息被髮送給定頻道時,都會收到消息。
7.1 命令
7.2 實驗
先訂閱後發佈後才能收到消息:
1 可以一次性訂閱多個,SUBSCRIBE c1 c2 c3
2 消息發佈,PUBLISH c2 hello-redis
3 訂閱多個,通配符*, PSUBSCRIBE new*
4 收取消息, PUBLISH new1 redis2015
7.3 缺點與其改進
- 和Redis的穩定性有關。對於舊版的redis來說,如果一個客戶端訂閱了某個或某些頻道,但它讀取消息的速度卻不夠快的話,那麼不斷積壓的消息就會使得Redis輸出緩存區的體積變得越來越大,這可能會導致Redis的速度變慢,甚至直接崩潰。也可能導致Redis被操作系統強制殺死,甚至導致操作系統本身不可用。新版的Redis不會出現這種問題,因爲它會自動斷開不符合client-output-buffer-limit pubsub配置選項要求的訂閱客戶端。
- 和數據傳輸的可靠性有關。任何網絡系統在執行操作時都可能會遇上斷線情況,而斷線產生的連接錯誤通常會使得網絡連接兩端中的其中一端進行重新連接。但是,如果客戶端在執行訂閱操作的過程中斷線,那麼客戶端將丟失在斷線期間發送的所有消息,因此依靠頻道來接收消息的用戶可能對Redis提供的PUBLISH命令和SUBSCRIBE命令的語義感到失望。
八.Redis的Java客戶端Jedis
8.1 操作環境及所需的包
操作環境:Eclipse
jar包:
Commons-pool-1.6.jar
Jedis-2.1.0.jar
8.2 實驗
8.2.1 實驗架構
8.2.2 測試聯通性
package com.redis.test;
import redis.clients.jedis.Jedis;
public class TestPing {
public static void main(String[] args)
{
//連接本地Jedis服務
Jedis jedis=new Jedis("127.0.0.1",6379);
System.out.println("Connected is Ok=====>"+jedis.ping());
}
}
輸出結果:
8.2.3 測試key和數據類型
package com.redis.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import redis.clients.jedis.Jedis;
public class TestShuju {
//測試數據類型
public static void main(String[] args){
Jedis jedis=new Jedis("127.0.0.1",6379);
jedis.set("k1", "hhh");
jedis.set("k2", "hh");
jedis.set("k3", "h");
//key
Set<String> keys=jedis.keys("*");
for(Iterator iterator=keys.iterator();iterator.hasNext();){
String key=(String)iterator.next();
System.out.println(key);
}
//String
System.out.println(jedis.get("k1"));
jedis.set("k4", "sd");
jedis.mset("str1","v1","str2","v2");
System.out.println(jedis.mget("str1","str2"));
//list
System.out.println("----------------------------------------");
//jedis.lpush("mylist","v1","v2","v3","v4","v5");
List<String> list = jedis.lrange("mylist",0,-1);
for (String element : list) {
System.out.println(element);
}
//set
jedis.sadd("orders","jd001");
jedis.sadd("orders","jd002");
jedis.sadd("orders","jd003");
Set<String> set1 = jedis.smembers("orders");
for (Iterator iterator = set1.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
jedis.srem("orders","jd002");
System.out.println(jedis.smembers("orders").size());
//hash
jedis.hset("hash1","userName","lisi");
System.out.println(jedis.hget("hash1","userName"));
Map<String,String> map = new HashMap<String,String>();
map.put("telphone","13811814763");
map.put("address","atguigu");
map.put("email","[email protected]");
jedis.hmset("hash2",map);
List<String> result = jedis.hmget("hash2", "telphone","email");
for (String element : result) {
System.out.println(element);
}
//zset
jedis.zadd("zset01",60d,"v1");
jedis.zadd("zset01",70d,"v2");
jedis.zadd("zset01",80d,"v3");
jedis.zadd("zset01",90d,"v4");
Set<String> s1 = jedis.zrange("zset01",0,-1);
for (Iterator iterator = s1.iterator(); iterator.hasNext();) {
String string = (String) iterator.next();
System.out.println(string);
}
}
}
8.2.4 事務
開啓一個事務:
package com.redis.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Response;
import redis.clients.jedis.Transaction;
public class TestTrans {
public static void main(String[] args){
Jedis jedis=new Jedis("127.0.0.1",6379);
Transaction transaction=jedis.multi();
Response<String> response = transaction.get("serialNum");
transaction.set("serialNum","s002");
response = transaction.get("serialNum");
transaction.lpush("list3","a");
transaction.lpush("list3","b");
transaction.lpush("list3","c");
transaction.exec();
System.out.println("serialNum***********"+response.get());
}
}
加鎖:
public class TestTransaction {
public boolean transMethod() {
Jedis jedis = new Jedis("127.0.0.1", 6379);
int balance;// 可用餘額
int debt;// 欠額
int amtToSubtract = 10;// 實刷額
jedis.watch("balance");
//jedis.set("balance","5");//此句不該出現,講課方便。模擬其他程序已經修改了該條目
balance = Integer.parseInt(jedis.get("balance"));
if (balance < amtToSubtract) {
jedis.unwatch();
System.out.println("modify");
return false;
} else {
System.out.println("***********transaction");
Transaction transaction = jedis.multi();
transaction.decrBy("balance", amtToSubtract);
transaction.incrBy("debt", amtToSubtract);
transaction.exec();
balance = Integer.parseInt(jedis.get("balance"));
debt = Integer.parseInt(jedis.get("debt"));
System.out.println("*******" + balance);
System.out.println("*******" + debt);
return true;
}
}
/**
* 通俗點講,watch命令就是標記一個鍵,如果標記了一個鍵, 在提交事務前如果該鍵被別人修改過,那事務就會失敗,這種情況通常可以在程序中
* 重新再嘗試一次。
* 首先標記了鍵balance,然後檢查餘額是否足夠,不足就取消標記,並不做扣減; 足夠的話,就啓動事務進行更新操作,
* 如果在此期間鍵balance被其它人修改, 那在提交事務(執行exec)時就會報錯, 程序中通常可以捕獲這類錯誤再重新執行一次,直到成功。
*/
public static void main(String[] args) {
TestTransaction test = new TestTransaction();
boolean retValue = test.transMethod();
System.out.println("main retValue-------: " + retValue);
}
}
8.2.5 主從複製
public static void main(String[] args) throws InterruptedException
{
Jedis jedis_M = new Jedis("127.0.0.1",6379);
Jedis jedis_S = new Jedis("127.0.0.1",6380);
jedis_S.slaveof("127.0.0.1",6379);
jedis_M.set("k6","v6");
Thread.sleep(500);
System.out.println(jedis_S.get("k6"));
}
8.3 JedisPool
獲取Jedis實例要從JedisPool中獲取,用完Jedis實例需要返回給JedisPool。如果Jedis在使用過程出錯也需要還給JedisPool。
8.3.1 實驗
JedisPool的配置,這個類有獲取和釋放Jedis實例的方法:
package com.redis.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
public class JedisPoolUtil {
private static volatile JedisPool jedisPool = null;
private JedisPoolUtil(){
}
public static JedisPool getJedisPoolInstance(){
if(jedisPool==null){
synchronized(JedisPoolUtil.class){
if(jedisPool==null){
JedisPoolConfig config=new JedisPoolConfig();
config.setMaxActive(100);
config.setMaxIdle(32);
config.setMaxWait(100*100);
config.setTestOnBorrow(true);
jedisPool=new JedisPool(config,"127.0.0.1");
}
}
}
return jedisPool;
}
public static void release(JedisPool jedisPool,Jedis jedis){
if(jedis!=null){
jedisPool.returnResourceObject(jedis);
}
}
}
測試:
package com.redis.test;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class Test {
public static void main(String[] args){
JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = null;
try
{
jedis = jedisPool.getResource();
jedis.set("k18","v183");
} catch (Exception e) {
e.printStackTrace();
}finally{
JedisPoolUtil.release(jedisPool, jedis);
}
}
}
8.3.2 配置
JedisPool的配置參數大部分是由JedisPoolConfig的對應項來賦值的(如上圖實驗所示)。
(1)maxActive:控制一個pool可分配多少個jedis實例,通過pool.getResource()來獲取;如果賦值爲-1,則表示不限制;如果pool已經分配了maxActive個jedis實例,則此時pool的狀態爲exhausted。
(2)maxIdle:控制一個pool最多有多少個狀態爲idle(空閒)的jedis實例;
(3)whenExhaustedAction:表示當pool中的jedis實例都被allocated完時,pool要採取的操作;默認有三種。
- WHEN_EXHAUSTED_FAIL --> 表示無jedis實例時,直接拋出NoSuchElementException;
- WHEN_EXHAUSTED_BLOCK --> 則表示阻塞住,或者達到maxWait時拋出JedisConnectionException;
- WHEN_EXHAUSTED_GROW --> 則表示新建一個jedis實例,也就說設置的maxActive無用;
(4)maxWait:表示當borrow一個jedis實例時,最大的等待時間,如果超過等待時間,則直接拋JedisConnectionException;
(5)testOnBorrow:獲得一個jedis實例的時候是否檢查連接可用性(ping());如果爲true,則得到的jedis實例均是可用的;
(6)testOnReturn:return 一個jedis實例給pool時,是否檢查連接可用性(ping());
(7)testWhileIdle:如果爲true,表示有一個idle object evitor線程對idle object進行掃描,如果validate失敗,此object會被從pool中drop掉;這一項只有在timeBetweenEvictionRunsMillis大於0時纔有意義;
(8)timeBetweenEvictionRunsMillis:表示idle object evitor兩次掃描之間要sleep的毫秒數;
(9)numTestsPerEvictionRun:表示idle object evitor每次掃描的最多的對象數;
(10)minEvictableIdleTimeMillis:表示一個對象至少停留在idle狀態的最短時間,然後才能被idle object evitor掃描並驅逐;這一項只有在timeBetweenEvictionRunsMillis大於0時纔有意義;
(11)softMinEvictableIdleTimeMillis:在minEvictableIdleTimeMillis基礎上,加入了至少minIdle個對象已經在pool裏面了。如果爲-1,evicted不會根據idle time驅逐任何對象。如果minEvictableIdleTimeMillis>0,則此項設置無意義,且只有在timeBetweenEvictionRunsMillis大於0時纔有意義;
(12)lifo:borrowObject返回對象時,是採用DEFAULT_LIFO(last in first out,即類似cache的最頻繁使用隊列),如果爲False,則表示FIFO隊列;
參考文章:
《Redis實戰》
《Redis深度歷險:核心原理和應用實踐》