概念
Redis是一個(gè)開(kāi)源的使用C語(yǔ)言編寫(xiě)、支持網(wǎng)絡(luò)埠况、可基于內(nèi)存亦可持久化的日志型、Key-Value內(nèi)存數(shù)據(jù)庫(kù)棵癣,并提供多種語(yǔ)言的API辕翰。
它也常被稱為數(shù)據(jù)結(jié)構(gòu)服務(wù)器,因?yàn)橹担╲alue)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等類型狈谊。
Redis支持?jǐn)?shù)據(jù)的備份喜命,即master-slave模式的數(shù)據(jù)備份。
Redis 優(yōu)勢(shì)
- 性能極高 – Redis能讀的速度是110000次/s,寫(xiě)的速度是81000次/s 的畴。
- 豐富的數(shù)據(jù)類型 – Redis支持二進(jìn)制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 數(shù)據(jù)類型操作渊抄。
- 原子 – Redis的所有操作都是原子性的,同時(shí)Redis還支持對(duì)幾個(gè)操作全并后的原子性執(zhí)行丧裁。
- 豐富的特性 – Redis還支持 publish/subscribe, 通知, key 過(guò)期等等特性护桦。
redis相比memcached的區(qū)別聯(lián)系
Redis使用單線程,而Memcached是多線程煎娇,
Redis使用直接申請(qǐng)內(nèi)存的方式來(lái)存儲(chǔ)數(shù)據(jù)二庵,并且可以配置虛擬內(nèi)存;Memcached使用預(yù)分配的內(nèi)存池的方式缓呛。
Redis實(shí)現(xiàn)了持久化和主從同步影晓,容災(zāi)性會(huì)更強(qiáng)。而Memcached只是存放在內(nèi)存中湾盒,服務(wù)器故障關(guān)機(jī)后數(shù)據(jù)就會(huì)消失门怪,
Redis支持五種數(shù)據(jù)類型:string,list, Hash,set及zset。而Memcached只是簡(jiǎn)單的key與value
Redis的優(yōu)點(diǎn):對(duì)數(shù)據(jù)高并發(fā)讀寫(xiě)票髓、對(duì)海量數(shù)據(jù)的高效率存儲(chǔ)和訪問(wèn)攀涵、對(duì)數(shù)據(jù)的可擴(kuò)展性和高可用性
Redis的應(yīng)用場(chǎng)景:取最新N個(gè)數(shù)據(jù)的操作、排行榜應(yīng)用取TOP N操作洽沟、需要精準(zhǔn)設(shè)定過(guò)期時(shí)間的應(yīng)用以故、計(jì)數(shù)器應(yīng)用、獲取某段時(shí)間所有數(shù)據(jù)排重值的唯一性操作裆操、實(shí)時(shí)消息系統(tǒng)怒详、構(gòu)建隊(duì)列系統(tǒng)、作緩存踪区。
redis核心對(duì)象
Redis內(nèi)部使用一個(gè)redisObject對(duì)象來(lái)表示所有的key和value昆烁。
redisObject最主要的信息如圖所示:
- type代表一個(gè)value對(duì)象具體是何種數(shù)據(jù)類型
- encoding是不同數(shù)據(jù)類型在redis內(nèi)部的存儲(chǔ)方式,比如:type=string代表value存儲(chǔ)的是一個(gè)普通字符串朽缴,那么對(duì)應(yīng)的encoding可以是raw或者是int善玫,如果是int則代表實(shí)際redis內(nèi)部是按數(shù)值型類存儲(chǔ)和表示這個(gè)字符串的,當(dāng)然前提是這個(gè)字符串本身可以用數(shù)值表示,比如:"123" "456"這樣的字符串茅郎。
- vm字段蜗元,只有打開(kāi)了Redis的虛擬內(nèi)存功能,此字段才會(huì)真正的分配內(nèi)存系冗,該功能默認(rèn)是關(guān)閉狀態(tài)的奕扣。
五種數(shù)據(jù)結(jié)構(gòu)
注:embstr編碼創(chuàng)建字符串對(duì)象只需內(nèi)存分配一次,調(diào)用一次內(nèi)存釋放函數(shù)掌敬,而raw都需要兩次惯豆。可使用INCR和INCRBY生成分布式系統(tǒng)唯一序列號(hào)ID
雙向鏈表便于在表的兩端操作奔害,但是它的內(nèi)存地址不連續(xù)楷兽,容易產(chǎn)生內(nèi)存碎片。ziplist是一整塊連續(xù)內(nèi)存华临,存儲(chǔ)效率很高芯杀。但它每次數(shù)據(jù)變動(dòng)都會(huì)引發(fā)一次內(nèi)存的realloc。所以quicklist結(jié)合了雙向鏈表和ziplist的優(yōu)點(diǎn)雅潭,是一個(gè)雙向無(wú)環(huán)鏈表揭厚,它的每一個(gè)節(jié)點(diǎn)都是一個(gè)ziplist。
五種數(shù)據(jù)類型的使用和內(nèi)部實(shí)現(xiàn)方式:
1)String
常用命令:set/get/decr/incr/mget等扶供;
應(yīng)用場(chǎng)景:String是最常用的一種數(shù)據(jù)類型筛圆,普通的key/value存儲(chǔ)都可以歸為此類;
實(shí)現(xiàn)方式:String在redis內(nèi)部存儲(chǔ)默認(rèn)就是一個(gè)字符串椿浓,被redisObject所引用太援,當(dāng)遇到incr、decr等操作時(shí)會(huì)轉(zhuǎn)成數(shù)值型進(jìn)行計(jì)算扳碍,此時(shí)redisObject的encoding字段為int粉寞。
2)Hash
常用命令:hget/hset/hgetall等
應(yīng)用場(chǎng)景:我們要存儲(chǔ)一個(gè)用戶信息對(duì)象數(shù)據(jù),其中包括用戶ID左腔、用戶姓名、年齡和生日捅儒,通過(guò)用戶ID我們希望獲取該用戶的姓名或者年齡或者生日液样;
實(shí)現(xiàn)方式:Redis的Hash實(shí)際是內(nèi)部存儲(chǔ)的Value為一個(gè)HashMap,并提供了直接存取這個(gè)Map成員的接口巧还。如圖2所示鞭莽,Key是用戶ID, value是一個(gè)Map。這個(gè)Map的key是成員的屬性名麸祷,value是屬性值澎怒。這樣對(duì)數(shù)據(jù)的修改和存取都可以直接通過(guò)其內(nèi)部Map的Key(Redis里稱內(nèi)部Map的key為field), 也就是通過(guò) key(用戶ID) + field(屬性標(biāo)簽) 就可以操作對(duì)應(yīng)屬性數(shù)據(jù)。當(dāng)前HashMap的實(shí)現(xiàn)有兩種方式:當(dāng)HashMap的成員比較少時(shí)Redis為了節(jié)省內(nèi)存會(huì)采用類似一維數(shù)組的方式來(lái)緊湊存儲(chǔ)阶牍,而不會(huì)采用真正的HashMap結(jié)構(gòu)喷面,這時(shí)對(duì)應(yīng)的value的redisObject的encoding為zipmap星瘾,當(dāng)成員數(shù)量增大時(shí)會(huì)自動(dòng)轉(zhuǎn)成真正的HashMap,此時(shí)encoding為ht。
3)List
常用命令:lpush/rpush/lpop/rpop/lrange等惧辈;
應(yīng)用場(chǎng)景:Redis list的應(yīng)用場(chǎng)景非常多琳状,也是Redis最重要的數(shù)據(jù)結(jié)構(gòu)之一,比如twitter的關(guān)注列表盒齿,粉絲列表等都可以用Redis的list結(jié)構(gòu)來(lái)實(shí)現(xiàn)念逞;
實(shí)現(xiàn)方式:Redis list的實(shí)現(xiàn)為一個(gè)雙向鏈表,即可以支持反向查找和遍歷边翁,更方便操作翎承,不過(guò)帶來(lái)了部分額外的內(nèi)存開(kāi)銷(xiāo),Redis內(nèi)部的很多實(shí)現(xiàn)符匾,包括發(fā)送緩沖隊(duì)列等也都是用的這個(gè)數(shù)據(jù)結(jié)構(gòu)叨咖。
4)Set
常用命令:sadd/spop/smembers/sunion等;
應(yīng)用場(chǎng)景:Redis set對(duì)外提供的功能與list類似是一個(gè)列表的功能待讳,特殊之處在于set是可以自動(dòng)排重的芒澜,當(dāng)你需要存儲(chǔ)一個(gè)列表數(shù)據(jù),又不希望出現(xiàn)重復(fù)數(shù)據(jù)時(shí)创淡,set是一個(gè)很好的選擇痴晦,并且set提供了判斷某個(gè)成員是否在一個(gè)set集合內(nèi)的重要接口,這個(gè)也是list所不能提供的琳彩;
實(shí)現(xiàn)方式:set 的內(nèi)部實(shí)現(xiàn)是一個(gè) value永遠(yuǎn)為null的HashMap誊酌,實(shí)際就是通過(guò)計(jì)算hash的方式來(lái)快速排重的,這也是set能提供判斷一個(gè)成員是否在集合內(nèi)的原因露乏。
5)Sorted Set
常用命令:zadd/zrange/zrem/zcard等碧浊;
應(yīng)用場(chǎng)景:Redis sorted set的使用場(chǎng)景與set類似,區(qū)別是set不是自動(dòng)有序的瘟仿,而sorted set可以通過(guò)用戶額外提供一個(gè)優(yōu)先級(jí)(score)的參數(shù)來(lái)為成員排序箱锐,并且是插入有序的,即自動(dòng)排序劳较。當(dāng)你需要一個(gè)有序的并且不重復(fù)的集合列表驹止,那么可以選擇sorted set數(shù)據(jù)結(jié)構(gòu),比如twitter 的public timeline可以以發(fā)表時(shí)間作為score來(lái)存儲(chǔ)观蜗,這樣獲取時(shí)就是自動(dòng)按時(shí)間排好序的臊恋。
實(shí)現(xiàn)方式:Redis sorted set的內(nèi)部使用HashMap和跳躍表(SkipList)來(lái)保證數(shù)據(jù)的存儲(chǔ)和有序,HashMap里放的是成員到score的映射墓捻,而跳躍表里存放的是所有的成員抖仅,排序依據(jù)是HashMap里存的score,使用跳躍表的結(jié)構(gòu)可以獲得比較高的查找效率,并且在實(shí)現(xiàn)上比較簡(jiǎn)單。
使用 Redis 作為 LRU 緩存
maxmemory 配置指令
maxmemory 配置指令是用來(lái)配置 Redis 為數(shù)據(jù)集使用指定的內(nèi)存容量大小撤卢』吩洌可以使用 redis.conf 文件來(lái)設(shè)置配置指令,或者之后在運(yùn)行時(shí)使用 CONFIG SET 命令凸丸。
設(shè)置 maxmemory 為 0拷邢,表示沒(méi)有內(nèi)存限制。這是 64 位系統(tǒng)的默認(rèn)行為屎慢,32 位的系統(tǒng)則使用 3G 大小作為隱式的內(nèi)存限制瞭稼。
當(dāng)指定的內(nèi)存容量到達(dá)時(shí),需要選擇不同的行為腻惠,即策略环肘。Redis 可以只為命令返回錯(cuò)誤,這樣將占用更多的內(nèi)存集灌,或者每次添加新數(shù)據(jù)時(shí)悔雹,回收掉一些舊的數(shù)據(jù)以避免內(nèi)存限制。
回收策略(Eviction policies)
當(dāng) maxmemory 限制到達(dá)的時(shí)候欣喧,Redis 將采取的準(zhǔn)確行為是由 maxmemory-policy 配置指令配置的腌零。
以下策略可用:
- noeviction:當(dāng)?shù)竭_(dá)內(nèi)存限制時(shí)返回錯(cuò)誤。當(dāng)客戶端嘗試執(zhí)行命令時(shí)會(huì)導(dǎo)致更多內(nèi)存占用(大多數(shù)寫(xiě)命令唆阿,除了 DEL 和一些例外)益涧。
- allkeys-lru:回收最近最少使用(LRU)的鍵,為新數(shù)據(jù)騰出空間驯鳖。
- volatile-lru:回收最近最少使用(LRU)的鍵闲询,但是只回收有設(shè)置過(guò)期的鍵,為新數(shù)據(jù)騰出空間浅辙。
- allkeys-random:回收隨機(jī)的鍵扭弧,為新數(shù)據(jù)騰出空間。
- volatile-random:回收隨機(jī)的鍵记舆,但是只回收有設(shè)置過(guò)期的鍵鸽捻,為新數(shù)據(jù)騰出空間。
- volatile-ttl:回收有設(shè)置過(guò)期的鍵泽腮,嘗試先回收離 TTL 最短時(shí)間的鍵泊愧,為新數(shù)據(jù)騰出空間。
當(dāng)沒(méi)有滿足前提條件的話盛正,volatile-lru,volatile-random 和 volatile-ttl 策略就表現(xiàn)得和 noeviction 一樣了屑埋。
選擇正確的回收策略是很重要的豪筝,取決于你的應(yīng)用程序的訪問(wèn)模式,但是,你可以在程序運(yùn)行時(shí)重新配置策略续崖,使用 INFO 輸出來(lái)監(jiān)控緩存命中和錯(cuò)過(guò)的次數(shù)敲街,以調(diào)優(yōu)你的設(shè)置。
一般經(jīng)驗(yàn)規(guī)則:
- 如果你期待你的用戶請(qǐng)求呈現(xiàn)冪律分布(power-law distribution)严望,也就是多艇,你期待一部分子集元素被訪問(wèn)得遠(yuǎn)比其他元素多,可以使用 allkeys-lru 策略像吻。在你不確定時(shí)這是一個(gè)好的選擇峻黍。
- 如果你是循環(huán)周期的訪問(wèn),所有的鍵被連續(xù)掃描拨匆,或者你期待請(qǐng)求正常分布(每個(gè)元素以相同的概率被訪問(wèn))姆涩,可以使用 allkeys-random 策略。
- 如果你想能給 Redis 提供建議惭每,通過(guò)使用你創(chuàng)建緩存對(duì)象的時(shí)候設(shè)置的 TTL 值骨饿,確定哪些對(duì)象應(yīng)該被過(guò)期,你可以使用 volatile-ttl 策略台腥。
當(dāng)你想使用單個(gè)實(shí)例來(lái)實(shí)現(xiàn)緩存和持久化一些鍵宏赘,allkeys-lru 和 volatile-random 策略會(huì)很有用。但是黎侈,通常最好是運(yùn)行兩個(gè) Redis 實(shí)例來(lái)解決這個(gè)問(wèn)題察署。
近似的 LRU 算法(Approximated LRU algorithm)
Redis 的 LRU 算法不是一個(gè)精確的實(shí)現(xiàn)。這意味著 Redis 不能選擇最佳候選鍵來(lái)回收蜓竹,也就是最久錢(qián)被訪問(wèn)的那些鍵箕母。相反,會(huì)嘗試運(yùn)營(yíng)一個(gè)近似的 LRU 算法俱济,通過(guò)采樣一小部分鍵嘶是,然后在采樣鍵中回收最適合(擁有最久訪問(wèn)時(shí)間)的那個(gè)。
Redis 的 LRU 算法有一點(diǎn)很重要蛛碌,你可以調(diào)整算法的精度聂喇,通過(guò)改變每次回收時(shí)檢查的采樣數(shù)量。這個(gè)參數(shù)可以通過(guò)如下配置指令:
maxmemory-samples 5
漸進(jìn)式rehash過(guò)程
redis常見(jiàn)性能問(wèn)題和解決方案:
(1) Master最好不要做任何持久化工作蔚携,如RDB內(nèi)存快照和AOF日志文件
(2) 如果數(shù)據(jù)比較重要希太,某個(gè)Slave開(kāi)啟AOF備份數(shù)據(jù),策略設(shè)置為每秒同步一次
(3) 為了主從復(fù)制的速度和連接的穩(wěn)定性酝蜒,Master和Slave最好在同一個(gè)局域網(wǎng)內(nèi)
(4) 盡量避免在壓力很大的主庫(kù)上增加從庫(kù)
(5) 主從復(fù)制不要用圖狀結(jié)構(gòu)誊辉,用單向鏈表結(jié)構(gòu)更為穩(wěn)定,即:Master <- Slave1 <- Slave2 <- Slave3...
這樣的結(jié)構(gòu)方便解決單點(diǎn)故障問(wèn)題亡脑,實(shí)現(xiàn)Slave對(duì)Master的替換堕澄。如果Master掛了邀跃,可以立刻啟用Slave1做Master,其他不變蛙紫。
redis有時(shí)候會(huì)請(qǐng)求超時(shí)拍屑,已知都是固定的一個(gè)時(shí)間,比如200ms或者500ms坑傅,問(wèn)這是為什么僵驰?
請(qǐng)求redis超時(shí),如果時(shí)間較長(zhǎng)唁毒,比如60s或者75s這樣的蒜茴,可能是redis的timeout配置。目前是時(shí)間較短枉证,200ms就超時(shí)矮男,一般來(lái)說(shuō)redis沒(méi)道理在能處理請(qǐng)求的時(shí)候報(bào)超時(shí)錯(cuò)誤,會(huì)否是現(xiàn)在超過(guò)了redis設(shè)置的最大連接數(shù)maxclients室谚,導(dǎo)致拒絕服務(wù)毡鉴;會(huì)否是client自身的連接超時(shí)設(shè)置。
無(wú)論如何秒赤,CS模式的兩端都有可能是原因
特性
Redis HyperLogLog
Redis HyperLogLog 是用來(lái)做基數(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)存就越多的集合形成鮮明對(duì)比鞍泉。
但是,因?yàn)?HyperLogLog 只會(huì)根據(jù)輸入元素來(lái)計(jì)算基數(shù)肮帐,而不會(huì)儲(chǔ)存輸入元素本身咖驮,所以 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ù)睦刃。
Redis 發(fā)布訂閱
Redis 發(fā)布訂閱(pub/sub)是一種消息通信模式:發(fā)送者(pub)發(fā)送消息,訂閱者(sub)接收消息十酣。
Redis 客戶端可以訂閱任意數(shù)量的頻道眯勾。
下圖展示了頻道 channel1 枣宫, 以及訂閱這個(gè)頻道的三個(gè)客戶端 —— client2 、 client5 和 client1 之間的關(guān)系:
當(dāng)有新消息通過(guò) PUBLISH 命令發(fā)送給頻道 channel1 時(shí)吃环, 這個(gè)消息就會(huì)被發(fā)送給訂閱它的三個(gè)客戶端:
Redis的持久化
提供兩種主要的持久化策略:RDB快照和AOF日志。
RDB快照:Redis支持將當(dāng)前數(shù)據(jù)的快照存成一個(gè)數(shù)據(jù)文件的持久化機(jī)制洋幻。
但是一個(gè)持續(xù)寫(xiě)入的數(shù)據(jù)庫(kù)如何生成快照呢郁轻?Redis借助了fork命令的copy on write機(jī)制。在生成快照時(shí)文留,將當(dāng)前進(jìn)程fork出一個(gè)子進(jìn)程好唯,然后在子進(jìn)程中循環(huán)所有的數(shù)據(jù),將數(shù)據(jù)寫(xiě)成為RDB文件燥翅。
- save:RDB不是在原來(lái)的文件上做增量骑篙,而是全部備份。整個(gè)過(guò)程中森书,主進(jìn)程是不進(jìn)行任何IO操作的
- save: 只管保存靶端,占主進(jìn)程,其它不管凛膏,以后的操作全部阻塞杨名,性能殺器
- BGSAVE:Redis會(huì)在后臺(tái)異步進(jìn)行快照操作,快照同時(shí)還可以響應(yīng)客戶端請(qǐng)求猖毫√ǖ可以通過(guò)lastsave命令獲取最后一次成功執(zhí)行快照的時(shí)間background后臺(tái)存儲(chǔ)
通過(guò)Redis的save指令來(lái)配置RDB快照生成的時(shí)機(jī),比如你可以配置當(dāng)10分鐘以內(nèi)有100次寫(xiě)入就生成快照吁断,也可以配置當(dāng)1小時(shí)內(nèi)有1000次寫(xiě)入就生成快照趁蕊,也可以多個(gè)規(guī)則一起實(shí)施。
Redis的RDB文件不會(huì)壞掉仔役,因?yàn)槠鋵?xiě)操作是在一個(gè)新進(jìn)程中進(jìn)行的掷伙,當(dāng)生成一個(gè)新的RDB文件時(shí),Redis生成的子進(jìn)程會(huì)先將數(shù)據(jù)寫(xiě)到一個(gè)臨時(shí)文件中骂因,然后通過(guò)原子性rename系統(tǒng)調(diào)用將臨時(shí)文件重命名為RDB文件炎咖,這樣在任何時(shí)候出現(xiàn)故障,Redis的RDB文件都總是可用的寒波。同時(shí)乘盼,Redis的RDB文件也是Redis主從同步內(nèi)部實(shí)現(xiàn)中的一環(huán)。
很明顯的看到俄烁,RDB有他的不足绸栅,就是一旦數(shù)據(jù)庫(kù)出現(xiàn)問(wèn)題,那么我們的RDB文件中保存的數(shù)據(jù)并不是全新的页屠,從上次RDB文件生成到Redis停機(jī)這段時(shí)間的數(shù)據(jù)全部丟掉了粹胯。
AOF日志:AOF日志的全稱是append only file蓖柔,它是一個(gè)追加寫(xiě)入的日志文件
一般數(shù)據(jù)庫(kù)的binlog不同的是,AOF文件是可識(shí)別的純文本风纠,它的內(nèi)容就是一個(gè)個(gè)的Redis標(biāo)準(zhǔn)命令况鸣。當(dāng)然,并不是發(fā)送發(fā)Redis的所有命令都要記錄到AOF日志里面竹观,只有那些會(huì)導(dǎo)致數(shù)據(jù)發(fā)生修改的命令才會(huì)追加到AOF文件镐捧。
那么每一條修改數(shù)據(jù)的命令都生成一條日志,那么AOF文件是不是會(huì)很大臭增?答案是肯定的懂酱,AOF文件會(huì)越來(lái)越大,所以Redis又提供了一個(gè)功能誊抛,叫做AOF rewrite列牺。其功能就是重新生成一份AOF文件,新的AOF文件中一條記錄的操作只會(huì)有一次拗窃,而不像一份老文件那樣瞎领,可能記錄了對(duì)同一個(gè)值的多次操作。
其生成過(guò)程和RDB類似并炮,也是fork一個(gè)進(jìn)程默刚,直接遍歷數(shù)據(jù),寫(xiě)入新的AOF臨時(shí)文件逃魄。在寫(xiě)入新文件的過(guò)程中荤西,所有的寫(xiě)操作日志還是會(huì)寫(xiě)到原來(lái)老的AOF文件中,同時(shí)還會(huì)記錄在內(nèi)存緩沖區(qū)中伍俘。當(dāng)重完操作完成后邪锌,會(huì)將所有緩沖區(qū)中的日志一次性寫(xiě)入到臨時(shí)文件中。然后調(diào)用原子性的rename命令用新的AOF文件取代老的AOF文件癌瘾。
AOF是一個(gè)寫(xiě)文件操作觅丰,其目的是將操作日志寫(xiě)到磁盤(pán)上,所以它也同樣會(huì)遇到我們上面說(shuō)的寫(xiě)操作的5個(gè)流程妨退。那么寫(xiě)AOF的操作安全性又有多高呢妇萄。實(shí)際上這是可以設(shè)置的,在Redis中對(duì)AOF調(diào)用write(2)寫(xiě)入后咬荷,何時(shí)再調(diào)用fsync將其寫(xiě)到磁盤(pán)上冠句,通過(guò)appendfsync選項(xiàng)來(lái)控制,下面appendfsync的三個(gè)設(shè)置項(xiàng)幸乒,安全強(qiáng)度逐漸變強(qiáng)懦底。
1)appendfsync no
當(dāng)設(shè)置appendfsync為no的時(shí)候,Redis不會(huì)主動(dòng)調(diào)用fsync去將AOF日志內(nèi)容同步到磁盤(pán)罕扎,所以這一切就完全依賴于操作系統(tǒng)的調(diào)試了聚唐。對(duì)大多數(shù)Linux操作系統(tǒng)丐重,是每30秒進(jìn)行一次fsync,將緩沖區(qū)中的數(shù)據(jù)寫(xiě)到磁盤(pán)上杆查。
2)appendfsync everysec
當(dāng)設(shè)置appendfsync為everysec的時(shí)候扮惦,Redis會(huì)默認(rèn)每隔一秒進(jìn)行一次fsync調(diào)用,將緩沖區(qū)中的數(shù)據(jù)寫(xiě)到磁盤(pán)亲桦。但是當(dāng)這一次的fsync調(diào)用時(shí)長(zhǎng)超過(guò)1秒時(shí)径缅。Redis會(huì)采取延遲fsync的策略,再等一秒鐘烙肺。也就是在兩秒后再進(jìn)行fsync,這一次的fsync就不管會(huì)執(zhí)行多長(zhǎng)時(shí)間都會(huì)進(jìn)行氧卧。這時(shí)候由于在fsync時(shí)文件描述符會(huì)被阻塞桃笙,所以當(dāng)前的寫(xiě)操作就會(huì)阻塞。所以結(jié)論就是沙绝,在絕大多數(shù)情況下搏明,Redis會(huì)每隔一秒進(jìn)行一次fsync。在最壞的情況下闪檬,兩秒鐘會(huì)進(jìn)行一次fsync操作星著。這一操作在大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)中被稱為group commit,就是組合多次寫(xiě)操作的數(shù)據(jù)粗悯,一次性將日志寫(xiě)到磁盤(pán)虚循。
3)appednfsync always
當(dāng)設(shè)置appendfsync為always時(shí),每一次寫(xiě)操作都會(huì)調(diào)用一次fsync样傍,這時(shí)數(shù)據(jù)是最安全的横缔,當(dāng)然,由于每次都會(huì)執(zhí)行fsync衫哥,所以其性能也會(huì)受到影響茎刚。
Redis的內(nèi)存管理機(jī)制
Redis的內(nèi)存管理機(jī)制主要通過(guò)源碼中的zmalloc.h和zmalloc.c兩個(gè)文件來(lái)實(shí)現(xiàn)的。Redis為了方便內(nèi)存的管理撤逢,在分配一塊內(nèi)存之后膛锭,會(huì)將這塊內(nèi)存的大小存入內(nèi)存的頭部。
如圖所示蚊荣,real_ptr是redis調(diào)用malloc后返回的指針初狰。redis將內(nèi)存的大小size存入頭部,size所占據(jù)的內(nèi)存大小是已知的妇押,為size_t類型的長(zhǎng)度跷究,然后返回ret_ptr。當(dāng)需要釋放內(nèi)存的時(shí)候敲霍,ret_ptr被傳給內(nèi)存管理程序俊马。通過(guò)ret_ptr丁存,程序可以很容易的計(jì)算出real_ptr的值,然后將real_ptr傳給free釋放內(nèi)存柴我。
Redis通過(guò)定義一個(gè)數(shù)組來(lái)記錄所有的內(nèi)存分配情況解寝,這個(gè)數(shù)組的長(zhǎng)度為ZMALLOC_MAX_ALLOC_STAT.數(shù)組的每一個(gè)元素代表當(dāng)前程序所分配的內(nèi)存塊的個(gè)數(shù),且內(nèi)存塊的個(gè)數(shù)艘儒,且內(nèi)存塊的大小為該元素的下標(biāo)聋伦。
在源碼中,這個(gè)數(shù)組為zmalloc_allocations界睁。zmalloc_allocations[16]代表已經(jīng)分配的長(zhǎng)度為16bytes的內(nèi)存塊的個(gè)數(shù)觉增。zmalloc.c中有一個(gè)靜態(tài)變量used_memory用來(lái)記錄當(dāng)前分配的內(nèi)存總大小。所以翻斟,總的來(lái)看逾礁,Redis采用的是包裝的mallc/free,相較于Memcached的內(nèi)存管理方法來(lái)說(shuō)访惜,要簡(jiǎn)單很多嘹履。
Redis 數(shù)據(jù)備份與恢復(fù)
SAVE :該命令將在 redis 安裝目錄中創(chuàng)建dump.rdb文件。
如果需要恢復(fù)數(shù)據(jù)债热,只需將備份文件 (dump.rdb) 移動(dòng)到 redis 安裝目錄并啟動(dòng)服務(wù)即可砾嫉。
Redis 事務(wù)
Redis 事務(wù)可以一次執(zhí)行多個(gè)命令, 并且?guī)в幸韵聝蓚€(gè)重要的保證:
- 事務(wù)是一個(gè)單獨(dú)的隔離操作:事務(wù)中的所有命令都會(huì)序列化窒篱、按順序地執(zhí)行焕刮。事務(wù)在執(zhí)行的過(guò)程中,不會(huì)被其他客戶端發(fā)送來(lái)的命令請(qǐng)求所打斷舌剂。
- 事務(wù)是一個(gè)原子操作:事務(wù)中的命令要么全部被執(zhí)行济锄,要么全部都不執(zhí)行。
一個(gè)事務(wù)從開(kāi)始到執(zhí)行會(huì)經(jīng)歷以下三個(gè)階段:
- MULTI 開(kāi)始事務(wù)
- 多個(gè)命令入隊(duì)到事務(wù)中
- EXEC 命令觸發(fā)事務(wù)
DISCARD: 取消事務(wù)霍转,放棄執(zhí)行事務(wù)塊內(nèi)的所有命令荐绝。
EXEC:執(zhí)行所有事務(wù)塊內(nèi)的命令。
MULTI:標(biāo)記一個(gè)事務(wù)塊的開(kāi)始避消。
WATCH key [key...]:監(jiān)視一個(gè)(或多個(gè)) key 低滩,如果在事務(wù)執(zhí)行之前這個(gè)(或這些) key 被其他命令所改動(dòng),那么事務(wù)將被打斷岩喷。
UNWATCH :取消 WATCH 命令對(duì)所有 key 的監(jiān)視恕沫。
Redis 腳本
Redis 腳本使用 Lua 解釋器來(lái)執(zhí)行腳本。 Reids 2.6 版本通過(guò)內(nèi)嵌支持 Lua 環(huán)境纱意。執(zhí)行腳本的常用命令為 EVAL婶溯。
EVAL script numkeys key [key ...] arg [arg ...]
Redis 性能測(cè)試
Redis 性能測(cè)試是通過(guò)同時(shí)執(zhí)行多個(gè)命令實(shí)現(xiàn)的。
redis-benchmark [option] [option value]
Redis 客戶端連接
Redis 通過(guò)監(jiān)聽(tīng)一個(gè) TCP 端口或者 Unix socket 的方式來(lái)接收來(lái)自客戶端的連接,當(dāng)一個(gè)連接建立后迄委,Redis 內(nèi)部會(huì)進(jìn)行以下一些操作:
- 首先褐筛,客戶端 socket 會(huì)被設(shè)置為非阻塞模式,因?yàn)?Redis 在網(wǎng)絡(luò)事件處理上采用的是非阻塞多路復(fù)用模型叙身。
- 然后為這個(gè) socket 設(shè)置 TCP_NODELAY 屬性渔扎,禁用 Nagle 算法
- 然后創(chuàng)建一個(gè)可讀的文件事件用于監(jiān)聽(tīng)這個(gè)客戶端 socket 的數(shù)據(jù)發(fā)送
【Nagle的算法通常會(huì)在TCP程序里添加兩行代碼,在未確認(rèn)數(shù)據(jù)發(fā)送的時(shí)候讓發(fā)送器把數(shù)據(jù)送到緩存里信轿。任何數(shù)據(jù)隨后繼續(xù)直到得到明顯的數(shù)據(jù)確認(rèn)或者直到攢到了一定數(shù)量的數(shù)據(jù)了再發(fā)包晃痴。nagling可以通過(guò)使用TCP_NODELAY 套接字選項(xiàng)關(guān)閉〔坪觯】
Redis 管道技術(shù)
Redis是一種基于客戶端-服務(wù)端模型以及請(qǐng)求/響應(yīng)協(xié)議的TCP服務(wù)倘核。這意味著通常情況下一個(gè)請(qǐng)求會(huì)遵循以下步驟:
- 客戶端向服務(wù)端發(fā)送一個(gè)查詢請(qǐng)求,并監(jiān)聽(tīng)Socket返回即彪,通常是以阻塞模式笤虫,等待服務(wù)端響應(yīng)。
- 服務(wù)端處理命令祖凫,并將結(jié)果返回給客戶端。
Redis 管道技術(shù)可以在服務(wù)端未響應(yīng)時(shí)酬凳,客戶端可以繼續(xù)向服務(wù)端發(fā)送請(qǐng)求惠况,并最終一次性讀取所有服務(wù)端的響應(yīng)。
Redis 分區(qū)
分區(qū)是分割數(shù)據(jù)到多個(gè)Redis實(shí)例的處理過(guò)程宁仔,因此每個(gè)實(shí)例只保存key的一個(gè)子集稠屠。
分區(qū)的優(yōu)勢(shì)
- 通過(guò)利用多臺(tái)計(jì)算機(jī)內(nèi)存的和值,允許我們構(gòu)造更大的數(shù)據(jù)庫(kù)翎苫。
- 通過(guò)多核和多臺(tái)計(jì)算機(jī)权埠,允許我們擴(kuò)展計(jì)算能力;通過(guò)多臺(tái)計(jì)算機(jī)和網(wǎng)絡(luò)適配器煎谍,允許我們擴(kuò)展網(wǎng)絡(luò)帶寬攘蔽。
分區(qū)的不足
redis的一些特性在分區(qū)方面表現(xiàn)的不是很好:
- 涉及多個(gè)key的操作通常是不被支持的。舉例來(lái)說(shuō)呐粘,當(dāng)兩個(gè)set映射到不同的redis實(shí)例上時(shí)满俗,你就不能對(duì)這兩個(gè)set執(zhí)行交集操作。
- 涉及多個(gè)key的redis事務(wù)不能使用作岖。
- 當(dāng)使用分區(qū)時(shí)唆垃,數(shù)據(jù)處理較為復(fù)雜,比如你需要處理多個(gè)rdb/aof文件痘儡,并且從多個(gè)實(shí)例和主機(jī)備份持久化文件辕万。
- 增加或刪除容量也比較復(fù)雜。redis集群大多數(shù)支持在運(yùn)行時(shí)增加、刪除節(jié)點(diǎn)的透明數(shù)據(jù)平衡的能力渐尿,但是類似于客戶端分區(qū)醉途、代理等其他系統(tǒng)則不支持這項(xiàng)特性。然而涡戳,一種叫做presharding的技術(shù)對(duì)此是有幫助的结蟋。
分區(qū)類型
Redis 有兩種類型分區(qū)。 假設(shè)有4個(gè)Redis實(shí)例 R0渔彰,R1嵌屎,R2,R3恍涂,和類似user:1宝惰,user:2這樣的表示用戶的多個(gè)key,對(duì)既定的key有多種不同方式來(lái)選擇這個(gè)key存放在哪個(gè)實(shí)例中再沧。也就是說(shuō)尼夺,有不同的系統(tǒng)來(lái)映射某個(gè)key到某個(gè)Redis服務(wù)。
范圍分區(qū)
最簡(jiǎn)單的分區(qū)方式是按范圍分區(qū)炒瘸,就是映射一定范圍的對(duì)象到特定的Redis實(shí)例淤堵。
比如,ID從0到10000的用戶會(huì)保存到實(shí)例R0顷扩,ID從10001到 20000的用戶會(huì)保存到R1拐邪,以此類推。
這種方式是可行的隘截,并且在實(shí)際中使用扎阶,不足就是要有一個(gè)區(qū)間范圍到實(shí)例的映射表。這個(gè)表要被管理婶芭,同時(shí)還需要各 種對(duì)象的映射表东臀,通常對(duì)Redis來(lái)說(shuō)并非是好的方法。哈希分區(qū)
另外一種分區(qū)方法是hash分區(qū)犀农。這對(duì)任何key都適用惰赋,也無(wú)需是object_name:這種形式,像下面描述的一樣簡(jiǎn)單:
用一個(gè)hash函數(shù)將key轉(zhuǎn)換為一個(gè)數(shù)字呵哨,比如使用crc32 hash函數(shù)谤逼。對(duì)key foobar執(zhí)行crc32(foobar)會(huì)輸出類似93024922的整數(shù)。
對(duì)這個(gè)整數(shù)取模仇穗,將其轉(zhuǎn)化為0-3之間的數(shù)字流部,就可以將這個(gè)整數(shù)映射到4個(gè)Redis實(shí)例中的一個(gè)了。93024922 % 4 = 2纹坐,就是說(shuō)key foobar應(yīng)該被存到R2實(shí)例中枝冀。注意:取模操作是取除的余數(shù),通常在多種編程語(yǔ)言中用%操作符實(shí)現(xiàn)。
常用命令
1)連接操作命令
quit:關(guān)閉連接(connection)
auth:簡(jiǎn)單密碼認(rèn)證
help cmd: 查看cmd幫助果漾,例如:help quit
2)持久化
save:將數(shù)據(jù)同步保存到磁盤(pán)
bgsave:將數(shù)據(jù)異步保存到磁盤(pán)
lastsave:返回上次成功將數(shù)據(jù)保存到磁盤(pán)的Unix時(shí)戳
shundown:將數(shù)據(jù)同步保存到磁盤(pán)球切,然后關(guān)閉服務(wù)
3)遠(yuǎn)程服務(wù)控制
info:提供服務(wù)器的信息和統(tǒng)計(jì)
monitor:實(shí)時(shí)轉(zhuǎn)儲(chǔ)收到的請(qǐng)求
slaveof:改變復(fù)制策略設(shè)置
config:在運(yùn)行時(shí)配置Redis服務(wù)器
4)對(duì)value操作的命令
exists(key):確認(rèn)一個(gè)key是否存在
del(key):刪除一個(gè)key
type(key):返回值的類型
keys(pattern):返回滿足給定pattern的所有key
randomkey:隨機(jī)返回key空間的一個(gè)
keyrename(oldname, newname):重命名key
dbsize:返回當(dāng)前數(shù)據(jù)庫(kù)中key的數(shù)目
expire:設(shè)定一個(gè)key的活動(dòng)時(shí)間(s)
ttl:獲得一個(gè)key的活動(dòng)時(shí)間
select(index):按索引查詢
move(key, dbindex):移動(dòng)當(dāng)前數(shù)據(jù)庫(kù)中的key到dbindex數(shù)據(jù)庫(kù)
flushdb:刪除當(dāng)前選擇數(shù)據(jù)庫(kù)中的所有key
flushall:刪除所有數(shù)據(jù)庫(kù)中的所有key
5)String
set(key, value):給數(shù)據(jù)庫(kù)中名稱為key的string賦予值value
get(key):返回?cái)?shù)據(jù)庫(kù)中名稱為key的string的value
getset(key, value):給名稱為key的string賦予上一次的value
mget(key1, key2,…, key N):返回庫(kù)中多個(gè)string的value
setnx(key, value):添加string,名稱為key绒障,值為value
setex(key, time, value):向庫(kù)中添加string吨凑,設(shè)定過(guò)期時(shí)間time
mset(key N, value N):批量設(shè)置多個(gè)string的值
msetnx(key N, value N):如果所有名稱為key i的string都不存在
incr(key):名稱為key的string增1操作
incrby(key, integer):名稱為key的string增加integer
decr(key):名稱為key的string減1操作
decrby(key, integer):名稱為key的string減少integer
append(key, value):名稱為key的string的值附加value
substr(key, start, end):返回名稱為key的string的value的子串
6)List
rpush(key, value):在名稱為key的list尾添加一個(gè)值為value的元素
lpush(key, value):在名稱為key的list頭添加一個(gè)值為value的 元素
llen(key):返回名稱為key的list的長(zhǎng)度
lrange(key, start, end):返回名稱為key的list中start至end之間的元素
ltrim(key, start, end):截取名稱為key的list
lindex(key, index):返回名稱為key的list中index位置的元素
lset(key, index, value):給名稱為key的list中index位置的元素賦值
lrem(key, count, value):刪除count個(gè)key的list中值為value的元素
lpop(key):返回并刪除名稱為key的list中的首元素
rpop(key):返回并刪除名稱為key的list中的尾元素
blpop(key1, key2,… key N, timeout):lpop命令的block版本。
brpop(key1, key2,… key N, timeout):rpop的block版本户辱。
rpoplpush(srckey, dstkey):返回并刪除名稱為srckey的list的尾元素鸵钝,
并將該元素添加到名稱為dstkey的list的頭部
7)Set
sadd(key, member):向名稱為key的set中添加元素member
srem(key, member) :刪除名稱為key的set中的元素member
spop(key) :隨機(jī)返回并刪除名稱為key的set中一個(gè)元素
smove(srckey, dstkey, member) :移到集合元素
scard(key) :返回名稱為key的set的基數(shù)
sismember(key, member) :member是否是名稱為key的set的元素
sinter(key1, key2,…key N) :求交集
sinterstore(dstkey, (keys)) :求交集并將交集保存到dstkey的集合
sunion(key1, (keys)) :求并集
sunionstore(dstkey, (keys)) :求并集并將并集保存到dstkey的集合
sdiff(key1, (keys)) :求差集
sdiffstore(dstkey, (keys)) :求差集并將差集保存到dstkey的集合
smembers(key) :返回名稱為key的set的所有元素
srandmember(key) :隨機(jī)返回名稱為key的set的一個(gè)元素
8)Hash
hset(key, field, value):向名稱為key的hash中添加元素field
hget(key, field):返回名稱為key的hash中field對(duì)應(yīng)的value
hmget(key, (fields)):返回名稱為key的hash中field i對(duì)應(yīng)的value
hmset(key, (fields)):向名稱為key的hash中添加元素field
hincrby(key, field, integer):將名稱為key的hash中field的value增加integer
hexists(key, field):名稱為key的hash中是否存在鍵為field的域
hdel(key, field):刪除名稱為key的hash中鍵為field的域
hlen(key):返回名稱為key的hash中元素個(gè)數(shù)
hkeys(key):返回名稱為key的hash中所有鍵
hvals(key):返回名稱為key的hash中所有鍵對(duì)應(yīng)的value
hgetall(key):返回名稱為key的hash中所有的鍵(field)及其對(duì)應(yīng)的value
Ref:
http://www.runoob.com/redis/redis-hyperloglog.html
http://chentian114.iteye.com/blog/2270254