複製鏈接
請複製以下鏈接發送給好友

Redis

鎖定
Redis(Remote Dictionary Server ),即遠程字典服務,是一個開源的使用ANSI C語言編寫、支持網絡、可基於內存亦可持久化的日誌型、Key-Value數據庫,並提供多種語言的API。
中文名
遠程字典服務
外文名
Remote Dictionary Server
簡    稱
Redis
分    類
數據庫
相    關
NoSql 數據存儲
開發語言
ANSIC語言
特    點
速度快

Redis定義

redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。
Redis 是一個高性能的key-value數據庫。 redis的出現,很大程度補償了memcached這類key/value存儲的不足,在部 分場合可以對關係數據庫起到很好的補充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。 [1] 
Redis支持主從同步。數據可以從主服務器向任意數量的從服務器上同步,從服務器可以是關聯其他從服務器的主服務器。這使得Redis可執行單層樹複製。存盤可以有意無意的對數據進行寫操作。由於完全實現了發佈/訂閲機制,使得從數據庫在任何地方同步樹時,可訂閲一個頻道並接收主服務器完整的消息發佈記錄。同步對讀取操作的可擴展性和數據冗餘很有幫助。
redis的官網地址,非常好記,是redis.io。(域名後綴io屬於國家域名,是british Indian Ocean territory,即英屬印度洋領地),Vmware在資助着redis項目的開發和維護。
從2010年3月15日起,Redis的開發工作由VMware主持。從2013年5月開始,Redis的開發由Pivotal贊助。

Redis性能

下面是官方的bench-mark數據: [1] 
測試完成了50個併發執行100000個請求。
設置和獲取的值是一個256字節字符串。
Linux box是運行Linux 2.6,這是X3320 Xeon 2.5 ghz。
文本執行使用loopback接口(127.0.0.1)。
結果:讀的速度是110000次/s,寫的速度是81000次/s 。

Redis支持語言

許多語言都包含Redis支持,包括: [1] 
授權協議:BSD
開源組織:無
開發語言:C/C++ 查看源碼 »
地區:不詳
操作系統:Linux
投 遞 者:紅薯
軟件類型:開源軟件
適用人羣:未知
所屬分類:服務器軟件、 緩存服務器
收錄時間:2009-04-14

Redis常用命令

就DB來説,Redis成績已經很驚人了,且不説memcachedbTokyo Cabinet之流,就説原版的memcached,速度似乎也只能達到這個級別。Redis根本是使用內存存儲,持久化的關鍵是這三條指令:SAVE BGSAVE LASTSAVE …
當接收到SAVE指令的時候,Redis就會dump數據到一個文件裏面。
值得一説的是它的獨家功能:存儲列表和集合,這是它與mc之流相比更有競爭力的地方。
不介紹mc裏面已經有的內容,只列出特殊的:
TYPE key — 用來獲取某key的類型
KEYS pattern — 匹配所有符合模式的key,比如KEYS * 就列出所有的key了,當然,複雜度O(n)
RANDOMKEY - 返回隨機的一個key
RENAME oldkeynewkey— key也可以改名
列表操作,精華
RPUSH key string — 將某個值加入到一個key列表末尾
LPUSH key string — 將某個值加入到一個key列表頭部
LLEN key — 列表長度
LRANGE key start end — 返回列表中某個範圍的值,相當於mysql裏面的分頁查詢那樣
LTRIM key start end — 只保留列表中某個範圍的值
LINDEX key index — 獲取列表中特定索引號的值,要注意是O(n)複雜度
LSET key index value — 設置列表中某個位置的值
LPOP key
RPOP key — 和上面的LPOP一樣,就是類似棧或隊列的那種取頭取尾指令,可以當成消息隊列來使用了
集合操作
SADD key member — 增加元素
SREM key member — 刪除元素
SCARD key — 返回集合大小
SISMEMBER key member — 判斷某個值是否在集合中
SINTER key1 key2 ... keyN — 獲取多個集合的交集元素
SMEMBERS key — 列出集合的所有元素
還有Multiple DB的命令,可以更換db,數據可以隔離開,默認是存放在DB 0。

Redis數據模型

Redis的外圍由一個鍵、值映射的字典構成。與其他非關係型數據庫主要不同在於:Redis中值的類型 [1]  不僅限於字符串,還支持如下抽象數據類型:
值的類型決定了值本身支持的操作。Redis支持不同無序、有序的列表,無序、有序的集合間的交集、並集等高級服務器端原子操作。

