Redis概述

概念

Redis是一個(gè)開源的使用C語(yǔ)言編寫、支持網(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,寫的速度是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ā)讀寫、對(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字段,只有打開了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都需要兩次??墒褂肐NCR和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)存開銷,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ù)寫命令,除了 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)回收,也就是最久錢被訪問(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開啟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ù)寫入的數(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ù)寫成為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次寫入就生成快照,也可以配置當(dāng)1小時(shí)內(nèi)有1000次寫入就生成快照,也可以多個(gè)規(guī)則一起實(shí)施。

Redis的RDB文件不會(huì)壞掉,因?yàn)槠鋵懖僮魇窃谝粋€(gè)新進(jìn)程中進(jìn)行的,當(dāng)生成一個(gè)新的RDB文件時(shí),Redis生成的子進(jìn)程會(huì)先將數(shù)據(jù)寫到一個(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è)追加寫入的日志文件

一般數(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ù),寫入新的AOF臨時(shí)文件。在寫入新文件的過(guò)程中,所有的寫操作日志還是會(huì)寫到原來(lái)老的AOF文件中,同時(shí)還會(huì)記錄在內(nèi)存緩沖區(qū)中。當(dāng)重完操作完成后,會(huì)將所有緩沖區(qū)中的日志一次性寫入到臨時(shí)文件中。然后調(diào)用原子性的rename命令用新的AOF文件取代老的AOF文件。

AOF是一個(gè)寫文件操作,其目的是將操作日志寫到磁盤上,所以它也同樣會(huì)遇到我們上面說(shuō)的寫操作的5個(gè)流程。那么寫AOF的操作安全性又有多高呢。實(shí)際上這是可以設(shè)置的,在Redis中對(duì)AOF調(diào)用write(2)寫入后,何時(shí)再調(diào)用fsync將其寫到磁盤上,通過(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)容同步到磁盤,所以這一切就完全依賴于操作系統(tǒng)的調(diào)試了。對(duì)大多數(shù)Linux操作系統(tǒng),是每30秒進(jìn)行一次fsync,將緩沖區(qū)中的數(shù)據(jù)寫到磁盤上。
2)appendfsync everysec
當(dāng)設(shè)置appendfsync為everysec的時(shí)候,Redis會(huì)默認(rèn)每隔一秒進(jìn)行一次fsync調(diào)用,將緩沖區(qū)中的數(shù)據(jù)寫到磁盤。但是當(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)前的寫操作就會(huì)阻塞。所以結(jié)論就是,在絕大多數(shù)情況下,Redis會(huì)每隔一秒進(jìn)行一次fsync。在最壞的情況下,兩秒鐘會(huì)進(jìn)行一次fsync操作。這一操作在大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)中被稱為group commit,就是組合多次寫操作的數(shù)據(jù),一次性將日志寫到磁盤。
3)appednfsync always
當(dāng)設(shè)置appendfsync為always時(shí),每一次寫操作都會(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ù)從開始到執(zhí)行會(huì)經(jīng)歷以下三個(gè)階段:

  • MULTI 開始事務(wù)
  • 多個(gè)命令入隊(duì)到事務(wù)中
  • EXEC 命令觸發(fā)事務(wù)
    DISCARD: 取消事務(wù),放棄執(zhí)行事務(wù)塊內(nèi)的所有命令。
    EXEC:執(zhí)行所有事務(wù)塊內(nèi)的命令。
    MULTI:標(biāo)記一個(gè)事務(wù)塊的開始。
    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ù)同步保存到磁盤
bgsave:將數(shù)據(jù)異步保存到磁盤
lastsave:返回上次成功將數(shù)據(jù)保存到磁盤的Unix時(shí)戳
shundown:將數(shù)據(jù)同步保存到磁盤,然后關(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容