2 Redis 概述安裝及常用資料型別
2.1 應用場景
-
Redis是一個開源的key-value儲存系統。
-
和Memcached類似,它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合)、zset(sorted set --有序集合)和hash(雜湊型別)。
-
這些資料型別都支援push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。
-
在此基礎上,Redis支援各種不同方式的排序。
-
與memcached一樣,為了保證效率,資料都是快取在記憶體中。
-
區別的是Redis會週期性的把更新的資料寫入磁碟或者把修改操作寫入追加的記錄檔案。
-
並且在此基礎上實現了master-slave(主從)同步。
2.1.1 配合關係型資料庫做快取記憶體
-
高頻次,熱門訪問的資料,降低資料庫IO
-
分散式架構,做session共享
2.1.2 多樣的資料結構儲存持久化資料
2.2 Redis 安裝
2.2.1 下載原始碼包
官網地址:
wget https://download.redis.io/releases/redis-6.2.6.tar.gz
2.2.2 解壓安裝
1)關閉防火牆及selinux
systemctl stop firewalld && systemctl disable firewalld
swapoff -a && sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
2)修改主機名
hostnamectl set-hostname "redis-server-01" && bash
3)安裝依賴環境
yum -y install centos-release-scl scl-utils-build devtoolset-8-toolchain gcc* gcc-* make vim wget
4)解壓原始碼包
tar -zxvf redis-6.2.6.tar.gz
mv redis-6.2.6/ /usr/local/redis
5)編譯
cd /usr/local/redis/
make && make install
6)安裝目錄
預設安裝在/usr/local/bin目錄下
-
redis-benchmark:效能測試工具,可以在自己本子執行,看看自己本子效能如何
-
redis-check-aof:修復有問題的AOF檔案,rdb和aof後面講
-
redis-check-dump:修復有問題的dump.rdb檔案
-
redis-sentinel:Redis叢集使用
-
redis-server:Redis伺服器啟動命令
-
redis-cli:客戶端,操作入口
2.2.3 啟動服務
1)前臺啟動
redis-server
2)後臺啟動(推薦)
# 備份檔案
cp redis.conf redis.conf.bak
# 開啟後臺啟動
sed -i 's/daemonize no/daemonize yes/g' redis.conf
# 啟動redis服務
redis-server /usr/local/redis/redis.conf
# 檢視是否啟動成功
ps -ef | grep redis
3)使用客戶端連線
redis-cli -p 6379
4)驗證測試
127.0.0.1:6379> ping
5)關閉服務
# 命令列關閉
redis-cli -p 6379 shutdown
# 服務內關閉
127.0.0.1:6379> shutdown
2.2.4 Redis是單執行緒+多路IO複用技術
多路複用是指使用一個執行緒來檢查多個檔案描述符(Socket)的就緒狀態,比如呼叫select和poll函式,傳入多個檔案描述符,如果有一個檔案描述符就緒,則返回,否則阻塞直到超時。
得到就緒狀態後進行真正的操作可以在同一個執行緒裡執行,也可以啟動執行緒執行(比如使用執行緒池)
序列 vs 多執行緒+鎖(memcached) vs 單執行緒+多路IO複用(Redis)
(與Memcache三點不同: 支援多資料型別,支援持久化,單執行緒+多路IO複用)
2.3 常用五大資料型別
2.3.1 Redis 鍵(key)
常用命令
-
新增key
set key value
-
檢視當前庫所有key
keys *
-
判斷某個key是否存在(返回1代表存在,返回0為不存在)
exists $key
-
檢視key的型別
type $key
-
刪除指定的key資料
del $key
-
根據value選擇非阻塞刪除
unlink $key
-
為給定的key設定過期時間(過期後就會自動刪除)
expire $key
-
檢視還有多少秒過期-1表示永不過期,-2表示已過期
ttl $key
-
select命令切換資料庫
select
-
dbsize檢視當前資料庫的key的數量
dbsize
-
flushdb清空當前庫
flushdb
-
flushall通殺全部庫
flushall
2.3.2 Redis 字串(String)
1)簡介
String是Redis最基本的型別,你可以理解成與Memcached一模一樣的型別,一個key對應一個value。
String型別是二進位制安全的。意味著Redis的string可以包含任何資料。比如jpg圖片或者序列化的物件。
String型別是Redis最基本的資料型別,一個Redis中字串value最多可以是512M
2)常用命令
set <key> <value>
# 新增鍵值對
get <key>
# 查詢對應鍵值
append <key> <value>
# 將給定的<value> 追加到原值的末尾
strlen <key>
# 獲得值的長度
setnx <key> <value>
# 只有在 key 不存在時,設定 key 的值,存在時不做改動
incr <key>
# 將 key 中儲存的數字值增1,只能對數字值操作,如果為空,新增值為1
decr <key>
# 將 key 中儲存的數字值減1只能對數字值操作,如果為空,新增值為-1
incrby <key> <increment>
# 將 key 中儲存的數字值增減。自定義步長。
mset <key1> <value1> <key2> <value2> .....
# 同時設定一個或多個 key-value對
mget <key1> <key2> <key3> .....
# 同時獲取一個或多個 value
msetnx <key1> <value1> <key2> <value2> .....
# 同時設定一個或多個 key-value 對,當且僅當所有給定 key 都不存在,因為原子性,會導致存在一個,都不設定成功
getrange <key><起始位置><結束位置>
# 獲得值的範圍,類似java中的substring,前包,後包
setrange <key> <起始位置> <value>
# 用 <value> 覆寫<key>所儲存的字串值,從<起始位置>開始(索引從0開始)。
setex <key> <過期時間> <value>
# 設定鍵值的同時,設定過期時間,單位秒。
getset <key> <value>
# 以新換舊,設定了新值同時獲得舊值。
3)原子性
所謂原子操作是指不會被執行緒排程機制打斷的操作;
這種操作一旦開始,就一直執行到結束,中間不會有任何 context switch (切換到另一個執行緒)。
(1)在單執行緒中, 能夠在單條指令中完成的操作都可以認為是"原子操作",因為中斷只能發生於指令之間。
(2)在多執行緒中,不能被其它程序(執行緒)打斷的操作就叫原子操作。
Redis單命令的原子性主要得益於Redis的單執行緒。
案例:
java中的i++是否是原子操作?不是
i=0;兩個執行緒分別對i進行++100次,值是多少? 2~200
4)資料結構
String的資料結構為簡單動態字串(Simple Dynamic String,縮寫SDS)。是可以修改的字串,內部結構實現上類似於Java的ArrayList,採用預分配冗餘空間的方式來減少記憶體的頻繁分配.
如圖中所示,內部為當前字串實際分配的空間capacity一般要高於實際字串長度len。當字串長度小於1M時,擴容都是加倍現有的空間,如果超過1M,擴容時一次只會多擴1M的空間。需要注意的是字串最大長度為512M。
2.3.3 Redis 列表(List)
1)簡介
單鍵多值
Redis 列表是簡單的字串列表,按照插入順序排序。你可以新增一個元素到列表的頭部(左邊)或者尾部(右邊)。
它的底層實際是個雙向連結串列,對兩端的操作效能很高,通過索引下標的操作中間的節點效能會較差。
2)常用命令
lpush/rpush <key> <value1> <value2> <value3> ....
# 從左邊/右邊插入一個或多個值。
lpop/rpop <key>
# 從左邊/右邊吐出一個值。值在鍵在,值光鍵亡。
rpoplpush <key1> <key2>
# 列表右邊吐出一個值,插到<key2>列表左邊。
lrange <key> <start> <stop>
# 按照索引下標獲得元素(從左到右) 0左邊第一個,-1右邊第一個,(0 -1表示獲取所有)
lindex <key> <index>
# 按照索引下標獲得元素(從左到右)
llen <key>
# 獲得列表長度
linsert <key> before/after <value> <newvalue>
# 在<value>的前/後面插入<newvalue>插入值
lrem <key> <n> <value>
# 從左邊刪除n個value(從左到右)
lset <key> <index> <value>
# 將列表key下標為index的值替換成value
3)資料結構
-
List的資料結構為快速連結串列quickList。
-
首先在列表元素較少的情況下會使用一塊連續的記憶體儲存,這個結構是ziplist,也即是壓縮列表。
-
它將所有的元素緊挨著一起儲存,分配的是一塊連續的記憶體。
-
當資料量比較多的時候才會改成quicklist。
-
因為普通的連結串列需要的附加指標空間太大,會比較浪費空間。比如這個列表裡存的只是int型別的資料,結構上還需要兩個額外的指標prev和next。
Redis將連結串列和ziplist結合起來組成了quicklist。也就是將多個ziplist使用雙向指標串起來使用。這樣既滿足了快速的插入刪除效能,又不會出現太大的空間冗餘。
2.3.4 Redis 集合(Set)
1)簡介
Redis set對外提供的功能與list類似是一個列表的功能,特殊之處在於set是可以自動排重的,當你需要儲存一個列表資料,又不希望出現重複資料時,set是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的重要介面,這個也是list所不能提供的。
Redis的Set是string型別的無序集合。它底層其實是一個value為null的hash表,所以新增,刪除,查詢的複雜度都是O(1)。
一個演算法,隨著資料的增加,執行時間的長短,如果是O(1),資料增加,查詢資料的時間不變
2)常用命令
sadd <key> <value1> <value2> .....
# 將一個或多個 member 元素加入到集合 key 中,已經存在的 member 元素將被忽略
smembers <key>
# 取出該集合的所有值
sismember <key> <value>
# 判斷集合<key>是否為含有該<value>值,有1,沒有0
scard <key>
# 返回該集合的元素個數
srem <key> <value1> <value2> ....
# 刪除集合中的某個元素
spop <key>
# 隨機從該集合中吐出一個值。
srandmember <key> <n>
# 隨機從該集合中取出n個值。不會從集合中刪除。
smove <source> <destination> value
# 把集合中一個值從一個集合移動到另一個集合
sunion <key1> <key2>
# 返回兩個集合的並集元素。
sdiff <key1> <key2>
#返回兩個集合的差集元素(key1中的,不包含key2中的)
sinter <key1> <key2>
# 返回兩個集合的交集元素
3)資料結構
Set資料結構是dict字典,字典是用雜湊表實現的。
Java中HashSet的內部實現使用的是HashMap,只不過所有的value都指向同一個物件。Redis的set結構也是一樣,它的內部也使用hash結構,所有的value都指向同一個內部值
2.3.5 Redis 雜湊(Hash)
1)簡介
Redis hash 是一個鍵值對集合,Redis hash是一個string型別的field和value的對映表,hash特別適合用於儲存物件。
類似Java裡面的Map<String,Object>。使用者ID為查詢的key,儲存的value使用者物件包含姓名,年齡,生日等資訊,如果用普通的key/value結構來儲存
主要有以下幾種儲存方式:
每次修改使用者的某個屬性需要,先反序列化改好後再序列化回去。開銷較大。
使用者ID資料冗餘
通過 key(使用者ID) + field(屬性標籤) 就可以操作對應屬性資料了,既不需要重複儲存資料,也不會帶來序列化和併發修改控制的問題
2)常用命令
hset <key> <field> <value>
# 給<key>集合中的 <field>鍵賦值<value>
hget <key> <field>
# 從<key1>集合<field>取出 value
hmset <key1> <field1> <value1> <field2> <value2>...
# 批量設定hash的值
hexists <key1> <field>
# 檢視雜湊表 key 中,給定域 field 是否存在。
hkeys <key>
# 列出該hash集合的所有field
hvals <key>
# 列出該hash集合的所有value
hincrby <key> <field> <increment>
# 為雜湊表 key 中的域 field 的值加上增量 1 -1
hsetnx <key> <field> <value>
# 將雜湊表 key 中的域 field 的值設定為 value ,當且僅當域 field 不存在 .
3)資料結構
Hash型別對應的資料結構是兩種:ziplist(壓縮列表),hashtable(雜湊表)。當field-value長度較短且個數較少時,使用ziplist,否則使用hashtable。
2.3.6 Redis 有序集合Zset(sorted set)
1)簡介
Redis有序集合zset與普通集合set非常相似,是一個沒有重複元素的字串集合。
不同之處是有序集合的每個成員都關聯了一個評分(score),這個評分(score)被用來按照從最低分到最高分的方式排序集合中的成員。集合的成員是唯一的,但是評分可以是重複了 。
因為元素是有序的, 所以你也可以很快的根據評分(score)或者次序(position)來獲取一個範圍的元素。
訪問有序集合的中間元素也是非常快的,因此你能夠使用有序集合作為一個沒有重複成員的智慧列表。
2)常用命令
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開始。
3)資料結構
SortedSet(zset)是Redis提供的一個非常特別的資料結構,一方面它等價於Java的資料結構Map<String, Double>,可以給每一個元素value賦予一個權重score,另一方面它又類似於TreeSet,內部的元素會按照權重score進行排序,可以得到每個元素的名次,還可以通過score的範圍來獲取元素的列表。
zset底層使用了兩個資料結構
(2)跳躍表,跳躍表的目的在於給元素value排序,根據score的範圍獲取元素列表。
4)跳躍表(跳錶)
有序集合在生活中比較常見,例如根據成績對學生排名,根據得分對玩家排名等。對於有序集合的底層實現,可以用陣列、平衡樹、連結串列等。陣列不便元素的插入、刪除;平衡樹或紅黑樹雖然效率高但結構複雜;連結串列查詢需要遍歷所有效率低。Redis採用的是跳躍表。跳躍表效率堪比紅黑樹,實現遠比紅黑樹簡單。
對比有序連結串列和跳躍表,從連結串列中查詢出51
(1) 有序連結串列
要查詢值為51的元素,需要從第一個元素開始依次查詢、比較才能找到。共需要6次比較。
(2) 跳躍表
-
從第2層開始,1節點比51節點小,向後比較。
-
21節點比51節點小,繼續向後比較,後面就是NULL了,所以從21節點向下到第1層
-
在第1層,41節點比51節點小,繼續向後,61節點比51節點大,所以從41向下
-
在第0層,51節點為要查詢的節點,節點被找到,共查詢4次。
-
從此可以看出跳躍表比有序連結串列效率要高
-