Redis數據結構

redis提供五種數據類型:string,hash,list,set及zset(sorted set)。
string(字符串)
string是最簡單的類型,你可以理解成與Memcached一模一樣的類型,一個key對應一個value,其上支持的操作與Memcached的操作類似。但它的功能更豐富。
redis採用結構sdshdr和sds封裝了字符串,字符串相關的操作實現在源文件sds.h/sds.c中。
數據結構定義如下:
typedefchar*sds;
structsdshdr{
longlen;
longfree;
charbuf[];
};
list(雙向鏈表)
list是一個鏈表結構,主要功能是push、pop、獲取一個範圍的所有值等等。操作中key理解為鏈表的名字。
對list的定義和實現在源文件adlist.h/adlist.c,相關的數據結構定義如下:
//list迭代器
typedefstructlistIter{
listNode*next;
intdirection;
}listIter;
//list數據結構
typedefstructlist{
listNode*head;
listNode*tail;
void*(*dup)(void*ptr);
void(*free)(void*ptr);
int(*match)(void*ptr,void*key);
unsignedintlen;
listIteriter;
}list;
dict(hash表)
set是集合,和我們數學中的集合概念相似,對集合的操作有添加刪除元素,有對多個集合求交併差等操作。操作中key理解為集合的名字。
在源文件dict.h/dict.c中實現了hashtable的操作,數據結構的定義如下:
//dict中的元素項
typedefstructdictEntry{
void*key;
void*val;
structdictEntry*next;
}dictEntry;
//dict相關配置函數
typedefstructdictType{
unsignedint(*hashFunction)(constvoid*key);
void*(*keyDup)(void*privdata,constvoid*key);
void*(*valDup)(void*privdata,constvoid*obj);
int(*keyCompare)(void*privdata,constvoid*key1,constvoid*key2);
void(*keyDestructor)(void*privdata,void*key);
void(*valDestructor)(void*privdata,void*obj);
}dictType;
//dict定義
typedefstructdict{
dictEntry**table;
dictType*type;
unsignedlongsize;
unsignedlongsizemask;
unsignedlongused;
void*privdata;
}dict;
//dict迭代器
typedefstructdictIterator{
dict*ht;
intindex;
dictEntry*entry,*nextEntry;
}dictIterator;
dict中table為dictEntry指針數組,數組中每個成員為hash值相同元素的單向鏈表。set是在dict的基礎上實現的,指定了key的比較函數為dictEncObjKeyCompare,若key相等則不再插入。
zset(排序set)
zset是set的一個升級版本,他在set的基礎上增加了一個順序屬性,這一屬性在添加修改元素的時候可以指定,每次指定後,zset會自動重新按新的值調整順序。可以理解了有兩列的mysql表,一列存value,一列存順序。操作中key理解為zset的名字。
typedefstructzskiplistNode{
structzskiplistNode**forward;
structzskiplistNode*backward;
doublescore;
robj*obj;
}zskiplistNode;
typedefstructzskiplist{
structzskiplistNode*header,*tail;
unsignedlonglength;
intlevel;
}zskiplist;
typedefstructzset{
dict*dict;
zskiplist*zsl;
}zset;
zset利用dict維護key -> value的映射關係,用zsl(zskiplist)保存value的有序關係。zsl實際是叉數
不穩定的多叉樹,每條鏈上的元素從根節點到葉子節點保持升序排序。

Redis存儲

redis使用了兩種文件格式:全量數據和增量請求。
全量數據格式是把內存中的數據寫入磁盤,便於下次讀取文件進行加載;
增量請求文件則是把內存中的數據序列化為操作請求,用於讀取文件進行replay得到數據,序列化的操作包括SET、RPUSH、SADD、ZADD。
redis的存儲分為內存存儲、磁盤存儲和log文件三部分,配置文件中有三個參數對其進行配置。
save seconds updates,save配置,指出在多長時間內,有多少次更新操作,就將數據同步到數據文件。這個可以多個條件配合,比如默認配置文件中的設置,就設置了三個條件。
appendonly yes/no ,appendonly配置,指出是否在每次更新操作後進行日誌記錄,如果不開啓,可能會在斷電時導致一段時間內的數據丟失。因為redis本身同步數據文件是按上面的save條件來同步的,所以有的數據會在一段時間內只存在於內存中。
appendfsync no/always/everysec ,appendfsync配置,no表示等操作系統進行數據緩存同步到磁盤,always表示每次更新操作後手動調用fsync()將數據寫到磁盤,everysec表示每秒同步一次。

