Redis介紹及實踐分享

1、Redis是什麼
1)Redis是REmote DIctionary Server的縮寫,是一個key-value存儲系統
2)Redis提供了一些豐富的數據結構,包括Strings,Lists,HashesSets和Ordered Sets以及Hashes。包括對這些數據結構的操作支持
3)Redis可以替代Memcached,並且解決了斷電後數據完全丟失的問題
4)Redis官方網站: http://redis.io
    Redis作者Blog: http://antirez.com

2、Redis安裝
Download, extract and compile Redis with:
$ wget http://download.redis.io/releases/redis-3.2.5.tar.gz
$ tar xzf redis-3.2.5.tar.gz
$ cd redis-3.2.5
$ make

The binaries that are now compiled are available in the src directory:Run Redis with:

$ src/redis-server
You can interact with Redis using the built-in client:
$ src/redis-cli
redis> set foo bar
OK
redis> get foo
"bar"

3、Redis優點
1)性能極高,Redis能支持10萬每秒的讀寫頻率
2)豐富的數據類型及對應的操作
3)Redis的所有操作都是原子性的,同時Redis還支持對幾個操作合併後的原子性執行,也即支持事
4)豐富的特性,Redis還支持publish/subscribe,key過期等特性

4、Redis性能
以下摘自官方測試描述:
在50個併發的情況下請求10W次,寫的速度是11W次/s,讀的速度是8.1w次/s

測試環境:
1)50個併發,請求10W次
2)讀和寫大小爲256bytes的字符串
3)Linux2.6 Xeon X3320 2.5GHz的服務器上
4)通過本機的loopback interface接口上執行

5、Redis數據類型
redis常用五種數據類型:string,hash,list,set,sorted set
 
Redis內部使用一個redisObject對象來表示所有的key和value,redisObject最主要的信息如上圖所示:
    type代表一個value對象具體是何種數據類型,encoding是不同數據類型在redis內部的存儲方式,比如:type=string代表value存儲的是一個普通字符串,那麼對應的encoding可以是raw或者是int,如果是int則代表實際redis內部是按數值型類存儲和表示這個字符串的

Commands 集合: http://redis.io/commands

5.1 String
常用命令:
set,get,decr,incr,mget 等

應用場景:
String是最常用的一種數據類型,普通的key/value存儲

實現方式:
String在redis內部存儲默認就是一個字符串,被redisObject所引用,當遇到incr,decr等操作時會轉成數值型進行計算,此時redisObject的encoding字段爲int

5.2 Hash
常用命令:
hget,hset,hgetall 等

應用場景:
比如,我們存儲供應商酒店價格的時候可以採取此結構,用酒店編碼作爲Key,RatePlan+RoomType作爲Filed,價格信息作爲Value

實現方式:
Hash對應Value內部實際就是一個HashMap,實際這裏會有2種不同實現,這個Hash的成員比較少時Redis爲了節省內存會採用類似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,對應的value redisObject的encoding爲zipmap;當成員數量增大時會自動轉成真正的HashMap,此時encoding爲ht

5.3 List
常用命令:
lpush,rpush,lpop,rpop,lrang等

應用場景:
Redis list應用場景非常多,也是Redis最重要的數據結構之一,比如twitter的關注列表,粉絲列表等都可以用Redis的list結構來實現

實現方式:
Redis list的實現爲一個雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過帶來了部分額外的內存開銷,Redis內部的很多實現,包括髮送緩衝隊列等也都是用的這個數據結構

5.4 Set
常用命令:
sadd,spop,smembers,sunion 等

應用場景:
Set對外提供的功能與list類似,當你需要存儲一個列表數據,又不希望出現重複數據時,set 是一個很好的選擇,並且set提供了判斷某個成員是否在一個set集合內的接口,這個也是list所不能提供的

實現方式:
Set 的內部實現是一個value永遠爲null的HashMap,實際就是通過計算hash的方式來快速排除重複的,這也是set能提供判斷一個成員是否在集合內的原因

5.5 Sorted set
常用命令:
zadd,zrange,zrem,zcard等

