1树绩、Bitmaps
1.1、簡介
現(xiàn)代計(jì)算機(jī)用二進(jìn)制(位) 作為信息的基礎(chǔ)單位隐轩, 1個(gè)字節(jié)等于8位饺饭, 例如“abc”字符串是由3個(gè)字節(jié)組成, 但實(shí)際在計(jì)算機(jī)存儲時(shí)將其用二進(jìn)制表示职车, “abc”分別對應(yīng)的ASCII碼分別是97瘫俊、 98、 99悴灵, 對應(yīng)的二進(jìn)制分別是01100001扛芽、 01100010和01100011,如下圖
合理地使用操作位能夠有效地提高內(nèi)存使用率和開發(fā)效率积瞒。
Redis提供了Bitmaps這個(gè)“數(shù)據(jù)類型”可以實(shí)現(xiàn)對位的操作:
(1) Bitmaps本身不是一種數(shù)據(jù)類型川尖, 實(shí)際上它就是字符串(key-value) , 但是它可以對字符串的位進(jìn)行操作茫孔。
(2) Bitmaps單獨(dú)提供了一套命令叮喳, 所以在Redis中使用Bitmaps和使用字符串的方法不太相同。 可以把Bitmaps想象成一個(gè)以位為單位的數(shù)組缰贝, 數(shù)組的每個(gè)單元只能存儲0和1馍悟, 數(shù)組的下標(biāo)在Bitmaps中叫做偏移量。
1.2剩晴、命令
1锣咒、setbit
(1)格式
setbit<key><offset><value>設(shè)置Bitmaps中某個(gè)偏移量的值(0或1)
*offset:偏移量從0開始
(2)實(shí)例
每個(gè)獨(dú)立用戶是否訪問過網(wǎng)站存放在Bitmaps中, 將訪問的用戶記做1李破, 沒有訪問的用戶記做0宠哄, 用偏移量作為用戶的id壹将。
設(shè)置鍵的第offset個(gè)位的值(從0算起) 嗤攻, 假設(shè)現(xiàn)在有20個(gè)用戶,userid=1诽俯, 6妇菱, 11承粤, 15, 19的用戶對網(wǎng)站進(jìn)行了訪問闯团, 那么當(dāng)前Bitmaps初始化結(jié)果如圖
unique:users:20201106代表2020-11-06這天的獨(dú)立訪問用戶的Bitmaps
注:
很多應(yīng)用的用戶id以一個(gè)指定數(shù)字(例如10000) 開頭辛臊, 直接將用戶id和Bitmaps的偏移量對應(yīng)勢必會造成一定的浪費(fèi), 通常的做法是每次做setbit操作時(shí)將用戶id減去這個(gè)指定數(shù)字房交。
在第一次初始化Bitmaps時(shí)彻舰, 假如偏移量非常大, 那么整個(gè)初始化過程執(zhí)行會比較慢候味, 可能會造成Redis的阻塞刃唤。
2、getbit
(1)格式
getbit<key><offset>獲取Bitmaps中某個(gè)偏移量的值
獲取鍵的第offset位的值(從0開始算)
(2)實(shí)例
獲取id=8的用戶是否在2020-11-06這天訪問過白群, 返回0說明沒有訪問過:
注:因?yàn)?00根本不存在尚胞,所以也是返回0
3、bitcount
統(tǒng)計(jì)字符串被設(shè)置為1的bit數(shù)帜慢。一般情況下笼裳,給定的整個(gè)字符串都會被進(jìn)行計(jì)數(shù),通過指定額外的 start 或 end 參數(shù)粱玲,可以讓計(jì)數(shù)只在特定的位上進(jìn)行躬柬。start 和 end 參數(shù)的設(shè)置,都可以使用負(fù)數(shù)值:比如 -1 表示最后一個(gè)位密幔,而 -2 表示倒數(shù)第二個(gè)位楔脯,start、end 是指bit組的字節(jié)的下標(biāo)數(shù)胯甩,二者皆包含昧廷。
(1)格式
bitcount<key>[start end] 統(tǒng)計(jì)字符串從start字節(jié)到end字節(jié)比特值為1的數(shù)量
(2)實(shí)例
計(jì)算2022-11-06這天的獨(dú)立訪問用戶數(shù)量
start和end代表起始和結(jié)束字節(jié)數(shù), 下面操作計(jì)算用戶id在第1個(gè)字節(jié)到第3個(gè)字節(jié)之間的獨(dú)立訪問用戶數(shù)偎箫, 對應(yīng)的用戶id是11木柬, 15, 19淹办。
舉例: K1 【01000001 01000000 00000000 00100001】眉枕,對應(yīng)【0,1怜森,2速挑,3】
bitcount K1 1 2 : 統(tǒng)計(jì)下標(biāo)1、2字節(jié)組中bit=1的個(gè)數(shù)副硅,即01000000 00000000
--》bitcount K1 1 2 --》1
bitcount K1 1 3 : 統(tǒng)計(jì)下標(biāo)1姥宝、2字節(jié)組中bit=1的個(gè)數(shù),即01000000 00000000 00100001
--》bitcount K1 1 3 --》3
bitcount K1 0 -2 : 統(tǒng)計(jì)下標(biāo)0到下標(biāo)倒數(shù)第2恐疲,字節(jié)組中bit=1的個(gè)數(shù)腊满,即01000001 01000000 00000000
--》bitcount K1 0 -2 --》3
注意:redis的setbit設(shè)置或清除的是bit位置套么,而bitcount計(jì)算的是byte位置。
4碳蛋、bitop
(1)格式
bitop and(or/not/xor) <destkey> [key…]
bitop是一個(gè)復(fù)合操作胚泌, 它可以做多個(gè)Bitmaps的and(交集) 、 or(并集) 肃弟、 not(非) 玷室、 xor(異或) 操作并將結(jié)果保存在destkey中。
(2)實(shí)例
2020-11-04 日訪問網(wǎng)站的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 日訪問網(wǎng)站的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
計(jì)算出兩天都訪問過網(wǎng)站的用戶數(shù)量
bitop and unique:users:and:20201104_03
unique:users:20201103unique:users:20201104
計(jì)算出任意一天都訪問過網(wǎng)站的用戶數(shù)量(例如月活躍就是類似這種) , 可以使用or求并集
1.3感论、Bitmaps與set對比
假設(shè)網(wǎng)站有1億用戶绅项, 每天獨(dú)立訪問的用戶有5千萬, 如果每天用集合類型和Bitmaps分別存儲活躍用戶可以得到表
很明顯比肄, 這種情況下使用Bitmaps能節(jié)省很多的內(nèi)存空間快耿, 尤其是隨著時(shí)間推移節(jié)省的內(nèi)存還是非常可觀的
但Bitmaps并不是萬金油芳绩, 假如該網(wǎng)站每天的獨(dú)立訪問用戶很少掀亥, 例如只有10萬(大量的僵尸用戶) , 那么兩者的對比如下表所示妥色, 很顯然搪花, 這時(shí)候使用Bitmaps就不太合適了, 因?yàn)榛旧洗蟛糠治欢际?嘹害。
2撮竿、HyperLoglog
2.1、簡介
在工作當(dāng)中笔呀,我們經(jīng)常會遇到與統(tǒng)計(jì)相關(guān)的功能需求幢踏,比如統(tǒng)計(jì)網(wǎng)站PV(PageView頁面訪問量),可以使用Redis的incr、incrby輕松實(shí)現(xiàn)许师。
但像UV(UniqueVisitor房蝉,獨(dú)立訪客)、獨(dú)立IP數(shù)微渠、搜索記錄數(shù)等需要去重和計(jì)數(shù)的問題如何解決搭幻?這種求集合中不重復(fù)元素個(gè)數(shù)的問題稱為基數(shù)問題。
解決基數(shù)問題有很多種方案:
(1)數(shù)據(jù)存儲在MySQL表中逞盆,使用distinct count計(jì)算不重復(fù)個(gè)數(shù)
(2)使用Redis提供的hash檀蹋、set、bitmaps等數(shù)據(jù)結(jié)構(gòu)來處理
以上的方案結(jié)果精確纳击,但隨著數(shù)據(jù)不斷增加续扔,導(dǎo)致占用空間越來越大,對于非常大的數(shù)據(jù)集是不切實(shí)際的焕数。
能否能夠降低一定的精度來平衡存儲空間纱昧?Redis推出了HyperLogLog
Redis HyperLogLog 是用來做基數(shù)統(tǒng)計(jì)的算法,HyperLogLog 的優(yōu)點(diǎn)是堡赔,在輸入元素的數(shù)量或者體積非常非常大時(shí)识脆,計(jì)算基數(shù)所需的空間總是固定的、并且是很小的善已。
在 Redis 里面灼捂,每個(gè) HyperLogLog 鍵只需要花費(fèi) 12 KB 內(nèi)存,就可以計(jì)算接近 2^64 個(gè)不同元素的基數(shù)换团。這和計(jì)算基數(shù)時(shí)悉稠,元素越多耗費(fèi)內(nèi)存就越多的集合形成鮮明對比。
但是艘包,因?yàn)?HyperLogLog 只會根據(jù)輸入元素來計(jì)算基數(shù)的猛,而不會儲存輸入元素本身,所以 HyperLogLog 不能像集合那樣想虎,返回輸入的各個(gè)元素卦尊。
什么是基數(shù)?
比如數(shù)據(jù)集 {1, 3, 5, 7, 5, 7, 8}, 那么這個(gè)數(shù)據(jù)集的基數(shù)集為 {1, 3, 5 ,7, 8}, 基數(shù)(不重復(fù)元素)為5舌厨。 基數(shù)估計(jì)就是在誤差可接受的范圍內(nèi)岂却,快速計(jì)算基數(shù)。
2.2裙椭、命令
1躏哩、pfadd
(1)格式
pfadd <key>< element> [element ...] 添加指定元素到 HyperLogLog 中
(2)實(shí)例
將所有元素添加到指定HyperLogLog數(shù)據(jù)結(jié)構(gòu)中。如果執(zhí)行命令后HLL估計(jì)的近似基數(shù)發(fā)生變化揉燃,則返回1震庭,否則返回0。
2你雌、pfcount
(1)格式
pfcount<key> [key ...] 計(jì)算HLL的近似基數(shù)器联,可以計(jì)算多個(gè)HLL,比如用HLL存儲每天的UV婿崭,計(jì)算一周的UV可以使用7天的UV合并計(jì)算即可
(2)實(shí)例
3拨拓、pfmerge
(1)格式
pfmerge<destkey><sourcekey> [sourcekey ...] 將一個(gè)或多個(gè)HLL合并后的結(jié)果存儲在另一個(gè)HLL中,比如每月活躍用戶可以使用每天的活躍用戶來合并計(jì)算可得
(2)實(shí)例
3氓栈、Geospatial
3.1渣磷、簡介
Redis 3.2 中增加了對GEO類型的支持。GEO授瘦,Geographic醋界,地理信息的縮寫竟宋。該類型,就是元素的2維坐標(biāo)形纺,在地圖上就是經(jīng)緯度丘侠。redis基于該類型,提供了經(jīng)緯度設(shè)置逐样,查詢蜗字,范圍查詢,距離查詢脂新,經(jīng)緯度Hash等常見操作挪捕。
3.2、命令
1争便、geoadd
(1)格式
geoadd<key>< longitude><latitude><member> [longitude latitude member...] 添加地理位置(經(jīng)度级零,緯度,名稱)
(2)實(shí)例
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
兩極無法直接添加滞乙,一般會下載城市數(shù)據(jù)妄讯,直接通過 Java 程序一次性導(dǎo)入。
有效的經(jīng)度從 -180 度到 180 度酷宵。有效的緯度從 -85.05112878 度到 85.05112878 度亥贸。
當(dāng)坐標(biāo)位置超出指定范圍時(shí),該命令將會返回一個(gè)錯(cuò)誤浇垦。
已經(jīng)添加的數(shù)據(jù)炕置,是無法再次往里面添加的。
2男韧、geopos
(1)格式
geopos <key><member> [member...] 獲得指定地區(qū)的坐標(biāo)值
(2)實(shí)例
3朴摊、geodist
(1)格式
geodist<key><member1><member2> [m|km|ft|mi ] 獲取兩個(gè)位置之間的直線距離
(2)實(shí)例
獲取兩個(gè)位置之間的直線距離
單位:
m 表示單位為米[默認(rèn)值]。
km 表示單位為千米此虑。
mi 表示單位為英里甚纲。
ft 表示單位為英尺。
如果用戶沒有顯式地指定單位參數(shù)朦前, 那么 GEODIST 默認(rèn)使用米作為單位
4介杆、georadius
(1)格式
georadius<key>< longitude><latitude>radius m|km|ft|mi 以給定的經(jīng)緯度為中心,找出某一半徑內(nèi)的元素
經(jīng)度 緯度 距離 單位
(2)實(shí)例