Redis安裝

獲取源碼、解壓、進入源碼目錄
使用wget工具等下載:
wget (百度不讓用鏈接)
tar xzf redis-1.2.6.tar.gz
cd redis-1.2.6。
編譯生成可執行文件
由於makefile文件已經寫好,我們只需要直接在源碼目錄執行make命令進行編譯即可:
make
make-test
sudo make install
make命令執行完成後,會在當前目錄下生成本個可執行文件,分別是redis-server、redis-cli、redis-benchmark、redis-stat,它們的作用如下:
redis-server:Redis服務器的daemon啓動程序
redis-cli:Redis命令行操作工具。當然,你也可以用telnet根據其純文本協議來操作
redis-benchmark:Redis性能測試工具,測試Redis在你的系統及你的配置下的讀寫性能
redis-stat:Redis狀態檢測工具,可以檢測Redis當前狀態參數及延遲狀況。
建立Redis目錄(非必須)
這個過程不是必須的,只是為了將Redis相關的資源統一管理而進行的操作。
執行以下命令建立相關目錄並拷貝相關文件至目錄中:
sudo -s
mkdir -p /usr/local/redis/bin
mkdir -p /usr/local/redis/etc
mkdir -p /usr/local/redis/var
cp redis-server redis-cli redis-benchmark redis-stat /usr/local/redis/bin/
cp redis.conf /usr/local/redis/etc/
配置參數
在我們成功安裝Redis後,我們直接執行redis-server即可運行Redis,此時它是按照默認配置來運行的(默認配置甚至不是後台運行)。我們希望Redis按我們的要求運行,則我們需要修改配置文件,Redis的配置文件就是我們上面第二個cp操作的redis.conf文件,它被我們拷貝到了/usr/local/redis/etc/目錄下。修改它就可以配置我們的server了。如何修改?下面是redis.conf的主要配置參數的意義:
daemonize:是否以後台daemon方式運行
pidfile:pid文件位置
port:監聽的端口號
timeout:請求超時時間
loglevel:log信息級別
logfile:log文件位置
databases:開啓數據庫的數量
save * *:保存快照的頻率,第一個*表示多長時間,第二個*表示執行多少次寫操作。在一定時間內執行一定數量的寫操作時,自動保存快照。可設置多個條件。
rdbcompression:是否使用壓縮
dbfilename:數據快照文件名(只是文件名,不包括目錄)
dir:數據快照的保存目錄(這個是目錄)
appendonly:是否開啓appendonlylog,開啓的話每次寫操作會記一條log,這會提高數據抗風險能力,但影響效率。
appendfsync:appendonlylog如何同步到磁盤(三個選項,分別是每次寫都強制調用fsync、每秒啓用一次fsync、不調用fsync等待系統自己同步)
下面是一個略做修改後的配置文件內容:
daemonizeyes
pidfile/usr/local/redis/var/redis.pid
port6379
timeout300
logleveldebug
logfile/usr/local/redis/var/redis.log
databases16
save9001
save30010
save6010000
rdbcompressionyes
dbfilenamedump.rdb
dir/usr/local/redis/var/
appendonlyno
appendfsyncalways
glueoutputbufyes
shareobjectsno
shareobjectspoolsize1024
將上面內容寫為redis.conf並保存到/usr/local/redis/etc/目錄下
然後在命令行執行:
/usr/local/redis/bin/redis-server /usr/local/redis/etc/redis.conf
即可在後台啓動redis服務,這時你通過
telnet127.0.0.16379
即可連接到你的redis服務
Redis常用內存優化手段與參數
通過我們上面的一些實現上的分析可以看出redis實際上的內存管理成本非常高,即佔用了過多的內存,作者對這點也非常清楚,所以提供了一系列的參數和手段來控制和節省內存,我們分別來討論下。
首先最重要的一點是不要開啓Redis的VM選項,即虛擬內存功能,這個本來是作為Redis存儲超出物理內存數據的一種數據在內存與磁盤換入換出的一個持久化策略,但是其內存管理成本也非常的高,並且我們後續會分析此種持久化策略並不成熟,所以要關閉VM功能,請檢查你的redis.conf文件中 vm-enabled 為 no。
其次最好設置下redis.conf中的maxmemory選項,該選項是告訴Redis當使用了多少物理內存後就開始拒絕後續的寫入請求,該參數能很好的保護好你的Redis不會因為使用了過多的物理內存而導致swap,最終嚴重影響性能甚至崩潰。
另外Redis為不同數據類型分別提供了一組參數來控制內存使用,我們在前面詳細分析過Redis Hash是value內部為一個HashMap,如果該Map的成員數比較少,則會採用類似一維線性的緊湊格式來存儲該Map, 即省去了大量指針的內存開銷,這個參數控制對應在redis.conf配置文件中下面2項:
  1. hash-max-zipmap-entries 64
  2. hash-max-zipmap-value 512
  3. hash-max-zipmap-entries