使用場景:
Sorted set的使用場景與set類似,區別是set不是自動有序的,而sorted set可以通過用戶額外提供一個優先級(score)的參數來爲成員排序,並且是插入有序的,即自動排序.當你需要一個有序的並且不重複的集合列表,那麼可以選擇sorted set數據結構

實現方式:
Sorted set的內部使用HashMap和跳躍表(SkipList)來保證數據的存儲和有序,HashMap裏放的是成員到score的映射,而跳躍表裏存放的是所有的成員,排序依據是HashMap裏存的score,使用跳躍表的結構可以獲得比較高的查找效率

5.6 內存優化
Redis爲不同數據類型分別提供了一組參數來控制內存使用,我們在前面提到過Redis Hash的value內部是一個HashMap,如果該Map的成員數比較少,則會採用一維數組的方式來緊湊存儲該Map,即省去了大量指針的內存開銷,這個參數在redis.conf配置文件中下面2項:
    hash-max-zipmap-entries   64
    hash-max-zipmap-value     512

含義是當value這個Map內部不超過多少個成員時會採用線性緊湊格式存儲,默認是64,即value內部有64個以下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的HashMap
hash-max-zipmap-value 含義是當value這個Map內部的每個成員值長度不超過多少字節就會採用線性緊湊存儲來節省空間

以上2個條件任意一個條件超 過設置值都會轉換成真正的HashMap,也就不會再節省內存了,那麼這個值是不是設置的越大越好呢,答案當然是否定的,HashMap的優勢就是查找和操作的時間複雜度都是O(1)的,而放棄Hash採用一維存儲則是O(n)的時間複雜度,如果成員數量很少,則影響不大,否則會嚴重影響性能,所以要權衡好這個值的設置,總體上還是時間成本和空間成本上的權衡

6、Redis發佈/訂閱
Redis的發佈/訂閱(Publish/Subscribe)功能類似於傳統的消息路由功能,發佈者發佈消息,訂閱者接收消息,溝通發佈者和訂閱者之間的橋樑是訂閱的Channel或者Pattern

訂閱者和發佈者之間的關係是鬆耦合的,發佈者不指定哪個訂閱者才能接收消息,訂閱者不只接收特定發佈者的消息

7、Redis數據過期設置
Redis可以按key設置過期時間,過期後將被自動刪除,這個特性讓Redis很適合用來存儲酒店動態流量和價格信息

用TTL命令可以獲取某個key值的過期時間,-1表示不會過期

8、Redis事務支持
Redis目前對事務支持還比較簡單,也即支持一些簡單的組合型的命令,只能保證一個client發起的事務中的命令可以連續的執行,而中間不會插入其他client的命令:由於Redis是單線程來處理所有client的請求的所以做到這點是很容易

事務的執行過程中,如果redis意外的掛了,這時候事務可能只被執行了一半,可以用redis-check-aof 工具進行修復

9、Redis數據存儲
9.1 數據快照
數據快照的原理是將整個Redis內存中存的所有數據遍歷一遍後,保存到一個擴展名爲rdb的數據文件中。通過SAVE命令可以調用這個過程

9.2 數據快照配置
save 900 1
save 300 10
save 60 10000
以上在redis.conf中的配置指出在多長時間內,有多少次更新操作,就將數據同步到數據文件,這個可以多個條件配合.上面的含義是900秒後有一個key發生改變就執行save,300秒後有10個key發生改變執行save,60秒有10000個key發生改變執行save

10、Redis AOF
10.1 數據快照的缺點是持久化之後如果出現crash則會丟失一段數據,因此作者增加了另外一種追加式的操作日誌記錄,叫append only file,其日誌文件以aof結尾,我們一般稱爲aof文件。要開啓aof日誌的記錄,需要在配置文件中進行如下設置:
appendonly yes

10.2 appendonly配置如果不開啓,可能會在斷電時導致一段時間內的數據丟失。因爲redis本身同步數據文件是按save條件來同步的,所以有的數據會在一段時間內只存在於內存中
appendfsync no/always/everysec
1)no:表示等操作系統進行數據緩存同步到磁盤
2)always:表示每次更新操作後手動調用fsync() 將數據寫到磁盤
3)everysec:表示每秒同步一次。一般用everysec

