第一章 Redis新數據類型
1.1 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
(1)格式
setbit<key><offset><value>
設置Bitmaps中某個偏移量的值(0或1)
*offset:偏移量從0開始
(2)實例
每個獨立用戶是否訪問過網站存放在Bitmaps中, 將訪問的用戶記做1, 沒有訪問的用戶記做0, 用偏移量作爲用戶的id。
設置鍵的第offset個位的值(從0算起) , 假設現在有20個用戶,userid=1, 6, 11, 15, 19的用戶對網站進行了訪問, 那麼當前Bitmaps初始化結果如圖
unique:users:20201106
代表2020-11-06這天的獨立訪問用戶的Bitmaps
注:
很多應用的用戶id以一個指定數字(例如10000) 開頭, 直接將用戶id和Bitmaps的偏移量對應勢必會造成一定的浪費, 通常的做法是每次做setbit操作時將用戶id減去這個指定數字。
在第一次初始化Bitmaps時, 假如偏移量非常大, 那麼整個初始化過程執行會比較慢, 可能會造成Redis的阻塞。
2、getbit
(1)格式
getbit<key><offset>
獲取Bitmaps中某個偏移量的值
獲取鍵的第offset位的值(從0開始算)
(2)實例
獲取id=8的用戶是否在2020-11-06這天訪問過, 返回0說明沒有訪問過:
注:因爲100根本不存在,所以也是返回0
3、bitcount
統計字符串被設置爲1的bit數。一般情況下,給定的整個字符串都會被進行計數,通過指定額外的 start 或 end 參數,可以讓計數只在特定的位上進行。start 和 end 參數的設置,都可以使用負數值:比如 -1 表示最後一個位,而 -2 表示倒數第二個位,start、end 是指bit組的字節的下標數,二者皆包含。
(1)格式
bitcount<key>[start end]
統計字符串從start字節到end字節比特值爲1的數量
(2)實例
計算2022-11-06這天的獨立訪問用戶數量
start和end代表起始和結束字節數, 下面操作計算用戶id在第1個字節到第3個字節之間的獨立訪問用戶數, 對應的用戶id是11, 15, 19。
舉例: K1 【01000001 01000000 00000000 00100001】,對應【0,1,2,3】
bitcount K1 1 2 : 統計下標1、2字節組中bit=1的個數,即01000000 00000000
--》bitcount K1 1 2 --》1
bitcount K1 1 3 : 統計下標1、2字節組中bit=1的個數,即01000000 00000000 00100001
--》bitcount K1 1 3 --》3
bitcount K1 0 -2 : 統計下標0到下標倒數第2,字節組中bit=1的個數,即01000001 01000000 00000000
--》bitcount K1 0 -2 --》3
注意:redis的setbit設置或清除的是bit位置,而bitcount計算的是byte位置。
4、bitop
(1)格式
bitop and(or/not/xor) <destkey> [key…]
bitop是一個複合操作, 它可以做多個Bitmaps的and(交集) 、 or(並集) 、 not(非) 、 xor(異或) 操作並將結果保存在destkey中。
(2)實例
2020-11-04 日訪問網站的userid=1,2,5,9。
setbit unique:users:20201104 1 1
setbit unique:users:20201104 2 1
setbit unique:users:20201104 5 1
setbit unique:users:20201104 9 1
2020-11-03 日訪問網站的userid=0,1,4,9。
setbit unique:users:20201103 0 1
setbit unique:users:20201103 1 1
setbit unique:users:20201103 4 1
setbit unique:users:20201103 9 1
計算出兩天都訪問過網站的用戶數量
bitop and unique:users:and:20201104_03 unique:users:20201103unique:users:20201104
計算出任意一天都訪問過網站的用戶數量(例如月活躍就是類似這種) , 可以使用or求並集
Bitmaps與set對比
假設網站有1億用戶, 每天獨立訪問的用戶有5千萬, 如果每天用集合類型和Bitmaps分別存儲活躍用戶可以得到表
set和Bitmaps存儲一天活躍用戶對比 | |||
---|---|---|---|
數據類型 | 每個用戶id佔用空間 | 需要存儲的用戶量 | 全部內存量 |
集合類型 | 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存儲一天活躍用戶對比(獨立用戶比較少) | |||
---|---|---|---|
數據類型 | 每個userid佔用空間 | 需要存儲的用戶量 | 全部內存量 |
集合類型 | 64位 | 100000 | 64位*100000 = 800KB |
Bitmaps | 1位 | 100000000 | 1位*100000000 = 12.5MB |
1.2 HyperLogLog
簡介
在工作當中,我們經常會遇到與統計相關的功能需求,比如統計網站PV(PageView頁面訪問量),可以使用Redis的incr、incrby輕鬆實現。
但像UV(Unique Visitor,獨立訪客)、獨立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
(1)格式
pfadd <key>< element> [element ...]
添加指定元素到 HyperLogLog 中
(2)實例
將所有元素添加到指定HyperLogLog數據結構中。如果執行命令後HLL估計的近似基數發生變化,則返回1,否則返回0。
2、pfcount
(1)格式
pfcount<key> [key ...]
計算HLL的近似基數,可以計算多個HLL,比如用HLL存儲每天的UV,計算一週的UV可以使用7天的UV合併計算即可
(2)實例
3、pfmerge
(1)格式
pfmerge<destkey><sourcekey> [sourcekey ...]
將一個或多個HLL合併後的結果存儲在另一個HLL中,比如每月活躍用戶可以使用每天的活躍用戶來合併計算可得
(2)實例
1.3 Geospatial
簡介
Redis 3.2 中增加了對GEO類型的支持。GEO,Geographic,地理信息的縮寫。該類型,就是元素的2維座標,在地圖上就是經緯度。redis基於該類型,提供了經緯度設置,查詢,範圍查詢,距離查詢,經緯度Hash等常見操作。
命令
1、geoadd
(1)格式
geoadd<key>< longitude><latitude><member> [longitude latitude member...]
添加地理位置(經度,緯度,名稱)
(2)實例
geoadd china:city 121.47 31.23 shanghai
geoadd china:city 106.50 29.53 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing
兩極無法直接添加,一般會下載城市數據,直接通過 Java 程序一次性導入。
有效的經度從 -180 度到 180 度。有效的緯度從 -85.05112878 度到 85.05112878 度。
當座標位置超出指定範圍時,該命令將會返回一個錯誤。
已經添加的數據,是無法再次往裏面添加的。
2、geopos
(1)格式
geopos <key><member> [member...]
獲得指定地區的座標值
(2)實例
3、geodist
(1)格式
geodist<key><member1><member2> [m|km|ft|mi ]
獲取兩個位置之間的直線距離
(2)實例
獲取兩個位置之間的直線距離
單位:
- m 表示單位爲米[默認值]。
- km 表示單位爲千米。
- mi 表示單位爲英里。
- ft 表示單位爲英尺。
如果用戶沒有顯式地指定單位參數, 那麼 GEODIST 默認使用米作爲單位
4、georadius
(1)格式
georadius<key>< longitude><latitude>radius m|km|ft|mi
以給定的經緯度爲中心,找出某一半徑內的元素
經度 緯度 距離 單位
(2)實例
第二章 Redis6.0 新功能
2.1 ACL
簡介
Redis ACL是Access Control List(訪問控制列表)的縮寫,該功能允許根據可以執行的命令和可以訪問的鍵來限制某些連接。
在Redis 5版本之前,Redis 安全規則只有密碼控制 還有通過rename 來調整高危命令比如 flushdb , KEYS* , shutdown 等。Redis 6 則提供ACL的功能對用戶進行更細粒度的權限控制 :
(1)接入權限:用戶名和密碼
(2)可以執行的命令
(3)可以操作的 KEY
參考官網:https://redis.io/topics/acl
命令
1、使用acl list命令展現用戶權限列表
(1)數據說明
2、使用acl cat命令
(1)查看添加權限指令類別
(2)加參數類型名可以查看類型下具體命令
3、使用acl whoami命令查看當前用戶
4、使用aclsetuser命令創建和編輯用戶ACL
(1)ACL規則
下面是有效ACL規則的列表。某些規則只是用於激活或刪除標誌,或對用戶ACL執行給定更改的單個單詞。其他規則是字符前綴,它們與命令或類別名稱、鍵模式等連接在一起。
ACL規則 | ||
---|---|---|
類型 | 參數 | 說明 |
啓動和禁用用戶 | on | 激活某用戶賬號 |
off | 禁用某用戶賬號。注意,已驗證的連接仍然可以工作。如果默認用戶被標記爲off,則新連接將在未進行身份驗證的情況下啓動,並要求用戶使用AUTH選項發送AUTH或HELLO,以便以某種方式進行身份驗證。 | |
權限的添加刪除 | + |
將指令添加到用戶可以調用的指令列表中 |
從用戶可執行指令列表移除指令 | ||
+@ |
添加該類別中用戶要調用的所有指令,有效類別爲@admin、@set、@sortedset…等,通過調用ACL CAT命令查看完整列表。特殊類別@all表示所有命令,包括當前存在於服務器中的命令,以及將來將通過模塊加載的命令。 | |
-@ |
從用戶可調用指令中移除類別 | |
allcommands | +@all的別名 | |
nocommand | -@all的別名 | |
可操作鍵的添加或刪除 | ~ |
添加可作爲用戶可操作的鍵的模式。例如~*允許所有的鍵 |
(2)通過命令創建新用戶默認權限
acl setuser user1
在上面的示例中,我根本沒有指定任何規則。如果用戶不存在,這將使用just created的默認屬性來創建用戶。如果用戶已經存在,則上面的命令將不執行任何操作。
(3)設置有用戶名、密碼、ACL權限、並啓用的用戶
acl setuser user2 on >password ~cached:* +get
(4)切換用戶,驗證權限
2.2 IO多線程
簡介
Redis6終於支撐多線程了,告別單線程了嗎?
IO多線程其實指客戶端交互部分的網絡IO交互處理模塊多線程,而非執行命令多線程。Redis6執行命令依然是單線程。
原理架構
Redis 6 加入多線程,但跟 Memcached 這種從 IO處理到數據訪問多線程的實現模式有些差異。Redis 的多線程部分只是用來處理網絡數據的讀寫和協議解析,執行命令仍然是單線程。之所以這麼設計是不想因爲多線程而變得複雜,需要去控制 key、lua、事務,LPUSH/LPOP 等等的併發問題。整體的設計大體如下:
另外,多線程IO默認也是不開啓的,需要再配置文件中配置
io-threads-do-reads yes
io-threads 4
2.3 工具支持 Cluster
之前老版Redis想要搭集羣需要單獨安裝ruby環境,Redis 5 將 redis-trib.rb 的功能集成到 redis-cli 。另外官方 redis-benchmark 工具開始支持 cluster 模式了,通過多線程的方式對多個分片進行壓測。
2.4 Redis新功能持續關注
Redis6新功能還有:
1、RESP3新的 Redis 通信協議:優化服務端與客戶端之間通信
2、Client side caching客戶端緩存:基於 RESP3 協議實現的客戶端緩存功能。爲了進一步提升緩存的性能,將客戶端經常訪問的數據cache到客戶端。減少TCP網絡交互。
3、Proxy集羣代理模式:Proxy 功能,讓 Cluster 擁有像單實例一樣的接入方式,降低大家使用cluster的門檻。不過需要注意的是代理不改變 Cluster 的功能限制,不支持的命令還是不會支持,比如跨 slot 的多Key操作。
4、Modules API
Redis 6中模塊API開發進展非常大,因爲Redis Labs爲了開發複雜的功能,從一開始就用上Redis模塊。Redis可以變成一個框架,利用Modules來構建不同系統,而不需要從頭開始寫然後還要BSD許可。Redis一開始就是一個向編寫各種系統開放的平臺。