含義是當value這個Map內部不超過多少個成員時會採用線性緊湊格式存儲,默認是64,即value內部有64個以下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的HashMap。
hash-max-zipmap-value 含義是當 value這個Map內部的每個成員值長度不超過多少字節就會採用線性緊湊存儲來節省空間。
以上2個條件任意一個條件超過設置值都會轉換成真正的HashMap,也就不會再節省內存了,那麼這個值是不是設置的越大越好呢,答案當然是否定的,HashMap的優勢就是查找和操作的時間複雜度都是O(1)的,而放棄Hash採用一維存儲則是O(n)的時間複雜度,如果
成員數量很少,則影響不大,否則會嚴重影響性能,所以要權衡好這個值的設置,總體上還是最根本的時間成本和空間成本上的權衡。
同樣類似的參數
list-max-ziplist-entries 512
説明:list數據類型多少節點以下會採用去指針的緊湊存儲格式。
list-max-ziplist-value 64
説明:list數據類型節點值大小小於多少字節會採用緊湊存儲格式。
set-max-intset-entries 512
説明:set數據類型內部數據如果全部是數值型,且包含多少節點以下會採用緊湊格式存儲。
最後想説的是Redis內部實現沒有對內存分配方面做過多的優化,在一定程度上會存在內存碎片,不過大多數情況下這個不會成為Redis的性能瓶頸,不過如果在Redis內部存儲的大部分數據是數值型的話,Redis內部採用了一個shared integer的方式來省去分配內存的開銷,即在系統啓動時先分配一個從1~n 那麼多個數值對象放在一個池子中,如果存儲的數據恰好是這個數值範圍內的數據,則直接從池子裏取出該對象,並且通過引用計數的方式來共享,這樣在系統存儲了大量數值下,也能一定程度上節省內存並且提高性能,這個參數值n的設置需要修改源代碼中的一行宏定義REDIS_SHARED_INTEGERS,該值默認是10000,可以根據自己的需要進行修改,修改後重新編譯就可以了。
另外redis 的6種過期策略redis 中的默認的過期策略是volatile-lru 。設置方式
config set maxmemory-policy volatile-lru
maxmemory-policy 六種方式
volatile-lru:只對設置了過期時間的key進行LRU(默認值)
allkeys-lru : 是從所有key裏 刪除 不經常使用的key
volatile-random:隨機刪除即將過期key
allkeys-random:隨機刪除
volatile-ttl : 刪除即將過期的
noeviction : 永不過期,返回錯誤
maxmemory-samples 3 是説每次進行淘汰的時候 會隨機抽取3個key 從裏面淘汰最不經常使用的(默認選項)

Redis版本發佈

2012年08月02日,Redis 2.4.16 小更新版本 NoSQL。 [3] 
2012年08月31日 ,Redis 2.4.17 小更新版本 NoSQL。 [4] 
2012年11月7日 Redis 2.6.3 發佈,高性能K/V服務器
2013年4月30日Redis 2.6.13 發佈,高性能K/V服務器 [5] 
2013年11月25日,Redis 2.8.1發佈。 [6] 
2015年2月,Redis3.0.0發佈. [7] 
2016年12月2日,Redis 4.0.0-RC1發佈。 [8] 
2018年10月17日,Redis 5.0.0發佈。
2019年9月25日,Redis 5.0.6發佈。
2019年11月19日,Redis 5.0.7發佈。
2020年3月12日,Redis 5.0.8發佈。
2020年4月17日,Redis 5.0.9發佈。
Redis 7.0.1 是最新的穩定版本。

Redis作者簡介

redis [2]  的作者,叫Salvatore Sanfilippo,來自意大利的西西里島,居住在卡塔尼亞。目前供職於Pivotal公司。他使用的網名是antirez。
參考資料