10.3 AOF文件只增不減會導致文件越來越大,重寫過程如下
1)Redis通過fork產生子進程
2)子進程將當前所有數據寫入一個臨時文件
3)父子進程是並行執行的,在子進程遍歷並寫臨時文件的時候,父進程在照常接收請求,處理請求,寫AOF,不過這時他是把新來的AOF寫在一個緩衝區中
4)子進程寫完臨時文件後就會退出.這時父進程會接收到子進程退出的消息,他會把自己現在收集在緩衝區中的所有AOF追加在臨時文件中
5)最後把臨時文件rename一下,改名爲appendonly.aof, 這時原來的aof文件被覆蓋。整個過程完成

11、Redis數據恢復
當Redis服務器掛掉時,重啓時將按以下優先級恢復數據到內存中:
1)如果只配置了AOF,重啓時加載AOF文件恢復數據
2)如果同時配置了RBD和AOF,啓動時只加載AOF文件恢復數據
3)如果只配置了RDB,啓動時將加載dump文件恢復數據

12、Redis主從複製
12.1 Master/Slave配置
Master IP:175.41.209.118
Master Redis Server Port:6379

Slave配置很簡單,只需要在slave服務器的redis.conf加入:
slaveof 175.41.209.118 6379
啓動master和slave,然後寫入數據到master,讀取slave,可以看到數據被複制到slave了

用途:讀寫分離,數據備份,災難恢復等

12.2 Redis主從複製過程
配置好slave後,slave與master建立連接,然後發送sync命令。無論是第一次連接還是重新連接,master都會啓動一個後臺進程,將數據庫快照保存到文件中,同時master主進程會開始收集新的寫命令並緩存;後臺進程完成寫文件後,master就發送文件給slave,slave將文件保存到硬盤上,再加載到內存中, 接着master就會把緩存的命令轉發給slave,後續master將收到的寫命令發送給slave;如果master同時收到多個slave發來的同步連接命令,master只會啓動一個進程來寫數據庫鏡像, 然後發送給所有的slave

12.3 Redis主從複製特點
1)master可以擁有多個slave
2)多個slave可以連接同一個master外,還可以連接到其他slave
3)主從複製不會阻塞master,在同步數據時,master可以繼續處理client請求
4)可以在master禁用數據持久化,註釋掉master配置文件中的所有save配置,只需在slave上配置數據持久化
5)提高系統的伸縮性

12.4 Redis主從複製速度
        官方提供了一個數據, Slave在21秒即完成了對Amazon網站 10G key set的複製

13、Redis客戶端
13.1 Redis的客戶端非常豐富,幾乎所有流行的語言都有客戶端
客戶端列表:http://redis.io/clients
Java客戶端推薦Jedis: https://github.com/xetorthio/jedis

13.2 Jedis目前Release版本是3.2.5,支持的特性如下,一句話概括,該有的都有了,不該有的也有了
• Sorting
• Connection handling
• Commands operating on any kind of values
• Commands operating on string values
• Commands operating on hashes
• Commands operating on lists
• Commands operating on sets
• Commands operating on sorted sets
• Transactions
• Pipelining
• Publish/Subscribe
• Persistence control commands
• Remote server control commands
• Connection pooling
• Sharding (MD5, MurmureHash)
• Key-tags for sharding
• Sharding with pipelining

13.3 Jedis使用
添加Maven依賴:
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<type>jar</type>
<scope>compile</scope>
</dependency>

最簡單的使用方式:
Jedis jedis = new Jedis("localhost");
jedis.set("foo", "bar");
String value = jedis.get("foo");

更多高級用法參考:https://github.com/xetorthio/jedis/wiki

14、Redis shard
14.1 目前,Redis server沒有提供shard功能,只能在client端實現
Redis有些客戶端實現了shard,比如Java客戶端Jedis.

Jedis使用一致性哈希算法實現shard,提供
JedisPool,JedisPoolConfig,JedisSharedInfo,ShardedJedisPool等相關類來使用shared功能

