最近學(xué)習(xí)了一段時(shí)間的redis钓账,以下是筆記毁渗,歡迎各位大佬們留言評(píng)論灌诅,若有不對(duì)的地方請指出芳来,我會(huì)及時(shí)更正!蟹蟹大家猜拾!
目錄:
1.redis簡介
2.redis豐富的數(shù)據(jù)結(jié)構(gòu)
3.redis事務(wù)即舌、分布式鎖
4.持久化
5.reids存儲(chǔ)使用的數(shù)據(jù)模型
6.redis淘汰策略
7.redis底層數(shù)據(jù)類型介紹
redis簡介
基于內(nèi)存存儲(chǔ)的非關(guān)系型數(shù)據(jù)庫、
性能強(qiáng)勁挎袜、單線程運(yùn)作侥涵、請求入隊(duì)
支持的數(shù)據(jù)結(jié)構(gòu)豐富:string、list宋雏、hash芜飘、set、zset
支持RDB和AOF持久化
redis支持的數(shù)據(jù)結(jié)構(gòu):string
set key value
setnx key value
setex key seconds value
exists key
get key
mget key1 key2 …
mset k1 v1 k2 v2 …
incr key
getset key value
pttl key
.....................................
redis支持的數(shù)據(jù)結(jié)構(gòu):hash
hset key field value
hsetnx key value 如果field存在則不改變
hget key field
hdel key fidle1 field2 …
hlen key 返回字段個(gè)數(shù)
hmget key f1 f2 …
hmset key f1 v1 f2 v2 …
hexists key field
hkeys key
hvals key
hgetall key
hincrby key field
hincrbyfloat key
field
..............................................
Ps:哈希類型的內(nèi)部編碼有ziplist 和 hashtable
當(dāng)哈希元素小于hash-max-ziplist-entries配置磨总、同時(shí)所有值都小于hash-max-ziplist-value配置嗦明,就會(huì)利用ziplist這樣更節(jié)省內(nèi)存?? ?/etc/redis/redis.conf
object encoding key 可以查看數(shù)據(jù)的編碼,我們可以發(fā)現(xiàn)蚪燕,當(dāng)超過上述兩個(gè)配置的值后娶牌,編碼會(huì)發(fā)生改變
redis支持的數(shù)據(jù)結(jié)構(gòu):list
lpush key v1 v2 …
rpush key v1 v2 …
lrange start end 0 -1查找所有
linsert key before/after value
lindex key index -1返回最后一個(gè)
llen key
lpop key
rpopkey?
lremkey count value??
…………………........................
Ps:內(nèi)部編碼有ziplist 和 linkedlist
當(dāng)list元素小于list-max-ziplist-entries配置、同時(shí)所有值都小于list-max-ziplist-value配置馆纳,就會(huì)利用ziplist這樣更節(jié)省內(nèi)存
Redis3.2版本新增了quicklist編碼诗良,以ziplist為節(jié)點(diǎn)的linkedlist,結(jié)合了兩者的優(yōu)勢
redis支持的數(shù)據(jù)結(jié)構(gòu):set
set:無索引鲁驶、無重復(fù)鉴裹、無序
sadd key v1 v2 …
srem key v1 v2 …
scard key 計(jì)算元素個(gè)數(shù)
sismember key element 判斷元素是否在集合中
spop key 隨機(jī)彈出元素
smembers key 返回所有元素
sinter key1 key2 返回兩個(gè)集合交集
sunion key1 key2 返回兩個(gè)集合并集?
sdiff key1 key2 返回兩個(gè)集合差集,順序影響結(jié)果
…………………..................
Ps:內(nèi)部編碼有intset 和 hashtable
當(dāng)set集合元素小于set-max-intset-entries配置默認(rèn)512個(gè)且都是整數(shù)钥弯,就會(huì)利用intset這樣更節(jié)省內(nèi)存径荔,當(dāng)數(shù)量沒有達(dá)到,但是其中只要有一個(gè)元素不是整數(shù)脆霎,編碼都會(huì)變?yōu)閔ashtable
redis支持的數(shù)據(jù)結(jié)構(gòu):zset
zset:元素有分?jǐn)?shù)作為排序依據(jù)总处、無重復(fù)
zadd key score member …
zcard key 計(jì)算成員個(gè)數(shù)
zscore key member 返回這個(gè)成員分?jǐn)?shù)
zrank key member 從低到高返回排名
zrevrank key member 從高到低返回排名,從0開始算的
zrem key member 移除元素
zincrby key increment member 給元素增加分?jǐn)?shù)
zrange key start end 返回排名的成員從低到高?
zrevrange key start end 返回排名的成員反之
zcount key min max 返回指定分?jǐn)?shù)范圍元素個(gè)數(shù)
zremrangebyscore key min max 刪除指定分?jǐn)?shù)范圍成員
…………………..................................
Ps:內(nèi)部編碼有ziplist 和 skiplist
當(dāng)zset集合元素個(gè)數(shù)小于zset-max-ziplist-entries配置默認(rèn)128睛蛛,且每個(gè)元素的值都小于zset-max-ziplist-value默認(rèn)64字節(jié)鹦马,就會(huì)使用ziplist編碼胧谈,否則編碼都會(huì)變?yōu)閟kiplist,因?yàn)槌^設(shè)置值荸频,讀寫效率就會(huì)下降
redis事務(wù)
1.multi 事務(wù)開始
2.一個(gè)事務(wù)需要執(zhí)行的操作第岖,例如多條命令行
3.exec結(jié)束事務(wù);discard取消事務(wù)
redis事務(wù)不支持回滾试溯,只要事務(wù)開始到事務(wù)提交蔑滓,期間只要有命令執(zhí)行成功,就會(huì)改變遇绞,所以redis提供了watch命令解決這個(gè)問題
1.watch key
2.multi
3.命令…
4.exec提交事務(wù)
watch代表監(jiān)聽一個(gè)key键袱,執(zhí)行事務(wù)的時(shí)候如果這個(gè)key發(fā)生了改變,事務(wù)執(zhí)行就不成功摹闽,返回nil蹄咖,這個(gè)時(shí)候需要人工干預(yù)
redis分布式鎖
這些命令是分布式鎖需要用到的命令,具體的流程如圖所示
setnx key value 返回1則代表拿到鎖付鹿,返回0則代表沒有拿到鎖
expire key timeout 為key設(shè)置一個(gè)超時(shí)時(shí)間澜汤,防止出現(xiàn)鎖永遠(yuǎn)不被釋放的情況
del key 釋放鎖
getset key
value
redis持久化
rdb
RDB持久化是把當(dāng)前進(jìn)程數(shù)據(jù)生成快照保存到硬盤的過程,觸發(fā)RDB持久化過程分為手動(dòng)觸發(fā)和自動(dòng)觸發(fā)舵匾。
手動(dòng):save bgsave俊抵,執(zhí)行save命令的時(shí)候會(huì)阻塞redis直到RDB完成為止;bgsave則是fork出子線程完成RDB操作坐梯,不會(huì)阻塞redis徽诲,只是在fork子線程會(huì)阻塞,這個(gè)時(shí)間不會(huì)很長吵血;
自動(dòng):在配置文件中有默認(rèn)save 900 1 save 300 10 save 60 10000谎替,只要滿足這三個(gè)其中的任意一個(gè)都會(huì)執(zhí)行自動(dòng)bgsave;
redis的save m n 是通過serverCron,dirty,lastsave時(shí)間戳來實(shí)現(xiàn)的蹋辅;
serverCron檢查配置文件钱贯、
dirty計(jì)數(shù)器記錄數(shù)據(jù)庫一共執(zhí)行了多少次修改,執(zhí)行bgsave會(huì)置零
lastsave是記錄上次執(zhí)行bgsave的時(shí)間
當(dāng)前時(shí)間-lastsave>m&&dirty>=n
RDB文件載入工作是在服務(wù)器啟動(dòng)時(shí)自動(dòng)執(zhí)行侦另,AOF優(yōu)先級(jí)高于RDB秩命,所以如果要使用RDB就要關(guān)閉AOF,redis載入RDB文件期間淋肾,redis處于阻塞狀態(tài)硫麻,直到載入完成為止;
aof
AOF持久化是將每次redis執(zhí)行的命令記錄起來樊卓,類似于mysqlbinlog,redis重啟時(shí)執(zhí)行aof文件中的命令來恢復(fù)數(shù)據(jù)
配置文件開啟appendonly yes 杠河,aof是自動(dòng)記錄碌尔,aof執(zhí)行流程浇辜;
1.命令追加:將命令追加到緩沖區(qū)aof_buf,這是為了避免磁盤io導(dǎo)致性能降低唾戚;
2.文件寫入和文件同步:緩沖區(qū)的文件同步策略參數(shù)由appendfsync控制
Everysec柳洋,默認(rèn),每次的命令都寫入aof文件中叹坦,但是每秒執(zhí)行一次文件同步操作熊镣;
Always:每次的命令寫入aof文件中,并且執(zhí)行文件同步操作募书,速度最慢但是最安全绪囱;
No:每次命令寫入aof文件中,但是由操作系統(tǒng)決定何時(shí)執(zhí)行文件同步莹捡,速度最快但是不安全鬼吵;?
3.文件重寫:子線程執(zhí)行,不影響主線程篮赢,目的主要是消除重復(fù)命令齿椅,過期的數(shù)據(jù),減少aof文件的大衅羝涣脚;
redis數(shù)據(jù)模型
涉及到內(nèi)存分配器(如jemalloc)、簡單動(dòng)態(tài)字符串(SDS)寥茫、5種對(duì)象類型及內(nèi)部編碼涩澡、redisObject。
下圖是執(zhí)行set hello world時(shí)坠敷,涉及到的數(shù)據(jù)模型
dictEntry:每個(gè)鍵值對(duì)都會(huì)有一個(gè)dictEntry妙同,里面存儲(chǔ)了指向key和value的指針,next指向下一個(gè)dictEntry膝迎;
Key:并不是直接存儲(chǔ)hello粥帚,而是存儲(chǔ)在sds結(jié)構(gòu)中;
Value:redisObject限次,redis支持的五種數(shù)據(jù)類型都是通過redisObject來存儲(chǔ)的芒涡,type代表類型,ptr指向?qū)ο笏诘刂仿袈址愋偷闹惦m然通過redisObject包裝费尽,但是仍然通過sds來存儲(chǔ);
Jemalloc:redis內(nèi)存分配器有三種羊始,libc旱幼、jemalloc、tcmalloc突委,默認(rèn)是jemalloc柏卤,在64位系統(tǒng)中冬三,將內(nèi)存分為,小缘缚,大勾笆,巨大三個(gè)級(jí)別,每個(gè)級(jí)別又有很多區(qū)域桥滨,redis存儲(chǔ)數(shù)據(jù)時(shí)窝爪,會(huì)找最合適的區(qū)域來存儲(chǔ);
RedisObject:類似于這樣的屬性RedisObject(type, lru, redis, refcount, encoding)
type齐媒,表示對(duì)象的類型蒲每,也就是redis支持的數(shù)據(jù)結(jié)構(gòu),占4個(gè)比特里初;
encoding啃勉,表示對(duì)象的內(nèi)部編碼,占4個(gè)比特双妨;
lru淮阐,記錄了對(duì)象最后一次被命令訪問的時(shí)間,對(duì)比這個(gè)時(shí)間和當(dāng)前時(shí)間可以得出這個(gè)對(duì)象的空轉(zhuǎn)刁品,從而在redis內(nèi)存回收機(jī)制是-lru的時(shí)候泣特,會(huì)優(yōu)先選擇空轉(zhuǎn)時(shí)間最長的對(duì)象進(jìn)行釋放,4.0版本占24比特挑随,2.6版本占22比特状您;
refcount,表示該對(duì)象被引用的次數(shù)兜挨,整型膏孟,初始化系對(duì)象為1,被新的調(diào)用拌汇,+1柒桑,當(dāng)對(duì)象不再被新的調(diào)用,-1噪舀,如果為0魁淳,則釋放該對(duì)象,refcount>1被稱為共享對(duì)象与倡,因?yàn)槌霈F(xiàn)重復(fù)的對(duì)象界逛,redis不會(huì)重復(fù)創(chuàng)建;
ptr纺座,指向具體的數(shù)據(jù)息拜,比如字符串就指向sds;
SDS:簡單動(dòng)態(tài)字符串(simple dynamic string),SDS(len, free, buf)
Len:表示buf已經(jīng)使用的長度
Free:表示buf未使用的長度
Buf:字節(jié)數(shù)組
與C字符串類似的是该溯,buf也是以’\0’作為結(jié)尾岛抄;
redis淘汰策略
noeviction: 不刪除策略, 達(dá)到最大內(nèi)存限制時(shí), 如果需要更多內(nèi)存, 直接返回錯(cuò)誤信息别惦。
allkeys-lru: 所有key通用; 優(yōu)先刪除最近最少使用(less recently used ,LRU) 的 key狈茉。
volatile-lru: 只限于設(shè)置了?expire?的部分; 優(yōu)先刪除最近最少使用(less recently used ,LRU) 的 key。
allkeys-random: 所有key通用; 隨機(jī)刪除一部分 key掸掸。volatile-random: 只限于設(shè)置了?expire?的部分; 隨機(jī)刪除一部分 key氯庆。
volatile-ttl: 只限于設(shè)置了?expire?的部分; 優(yōu)先刪除剩余時(shí)間(time to live,TTL) 短的key。
redis簡單正則查詢key
合理的運(yùn)用一些正則符號(hào)扰付,比如? *? ?? []??? 減少去全量查找產(chǎn)生的內(nèi)存開銷
keys hell* 可以查到到比如 hello helloo?? *代表一位或者多位
keys hell? 可以查找到比如 hello hella???? ?代表一位
keys hel[lo]o可以查到hello heloo???? []里面填的什么內(nèi)容就會(huì)在這個(gè)數(shù)組里面匹配堤撵,里面也可以放正則表達(dá)式,比如[a-zA-Z]
redis數(shù)據(jù)類型詳解
linkedList:
linkedList:雙向列表的每個(gè)數(shù)據(jù)節(jié)點(diǎn)都有兩個(gè)指針羽莺,分別指向前面一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)实昨,因此從雙向列表中的每一個(gè)節(jié)點(diǎn)都很容易獲取前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn);
這個(gè)是linkedList的數(shù)據(jù)結(jié)構(gòu)
特點(diǎn):訪問快速盐固,從頭部或者尾部插入元素快荒给,時(shí)間復(fù)雜度為o(1),自帶鏈表長度的屬性刁卜,所以得出鏈表長度的時(shí)間復(fù)雜度為o(1)志电;
zipList:
適用于存儲(chǔ)小的整數(shù)值或者比較簡短的字符串,如果超過redis的配置蛔趴,會(huì)自動(dòng)轉(zhuǎn)換為linkedlist挑辆;
之所以達(dá)到了壓縮的效果,是因?yàn)榇鎯?chǔ)的原理避開了內(nèi)存碎片孝情,它占用的是一大塊內(nèi)存鱼蝉,每一個(gè)entry都是連續(xù)再一起的,而linkedlist是通過指針來指向的箫荡,首先指針會(huì)占內(nèi)存魁亦,內(nèi)存分配器分配內(nèi)存的時(shí)候并不是數(shù)據(jù)多大就分配多大內(nèi)存,而是分配一塊合適這個(gè)數(shù)據(jù)的內(nèi)存空間菲茬,也造成了內(nèi)存浪費(fèi)吉挣;
壓縮的原理:entry里面的prevrawlen屬性會(huì)記錄前一個(gè)entry占用的字節(jié)總數(shù),如果前一個(gè)entry的長度小于254字節(jié)婉弹,那么prevrawlen屬性就會(huì)用1個(gè)字節(jié)的空間保存這個(gè)長度值睬魂,反之上一個(gè)entry大于等于254,就會(huì)用5個(gè)字節(jié)保存這個(gè)值镀赌;
弊端:連鎖更新效應(yīng)氯哮,假設(shè)現(xiàn)在有一個(gè)ziplist,有10個(gè)entry商佛,每個(gè)entry的大小都是253喉钢,那么每個(gè)prevrawlen的都是占用1個(gè)字節(jié)姆打,但是我現(xiàn)在插入了一條255的entry,這個(gè)時(shí)候新entry的后一個(gè)entry的prevrawlen屬性就需要擴(kuò)大肠虽,導(dǎo)致這個(gè)entry的大小變了幔戏,又導(dǎo)致后面的也變了,一直變到最后一個(gè)entry税课,這樣子就一直連續(xù)在做內(nèi)存分配操作闲延,時(shí)間復(fù)雜度大大提升,最壞的情況是o(N^2)
intset:
創(chuàng)建一個(gè)set集合的時(shí)候韩玩,首先會(huì)指定一個(gè)默認(rèn)編碼垒玲,默認(rèn)是INTSET_ENC_INT16
執(zhí)行插入新元素的時(shí)候,會(huì)根據(jù)value得到合適的編碼找颓,然后比較當(dāng)前集合的編碼合愈,如果不滿足就對(duì)集合進(jìn)行升級(jí);
如果滿足击狮,先判斷集合元素是否存在佛析,如果存在就直接返回,如果不存在帘不,擴(kuò)大集合空間说莫,選新的內(nèi)存地址,添加新元素寞焙;
intset內(nèi)部只有”編碼升級(jí)”的過程储狭,沒有”降級(jí)”的操作。當(dāng)將唯一一個(gè)高位元素從將集合移除時(shí)捣郊,此時(shí)辽狈,集合不會(huì)轉(zhuǎn)換為低位編碼集合。
hashtable:
table屬性是一個(gè)數(shù)組呛牲,里面存儲(chǔ)的都是一個(gè)指向dictEntry的指針刮萌,而每個(gè)dictEntry都有一個(gè)next指向下一個(gè)元素的指針屬性,如果出現(xiàn)鍵哈希值相同的情況娘扩,會(huì)被分到同一個(gè)索引上着茸,然后它們通過next就形成了一個(gè)單向鏈表,從而解決了哈希沖突的問題琐旁;
Set應(yīng)用場景:它的特性是無序涮阔,不重復(fù),求交集灰殴、并集敬特、差集很方便;適用于存儲(chǔ)用戶粉絲,計(jì)算共同好友伟阔;