14.2 Jedis shardedJedisPool的創建例子
<bean id="dataJedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="200"/>
<property name="maxIdle" value="200"/>
<property name="maxWait" value="1000"/>
<property name="testOnBorrow" value="true"/>
</bean>
<bean id="dataJedisShardInfo" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="${redis.host}"/>
<constructor-arg index="1" value="${redis.port}"/>
<constructor-arg index="2" value="10000"/>
</bean>
<bean id="dataShardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="dataJedisPoolConfig"/>
<constructor-arg index="1">
<list>
<ref bean="dataJedisShardInfo"/>
</list>
</constructor-arg>
</bean>

14.3 Jedis shardedJedisPool的使用
ShardedJedis shardedJedis = shardedJedisPool.getResource();
//xxoo
shardedJedisPool.returnResource(shardedJedis);

15、Redis cluster
穩定版本的redis(2.4.5)只支持簡單的master-slave replication: 一個master寫,多個slave讀。只能通過客戶端一致性哈希自己做sharding

Redis cluster是下一階段最重要的功能之一,會有集羣的自動sharding,多節點容錯等:集羣功將在3.0版本推出

16、Redis Use In ChoiceHotels
16.1 Choice的Redis Server是一主一從,使用亞馬遜AWS虛擬機,機器配置如下:
7.5 GB memory
4 EC2 Compute Units (2 virtual cores with 2
EC2 Compute Units each)
64-bit platform
I/O Performance: High

16.2 現狀:每天約更新30萬左右的數據,現在庫裏有400W條紀錄(400W個Key,使用的是Hash結構存儲),每條數據都設有過期時間,佔用了2.5G的內存

爲了提高讀寫性能Master關閉了Persistence功能,Slave只負責同步備份Master的數據,不對外提供服務
另一種可選方案是用Master提供寫服務,Slave提供讀服務來實現讀寫分離

16.3 Master開啓Save功能的影響:在dump過程中,除了磁盤有大量的IO操作以外,Redis是fork一個子進程來dump數據到硬盤,原有進程佔用30%+的CPU,dump數據的子進程單獨另外佔用一個CPU

Master開啓Save對性能的直接影響:TPS大概減少30%
Choice Master Redis服務器開啓Save功能後的明顯影響是:機器Load一直居高不下,大量請求超時:關閉Save功能後服務器壓力銳減,基本無請求超時情況發生

16.4 Redis開啓AOF日誌功能的影響:對性能有影響,但是由於每次追加的數據量小,所以對性能的影響相對小很多
Choice Master Reids開啓AOF功能後,機器load微升,對性能無明顯影響

16.5 bgrewriteaof對性能的影響:爲了定時減小AOF文件的大小,Redis2.4以後增加了自動的bgrewriteaof的功能,Redis會選擇一個自認爲負載低的情況下執行bgrewriteaof,這個重寫AOF文件的過程是很影響性能的
Choice Master開啓自動bgrewriteaof功能對系統的明顯影響是:高併發時段有請求超時,機器load 明顯上升幾倍

16.6 目前較好的方案是:Mater關閉Save功能,關閉AOF日誌功能,以求達到性能最佳:Slave開啓
Save並開啓AOF日誌功能,並開啓bgrewriteaof功能,不對外提供服務,這樣Slave的負載總體上會一直略高於Master負載,但Master性能達到最

16.7 總結:
從目前使用的情況來看,總體效果還是比較理想的, ChoiceHotels的價格存儲使用Redis的Hash結構也非常適合,HotelCode+Date最爲key, 這樣的key很容易設置過期,RatePlan+RoomType作爲Filed, 價格和流量是Value
目前每天從GTA到Choice大約500w個請求,整個過程80%的請求在500毫秒內返回,基本無超時現象發生

16.8 Redis Master Info
redis_version:2.4.4
connected_clients:171
connected_slaves:1
used_memory_human:2.37G
used_memory_peak_human:2.46G
aof_enabled:0
expired_keys:1595004
keyspace_hits:2611419705
keyspace_misses:55827727
role:master
aof_current_size:3874203906
aof_base_size:3850549480
db0:keys=4073286,expires=4073286
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章