1.簡介
Redis是一個開源的key-value數據庫。它又常常被認爲是一個數據結構服務器。由於它的value不只包括基本的string類型還有 list,set ,sorted set和hash類型。固然這些類型的元素也都是string類型。也就是說list,set這些集合類型也只能包含
string 類型。你能夠在這些類型上作不少原子性的操做。好比對一個字符value追加字符串(APPEND命令)。加加或者減減一個數字字符串(INCR命令,當 然是按整數處理的).能夠對list類型進行push,或者pop元素操做(能夠模擬棧和隊列)。對於set類型能夠進行一些集合相關操做 (intersection union difference)。memcache也有相似與++,--的命令。
不過memcache的 value只包括string類型。遠沒有redis的value類型豐富。和memcahe同樣爲了性能。redis的數據一般都是放到內存中的。固然 redis能夠每間隔必定時間將內存中數據寫入到磁盤以防止數據丟失。redis也支持主從複製機制(master-slave replication)。redis的其餘特性包括簡單的事務支持和 發佈訂閱(pub/sub)通道功能,並且redis配置管理很是簡單。還有各類語言版本的開源客戶端類庫。
2.安裝
下載地址:http://redis.googlecode.com/files/redis-2.0.4.tar.gz
2.0目前是最新穩定版
能夠在Linux下運行以下命令進行安裝html
$ tar xzf redis-2.0.4.tar.gz $ cd redis-2.0.4 $ make
make完後 redis-2.0.4目錄下會出現編譯後的redis服務程序redis-server,還有用於測試的客戶端程序redis-cli
下面啓動redis服務.java
$./redis-server
注意這種方式啓動redis 使用的是默認配置。也能夠經過啓動參數告訴redis使用指定配置文件使用下面命令啓動.mysql
$ ./redis-server redis.conf
redis.conf是一個默認的配置文件。咱們能夠根據須要使用本身的配置文件。
啓動redis服務進程後,就可使用測試客戶端程序redis-cli和redis服務交互了.
好比linux
$ ./redis-cli redis> set foo bar OK redis> get foo "bar"
這裏演示了get和set命令操做簡單類型value的例子。foo是key ,bar是個string類型的value
沒linux的能夠經過這個在線的來練習,固然在線版的不少管理相關的命令是不支持的。
http://try.redis-db.com/
3.java客戶端hello,world
客戶端jar包地址http://cloud.github.com/downloads/alphazero/jredis/jredis-1.0-rc2.jar 。版本目前有點老,支持到Redis 1.2.6。最新版2.0的還沒release
在eclipse中新建一個java項目,而後添加jredis包引用。下面是個hello,world程序git
package jredisStudy; import org.jredis.*; import org.jredis.ri.alphazero.JRedisClient; public class App { public static void main(String[] args) { try { JRedis jr = new JRedisClient("192.168.56.55",6379); //redis服務地址和端口號 String key = "mKey"; jr.set(key, "hello,redis!"); String v = new String(jr.get(key)); String k2 = "count"; jr.incr(k2); jr.incr(k2); System.out.println(v); System.out.println(new String(jr.get(k2))); } catch (Exception e) { // TODO: handle exception } } }
好了redis環境已經搭建好了。後面會寫寫redis的各類類型和類型相關的命令和一些具體的應用場景github
from:http://www.cnblogs.com/xhan/archive/2011/02/01/1948751.htmlredis
本文介紹下redis支持的各類數據類型包括string,list ,set ,sorted set 和hash算法
Technorati 標籤: redis cache list 存儲spring
1. keys
redis本質上一個key-value db,因此咱們首先來看看他的key.首先key也是字符串類型,可是key中不能包括邊界字符
因爲key不是binary safe的字符串,因此像"my key"和"mykey\n"這樣包含空格和換行的key是不容許的
順便說一下在redis內部並不限制使用binary字符,這是redis協議限制的。"\r\n"在協議格式中會做爲特殊字符。
redis 1.2之後的協議中部分命令已經開始使用新的協議格式了(好比MSET)。總之目前仍是把包含邊界字符當成非法的key吧,
省得被bug糾纏。
另外關於key的一個格式約定介紹下,object-type:id:field。好比user:1000:password,blog:xxidxx:title
還有key的長度最好不要太長。道理很明顯佔內存啊,並且查找時候相對短key也更慢。不過也推薦太短的key,
好比u:1000:pwd,這樣的。顯然沒上面的user:1000:password可讀性好。
下面介紹下key相關的命令
exits key 測試指定key是否存在,返回1表示存在,0不存在
del key1 key2 ....keyN 刪除給定key,返回刪除key的數目,0表示給定key都不存在
type key 返回給定key的value類型。返回 none 表示不存在key,string字符類型,list 鏈表類型 set 無序集合類型...
keys pattern 返回匹配指定模式的全部key,下面給個例子
randomkey 返回從當前數據庫中隨機選擇的一個key,若是當前數據庫是空的,返回空串
rename oldkey newkey 原子的重命名一個key,若是newkey存在,將會被覆蓋,返回1表示成功,0失敗。多是oldkey不存在或者和newkey相同
renamenx oldkey newkey 同上,可是若是newkey存在返回失敗
dbsize 返回當前數據庫的key數量
expire key seconds 爲key指定過時時間,單位是秒。返回1成功,0表示key已經設置過過時時間或者不存在
ttl key 返回設置過過時時間的key的剩餘過時秒數 -1表示key不存在或者沒有設置過過時時間
select db-index 經過索引選擇數據庫,默認鏈接的數據庫全部是0,默認數據庫數是16個。返回1表示成功,0失敗
move key db-index 將key從當前數據庫移動到指定數據庫。返回1成功。0 若是key不存在,或者已經在指定數據庫中
flushdb 刪除當前數據庫中全部key,此方法不會失敗。慎用
flushall 刪除全部數據庫中的全部key,此方法不會失敗。更加慎用
2. string 類型
string是redis最基本的類型,並且string類型是二進制安全的。意思是redis的string能夠包含任何數據。好比jpg圖片或者序列化的對象
。從內部實現來看其實string能夠看做byte數組,最大上限是1G字節。下面是string類型的定義。sql
struct sdshdr { long len; long free; char buf[]; };
buf是個char數組用於存貯實際的字符串內容。其實char和c#中的byte是等價的,都是一個字節
len是buf數組的長度,free是數組中剩餘可用字節數。由此能夠理解爲何string類型是二進制安全的了。由於它本質上就是個byte數組。
固然能夠包含任何數據了。另外string類型能夠被部分命令按int處理.好比incr等命令,下面詳細介紹。還有redis的其餘類型像list,set,sorted set ,hash
它們包含的元素與都只能是string類型。
若是隻用string類型,redis就能夠被看做加上持久化特性的memcached.固然redis對string類型的操做比memcached多不少啊。以下:
set key value 設置key對應的值爲string類型的value,返回1表示成功,0失敗
setnx key value 同上,若是key已經存在,返回0 。nx 是not exist的意思
get key 獲取key對應的string值,若是key不存在返回nil
getset key value 原子的設置key的值,並返回key的舊值。若是key不存在返回nil
mget key1 key2 ... keyN 一次獲取多個key的值,若是對應key不存在,則對應返回nil。下面是個實驗,首先清空當前數據庫,而後
設置k1,k2.獲取時k3對應返回nil
redis> flushdb OK redis> dbsize (integer) 0 redis> set k1 a OK redis> set k2 b OK redis> mget k1 k2 k3 1. "a" 2. "b" 3. (nil)
mset key1 value1 ... keyN valueN 一次設置多個key的值,成功返回1表示全部的值都設置了,失敗返回0表示沒有任何值被設置
msetnx key1 value1 ... keyN valueN 同上,可是不會覆蓋已經存在的key
incr key 對key的值作加加操做,並返回新的值。注意incr一個不是int的value會返回錯誤,incr一個不存在的key,則設置key爲1
decr key 同上,可是作的是減減操做,decr一個不存在key,則設置key爲-1
incrby key integer 同incr,加指定值 ,key不存在時候會設置key,並認爲原來的value是 0
decrby key integer 同decr,減指定值。decrby徹底是爲了可讀性,咱們徹底能夠經過incrby一個負值來實現一樣效果,反之同樣。
substr 返回截取過的key的字符串值,注意並不修改key的值。下標是從0開始的.(redis在2.0版本之後不包括2.0,使用的方法是getrange 參數相同。)
append key value 給指定key的字符串值追加value,返回新字符串值的長度。下面給個例子
redis> set k hello OK redis> append k ,world (integer) 11 redis> get k "hello,world" substr key start end redis> substr k 0 8 "hello,wor" redis> get k "hello,world"
3. list
redis的list類型其實就是一個每一個子元素都是string類型的雙向鏈表。因此[lr]push和[lr]pop命令的算法時間複雜度都是O(1)
另外list會記錄鏈表的長度。因此llen操做也是O(1).鏈表的最大長度是(2的32次方-1)。咱們能夠經過push,pop操做從鏈表的頭部
或者尾部添加刪除元素。這使得list既能夠用做棧,也能夠用做隊列。有意思的是list的pop操做還有阻塞版本的。當咱們[lr]pop一個
list對象是,若是list是空,或者不存在,會當即返回nil。可是阻塞版本的b[lr]pop能夠則能夠阻塞,固然能夠加超時時間,超時後也會返回nil
。爲何要阻塞版本的pop呢,主要是爲了不輪詢。舉個簡單的例子若是咱們用list來實現一個工做隊列。執行任務的thread能夠調用阻塞版本的pop去
獲取任務這樣就能夠避免輪詢去檢查是否有任務存在。當任務來時候工做線程能夠當即返回,也能夠避免輪詢帶來的延遲。ok下面介紹list相關命令
lpush key string 在key對應list的頭部添加字符串元素,返回1表示成功,0表示key存在且不是list類型
rpush key string 同上,在尾部添加
llen key 返回key對應list的長度,key不存在返回0,若是key對應類型不是list返回錯誤
lrange key start end 返回指定區間內的元素,下標從0開始,負值表示從後面計算,-1表示倒數第一個元素 ,key不存在返回空列表
ltrim key start end 截取list,保留指定區間內元素,成功返回1,key不存在返回錯誤
lset key index value 設置list中指定下標的元素值,成功返回1,key或者下標不存在返回錯誤
lrem key count value 從key對應list中刪除count個和value相同的元素。count爲0時候刪除所有
lpop key 從list的頭部刪除元素,並返回刪除元素。若是key對應list不存在或者是空返回nil,若是key對應值不是list返回錯誤
rpop 同上,可是從尾部刪除
blpop key1...keyN timeout 從左到右掃描返回對第一個非空list進行lpop操做並返回,好比blpop list1 list2 list3 0 ,若是list不存在
list2,list3都是非空則對list2作lpop並返回從list2中刪除的元素。若是全部的list都是空或不存在,則會阻塞timeout秒,timeout爲0表示一直阻塞。
當阻塞時,若是有client對key1...keyN中的任意key進行push操做,則第一在這個key上被阻塞的client會當即返回。若是超時發生,則返回nil。有點像unix的select或者poll
brpop 同blpop,一個是從頭部刪除一個是從尾部刪除
rpoplpush srckey destkey 從srckey對應list的尾部移除元素並添加到destkey對應list的頭部,最後返回被移除的元素值,整個操做是原子的.若是srckey是空
或者不存在返回nil
4. set
redis的set是string類型的無序集合。set元素最大能夠包含(2的32次方-1)個元素。set的是經過hash table實現的,因此添加,刪除,查找的複雜度都是O(1)。hash table會隨着添加或者刪除自動的調整大小。須要注意的是調整hash table大小時候須要同步(獲取寫鎖)會阻塞其餘讀寫操做。可能不久後就會改用跳錶(skip list)來實現
跳錶已經在sorted set中使用了。關於set集合類型除了基本的添加刪除操做,其餘有用的操做還包含集合的取並集(union),交集(intersection),
差集(difference)。經過這些操做能夠很容易的實現sns中的好友推薦和blog的tag功能。下面詳細介紹set相關命令
sadd key member 添加一個string元素到,key對應的set集合中,成功返回1,若是元素以及在集合中返回0,key對應的set不存在返回錯誤
srem key member 從key對應set中移除給定元素,成功返回1,若是member在集合中不存在或者key不存在返回0,若是key對應的不是set類型的值返回錯誤
spop key 刪除並返回key對應set中隨機的一個元素,若是set是空或者key不存在返回nil
srandmember key 同spop,隨機取set中的一個元素,可是不刪除元素
smove srckey dstkey member 從srckey對應set中移除member並添加到dstkey對應set中,整個操做是原子的。成功返回1,若是member在srckey中不存在返回0,若是
key不是set類型返回錯誤
scard key 返回set的元素個數,若是set是空或者key不存在返回0
sismember key member 判斷member是否在set中,存在返回1,0表示不存在或者key不存在
sinter key1 key2...keyN 返回全部給定key的交集
sinterstore dstkey key1...keyN 同sinter,可是會同時將交集存到dstkey下
sunion key1 key2...keyN 返回全部給定key的並集
sunionstore dstkey key1...keyN 同sunion,並同時保存並集到dstkey下
sdiff key1 key2...keyN 返回全部給定key的差集
sdiffstore dstkey key1...keyN 同sdiff,並同時保存差集到dstkey下
smembers key 返回key對應set的全部元素,結果是無序的
5 sorted set
和set同樣sorted set也是string類型元素的集合,不一樣的是每一個元素都會關聯一個double類型的score。sorted set的實現是skip list和hash table的混合體
當元素被添加到集合中時,一個元素到score的映射被添加到hash table中,因此給定一個元素獲取score的開銷是O(1),另外一個score到元素的映射被添加到skip list
並按照score排序,因此就能夠有序的獲取集合中的元素。添加,刪除操做開銷都是O(log(N))和skip list的開銷一致,redis的skip list實現用的是雙向鏈表,這樣就
能夠逆序從尾部取元素。sorted set最常常的使用方式應該是做爲索引來使用.咱們能夠把要排序的字段做爲score存儲,對象的id當元素存儲。下面是sorted set相關命令
zadd key score member 添加元素到集合,元素在集合中存在則更新對應score
zrem key member 刪除指定元素,1表示成功,若是元素不存在返回0
zincrby key incr member 增長對應member的score值,而後移動元素並保持skip list保持有序。返回更新後的score值
zrank key member 返回指定元素在集合中的排名(下標),集合中元素是按score從小到大排序的
zrevrank key member 同上,可是集合中元素是按score從大到小排序
zrange key start end 相似lrange操做從集合中去指定區間的元素。返回的是有序結果
zrevrange key start end 同上,返回結果是按score逆序的
zrangebyscore key min max 返回集合中score在給定區間的元素
zcount key min max 返回集合中score在給定區間的數量
zcard key 返回集合中元素個數
zscore key element 返回給定元素對應的score
zremrangebyrank key min max 刪除集合中排名在給定區間的元素
zremrangebyscore key min max 刪除集合中score在給定區間的元素
6. hash
redis hash是一個string類型的field和value的映射表.它的添加,刪除操做都是O(1)(平均).hash特別適合用於存儲對象。相較於將對象的每一個字段存成
單個string類型。將一個對象存儲在hash類型中會佔用更少的內存,而且能夠更方便的存取整個對象。省內存的緣由是新建一個hash對象時開始是用zipmap(又稱爲small hash)來存儲的。這個zipmap其實並非hash table,可是zipmap相比正常的hash實現能夠節省很多hash自己須要的一些元數據存儲開銷。儘管zipmap的添加,刪除,查找都是O(n),可是因爲通常對象的field數量都不太多。因此使用zipmap也是很快的,也就是說添加刪除平均仍是O(1)。若是field或者value的大小超出必定限制後,redis會在內部自動將zipmap替換成正常的hash實現. 這個限制能夠在配置文件中指定
redis> set test dsf OK redis> set tast dsaf OK redis> set tist adff OK redis> keys t* 1. "tist" 2. "tast" 3. "test" redis> keys t[ia]st 1. "tist" 2. "tast" redis> keys t?st 1. "tist" 2. "tast" 3. "test"
hash-max-zipmap-entries 64 #配置字段最多64個
hash-max-zipmap-value 512 #配置value最大爲512字節
下面介紹hash相關命令
hset key field value 設置hash field爲指定值,若是key不存在,則先建立
hget key field 獲取指定的hash field
hmget key filed1....fieldN 獲取所有指定的hash filed
hmset key filed1 value1 ... filedN valueN 同時設置hash的多個field
hincrby key field integer 將指定的hash filed 加上給定值
hexists key field 測試指定field是否存在
hdel key field 刪除指定的hash field
hlen key 返回指定hash的field數量
hkeys key 返回hash的全部field
hvals key 返回hash的全部value
hgetall 返回hash的全部filed和value
轉自:http://www.cnblogs.com/xhan/archive/2011/02/02/1948891.html
本篇文章介紹下redis排序命令.redis支持對list,set和sorted set元素的排序。排序命令是sort 完整的命令格式以下:
SORT key [BY pattern] [LIMIT start count] [GET pattern] [ASC|DESC] [ALPHA] [STORE dstkey]
下面咱們一一說明各類命令選項
(1)sort key
這個是最簡單的狀況,沒有任何選項就是簡單的對集合自身元素排序並返回排序結果.下面給個例子
redis> lpush ml 12 (integer) 1 redis> lpush ml 11 (integer) 2 redis> lpush ml 23 (integer) 3 redis> lpush ml 13 (integer) 4 redis> sort ml 1. "11" 2. "12" 3. "13" 4. "23"
(2)[ASC|DESC] [ALPHA]
sort默認的排序方式(asc)是從小到大排的,固然也能夠按照逆序或者按字符順序排。逆序能夠加上desc選項,想按字母順序排能夠加alpha選項,固然alpha能夠和desc一塊兒用。下面是個按字母順序排的例子
redis> lpush mylist baidu (integer) 1 redis> lpush mylist hello (integer) 2 redis> lpush mylist xhan (integer) 3 redis> lpush mylist soso (integer) 4 redis> sort mylist 1. "soso" 2. "xhan" 3. "hello" 4. "baidu" redis> sort mylist alpha 1. "baidu" 2. "hello" 3. "soso" 4. "xhan" redis> sort mylist desc alpha 1. "xhan" 2. "soso" 3. "hello" 4. "baidu"
(3)[BY pattern]
除了能夠按集合元素自身值排序外,還能夠將集合元素內容按照給定pattern組合成新的key,並按照新key中對應的內容進行排序。下面的例子接着使用第一個例子中的ml集合作演示:
redis> set name11 nihao OK redis> set name12 wo OK redis> set name13 shi OK redis> set name23 lala OK redis> sort ml by name* 1. "13" 2. "23" 3. "11" 4. "12"
*表明了ml中的元素值,因此這個排序是按照name12 name13 name23 name23這四個key對應值排序的,固然返回的仍是排序後ml集合中的元素
(對排序結果有疑問可看最下面的FAQ)
(4)[GET pattern]
上面的例子都是返回的ml集合中的元素。咱們也能夠經過get選項去獲取指定pattern做爲新key對應的值。看個組合起來的例子
redis> sort ml by name* get name* alpha 1. "lala" 2. "nihao" 3. "shi" 4. "wo"
這 次返回的就不在是ml中的元素了,而是name12 name13 name23 name23對應的值。固然排序是按照name12 name13 name23 name23值並根據字母順序排的。另外get選項能夠有多個。看例子(#特殊符號引用的是原始集合也就是ml)
redis> sort ml by name* get name* get # alpha 1. "lala" 2. "23" 3. "nihao" 4. "11" 5. "shi" 6. "13" 7. "wo" 8. "12"
最後在還有一個引用hash類型字段的特殊字符->,下面是例子
redis> hset user1 name hanjie (integer) 1 redis> hset user11 name hanjie (integer) 1 redis> hset user12 name 86 (integer) 1 redis> hset user13 name lxl (integer) 1 redis> sort ml get user*->name 1. "hanjie" 2. "86" 3. "lxl" 4. (nil)
很容易理解,注意當對應的user23不存在時候返回的是nil
(5) [LIMIT start count]
上面例子返回結果都是所有。limit選項能夠限定返回結果的數量。例子
redis> sort ml get name* limit 1 2 1. "wo" 2. "shi"
start下標是從0開始的,這裏的limit選項意思是從第二個元素開始獲取2個
(6)[STORE dstkey]
若是對集合常常按照固定的模式去排序,那麼把排序結果緩存起來會減小很多cpu開銷.使用store選項能夠將排序內容保存到指定key中。保存的類型是list
redis> sort ml get name* limit 1 2 store cl (integer) 2 redis> type cl list redis> lrange cl 0 -1 1. "wo" 2. "shi"
這個例子咱們將排序結果保存到了cl中
功能介紹完後,再討論下關於排序的一些問題。若是咱們有多個redis server的話,不一樣的key可能存在於不一樣的server上。好比name12 name13 name23 name23,頗有可能分別在四個不一樣的server上存貯着。這種狀況會對排序性能形成很大的影響。redis做者在他的blog上提到了這個問題的解 決辦法,就是經過key tag將須要排序的key都放到同一個server上 。因爲具體決定哪一個key存在哪一個服務器上通常都是在client端hash的辦法來作的。咱們能夠經過只對key的部分進行hash.舉個例子假如咱們 的client若是發現key中包含[]。那麼只對key中[]包含的內容進行hash。咱們將四個name相關的key,都這樣命名[name]12 [name]13 [name]23 [name]23,因而client 程序就會把他們都放到同一server上。不知道jredis實現了沒。
還有一個問題也比較嚴重。若是要sort的集合很是大的話排序就會消耗很長時間。因爲redis單線程的,因此長時間的排序操做會阻塞其餘client的 請求。解決辦法是經過主從複製機制將數據複製到多個slave上。而後咱們只在slave上作排序操做。並進可能的對排序結果緩存。另外就是一個方案是就 是採用sorted set對須要按某個順序訪問的集合創建索引。
實例:
redis> sadd tom:friend:list 123 #tom的好友列表 裏面是好友的uid 1 redis> sadd tom:friend:list 456 1 redis> sadd tom:friend:list 789 1 redis> sadd tom:friend:list 101 1 redis> set uid:sort:123 1000 #uid對應的成績 OK redis> set uid:sort:456 6000 OK redis> set uid:sort:789 100 OK redis> set uid:sort:101 5999 OK redis> set uid:123 "{'uid':123,'name':'lucy'}" #增長uid對應好友信息 OK redis> set uid:456 "{'uid':456,'name':'jack'}" OK redis> set uid:789 "{'uid':789,'name':'marry'}" OK redis> set uid:101 "{'uid':101,'name':'icej'}" OK redis> sort tom:friend:list by uid:sort:* get uid:* #從好友列表中得到id與uid:sort字段匹配後排序,並根據排序後的順序,用key在uid表得到信息 1. {'uid':789,'name':'marry'} 2. {'uid':123,'name':'lucy'} 3. {'uid':101,'name':'icej'} 4. {'uid':456,'name':'jack'} redis> sort tom:friend:list by uid:sort:* get uid:* get uid:sort:* 1. {'uid':789,'name':'marry'} 2. 100 3. {'uid':123,'name':'lucy'} 4. 1000 5. {'uid':101,'name':'icej'} 6. 5999 7. {'uid':456,'name':'jack'} 8. 6000
FAQ:
1.sort ml by name* get name* get # 爲何會按照shi lala nihao wo的順序排下來,這個跟單純的排序name*和name * alpha的結果都不同
這個問題要從redis的實現邏輯上來分析了
a)list在插入後 ,默認是按照時間的前後反序排列的 , lrange ml 0 -1,結果是:13 23 11 12. 這是由於list插入時是將最新的item插入到鏈表頭
b)sort m1 by name* 肯定是會按照name*的值進行排序的.但當name*對應的value不是num型而且沒有設置alpha的時候,會致使排序分值都是相同的,由於程序將把name*對應的值嘗試轉換爲nun型
c)這就會致使sort ml by name*會按照ml的天然順序進行排列了
if (alpha) { if (sortby) vector[j].u.cmpobj = getDecodedObject(byval); } else { if (byval->encoding == REDIS_ENCODING_RAW) { vector[j].u.score = strtod(byval->ptr,NULL); } else if (byval->encoding == REDIS_ENCODING_INT) { /* Don't need to decode the object if it's * integer-encoded (the only encoding supported) so * far. We can just cast it */ vector[j].u.score = (long)byval->ptr; } else { redisAssert(1 != 1); } }
參考:
http://www.cnblogs.com/xhan/archive/2011/02/03/1948970.html
http://icej.javaeye.com/blog/517795
redis對事務的支持目前還比較簡單。redis只能保證一個client發起的事務中的命令能夠連續的執行,而中間不會插入其餘client的命令。 因爲redis是單線程來處理全部client的請求的因此作到這點是很容易的。通常狀況下redis在接受到一個client發來的命令後會當即處理並 返回處理結果,可是當一個client在一個鏈接中發出multi命令有,這個鏈接會進入一個事務上下文,該鏈接後續的命令並非當即執行,而是先放到一 個隊列中。當今後鏈接受到exec命令後,redis會順序的執行隊列中的全部命令。並將全部命令的運行結果打包到一塊兒返回給client.而後此鏈接就 結束事務上下文。下面能夠看一個例子
redis> multi OK redis> incr a QUEUED redis> incr b QUEUED redis> exec 1. (integer) 1 2. (integer) 1
從這個例子咱們能夠看到incr a ,incr b命令發出後並沒執行而是被放到了隊列中。調用exec後倆個命令被連續的執行,最後返回的是兩條命令執行後的結果
咱們能夠調用discard命令來取消一個事務。接着上面例子
redis> multi OK redis> incr a QUEUED redis> incr b QUEUED redis> discard OK redis> get a "1" redis> get b "1"
能夠發現此次incr a incr b都沒被執行。discard命令其實就是清空事務的命令隊列並退出事務上下文。
雖然說redis事務在本質上也至關於序列化隔離級別的了。可是因爲事務上下文的命令只排隊並不當即執行,因此事務中的寫操做不能依賴事務中的讀操做結果。看下面例子
redis> multi OK redis> get a QUEUED redis> get b QUEUED redis> exec 1. "1" 2. "1"
發現問題了吧。假如咱們想用事務實現incr操做怎麼辦?能夠這樣作嗎?
redis> get a "1" redis> multi OK redis> set a 2 QUEUED redis> exec 1. OK redis> get a, "2"
結論很明顯這樣是不行的。這樣和 get a 而後直接set a是沒區別的。很明顯因爲get a 和set a並不能保證兩個命令是連續執行的(get操做不在事務上下文中)。極可能有兩個client同時作這個操做。結果咱們指望是加兩次a從原來的1變成3. 可是頗有可能兩個client的get a,取到都是1,形成最終加兩次結果倒是2。主要問題咱們沒有對共享資源a的訪問進行任何的同步
也就是說redis沒提供任何的加鎖機制來同步對a的訪問。
還好redis 2.1後添加了watch命令,能夠用來實現樂觀鎖。看個正確實現incr命令的例子,只是在前面加了watch a
redis> watch a OK redis> get a "1" redis> multi OK redis> set a 2 QUEUED redis> exec 1. OK redis> get a, "2"
watch 命令會監視給定的key,當exec時候若是監視的key從調用watch後發生過變化,則整個事務會失敗。也能夠調用watch屢次監視多個key.這 樣就能夠對指定的key加樂觀鎖了。注意watch的key是對整個鏈接有效的,事務也同樣。若是鏈接斷開,監視和事務都會被自動清除。固然了 exec,discard,unwatch命令都會清除鏈接中的全部監視.
redis的事務實現是如此簡單,固然會存在一些問題。第一個問題是redis只能保證事務的每一個命令連續執行,可是若是事務中的一個命令失敗了,並不回滾其餘命令,好比使用的命令類型不匹配。
redis> set a 5 OK redis> lpush b 5 (integer) 1 redis> set c 5 OK redis> multi OK redis> incr a QUEUED redis> incr b QUEUED redis> incr c QUEUED redis> exec 1. (integer) 6 2. (error) ERR Operation against a key holding the wrong kind of value 3. (integer) 6
能夠看到雖然incr b失敗了,可是其餘兩個命令仍是執行了。
最 後一個十分罕見的問題是 當事務的執行過程當中,若是redis意外的掛了。很遺憾只有部分命令執行了,後面的也就被丟棄了。固然若是咱們使用的append-only file方式持久化,redis會用單個write操做寫入整個事務內容。便是是這種方式仍是有可能只部分寫入了事務到磁盤。發生部分寫入事務的狀況 下,redis重啓時會檢測到這種狀況,而後失敗退出。可使用redis-check-aof工具進行修復,修復會刪除部分寫入的事務內容。修復完後就 可以從新啓動了。
from:http://www.cnblogs.com/xhan/archive/2011/02/04/1949151.html
redis是一個cs模式的tcp server,使用和http相似的請求響應協議。一個client能夠經過一個socket鏈接發起多個請求命令。每一個請求命令發出後client一般 會阻塞並等待redis服務處理,redis處理完後請求命令後會將結果經過響應報文返回給client。基本的通訊過程以下
Client: INCR X Server: 1 Client: INCR X Server: 2 Client: INCR X Server: 3 Client: INCR X Server: 4
基 本上四個命令須要8個tcp報文才能完成。因爲通訊會有網絡延遲,假如從client和server之間的包傳輸時間須要0.125秒。那麼上面的四個命 令8個報文至少會須要1秒才能完成。這樣即便redis每秒能處理100個命令,而咱們的client也只能一秒鐘發出四個命令。這顯示沒有充分利用 redis的處理能力。除了能夠利用mget,mset 之類的單條命令處理多個key的命令外
咱們還能夠利用pipeline的方式從client打包多條命令一塊兒發出,不須要等待單條命令的響應返回,而redis服務端會處理完多條命令後會將多條命令的處理結果打包到一塊兒返回給客戶端。通訊過程以下
Client: INCR X Client: INCR X Client: INCR X Client: INCR X Server: 1 Server: 2 Server: 3 Server: 4
假 設不會由於tcp 報文過長而被拆分。可能兩個tcp報文就能完成四條命令,client能夠將四個incr命令放到一個tcp報文一塊兒發送,server則能夠將四條命令 的處理結果放到一個tcp報文返回。經過pipeline方式當有大批量的操做時候。咱們能夠節省不少原來浪費在網絡延遲的時間。須要注意到是用 pipeline方式打包命令發送,redis必須在處理完全部命令前先緩存起全部命令的處理結果。打包的命令越多,緩存消耗內存也越多。因此並是否是打 包的命令越多越好。具體多少合適須要根據具體狀況測試。下面是個jredis客戶端使用pipeline的測試
package jredisStudy; import org.jredis.JRedis; import org.jredis.connector.ConnectionSpec; import org.jredis.ri.alphazero.JRedisClient; import org.jredis.ri.alphazero.JRedisPipelineService; import org.jredis.ri.alphazero.connection.DefaultConnectionSpec; public class PipeLineTest { public static void main(String[] args) { long start = System.currentTimeMillis(); usePipeline(); long end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); withoutPipeline(); end = System.currentTimeMillis(); System.out.println(end-start); } private static void withoutPipeline() { try { JRedis jredis = new JRedisClient("192.168.56.55",6379); for(int i =0 ; i < 100000 ; i++) { jredis.incr("test2"); } jredis.quit(); } catch (Exception e) { } } private static void usePipeline() { try { ConnectionSpec spec = DefaultConnectionSpec.newSpec("192.168.56.55", 6379, 0, null); JRedis jredis = new JRedisPipelineService(spec); for(int i =0 ; i < 100000 ; i++) { jredis.incr("test2"); } jredis.quit(); } catch (Exception e) { } } }
輸出
103408 //使用了pipeline
104598 //沒有使用
測試結果不是很明顯,這應該是跟個人測試環境有關。我是在本身win鏈接虛擬機的linux。網絡延遲比較小。因此pipeline
優點不明顯。若是網絡延遲小的話,最好仍是不用pipeline。除了增長複雜外,帶來的性能提高不明顯。
發佈訂閱(pub/sub)是一種消息通訊模式,主要的目的是解耦消息發佈者和消息訂閱者之間的耦合,這點和設計模式中的觀察者模式比較類似。pub /sub不只僅解決發佈者和訂閱者直接代碼級別耦合也解決二者在物理部署上的耦合。redis做爲一個pub/sub server,在訂閱者和發佈者之間起到了消息路由的功能。訂閱者能夠經過subscribe和psubscribe命令向redis server訂閱本身感興趣的消息類型,redis將消息類型稱爲通道(channel)。當發佈者經過publish命令向redis server發送特定類型的消息時。訂閱該消息類型的所有client都會收到此消息。這裏消息的傳遞是多對多的。一個client能夠訂閱多個 channel,也能夠向多個channel發送消息。
下面作個實驗。這裏使用兩個不一樣的client一個是redis自帶的redis-cli另外一個是用Java寫的簡單的client。代碼以下
import java.net.*; import java.io.*; public class PubSubTest { public static void main(String[] args) { String cmd = args[0]+"\r\n"; try { Socket socket = new Socket("192.168.56.55",6379); InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); out.write(cmd.getBytes()); //發送訂閱命令 byte[] buffer = new byte[1024]; while (true) { int readCount = in.read(buffer); System.out.write(buffer, 0, readCount); System.out.println("--------------------------------------"); } } catch (Exception e) { } } }
代碼就是簡單的從命令行讀取傳過來的訂閱命令,而後經過一個socket鏈接發送給redis server,而後進入while循環一直讀取redis server傳過來訂閱的消息。並打印到控制檯
1 首先編譯並運行此java程序(我是win7下面運行的)
D:\>javac PubSubTest.java D:\>java PubSubTest "subscribe news.share news.blog" *3 $9 subscribe $10 news.share :1 *3 $9 subscribe $9 news.blog :2
--------------------------------------
2 啓動redis-cli
redis> psubscribe news.* Reading messages... (press Ctrl-c to quit) 1. "psubscribe" 2. "news.*" 3. (integer) 1
3 再啓動一個redis-cli用來發布兩條消息
redis> publish news.share "share a link http://www.google.com" (integer) 2 redis> publish news.blog "I post a blog" (integer) 2
4.查看兩個訂閱client的輸出
此時java client打印以下內容
*3 $7 message $10 news.share $34 share a link http://www.google.com -------------------------------------- *3 $7 message $9 news.blog $13 I post a blog
--------------------------------------
另外一個redis-cli輸出以下
1. "pmessage" 2. "news.*" 3. "news.share" 4. "share a link http://www.google.com" 1. "pmessage" 2. "news.*" 3. "news.blog" 4. "I post a blog"
分析下
java client使用subscribe命令訂閱news.share和news.blog兩個通道,而後當即收到server返回的訂閱成功消息,能夠看出 redis的協議是文本類型的,這裏不解釋具體協議內容了,能夠參考http://redis.io/topics/protocol或者http://terrylee.me/blog/post/2011/01/26/redis-internal-part3.aspx。這個報文內容有兩部分,第一部分表示該socket鏈接上使用 subscribe訂閱news.share成功後,此鏈接訂閱通道數爲1,後一部分表示使用subscribe訂閱news.blog成功後,該鏈接訂 閱通道總數爲2。
redis client使用psubscribe訂閱了一個使用通配符的通道(*表示任意字符串),此訂閱會收到全部與news.*匹配的通道消息。redis- cli打印到控制檯的訂閱成功消息表示使用psubscribe命令訂閱news.*成功後,鏈接訂閱通道總數爲1。
當咱們在一個client使用publish 向news.share和news.blog通道發出兩個消息後。redis返回的(integer) 2表示有兩個鏈接收到了此消息。經過觀察兩個訂閱者的輸出能夠驗證。具體格式不解釋了,都比較簡單。
看 完一個小例子後應該對pub/sub功能有了一個感性的認識。須要注意的是當一個鏈接經過subscribe或者psubscribe訂閱通道後就進入訂 閱模式。在這種模式除了再訂閱額外的通道或者用unsubscribe或者punsubscribe命令退出訂閱模式,就不能再發送其餘命令。另外使用 psubscribe命令訂閱多個通配符通道,若是一個消息匹配上了多個通道模式的話,會屢次收到同一個消息。
jredis目前版本沒提供pub/sub支持,不過本身實現一個應該也挺簡單的。整個應用程序能夠共享同一個鏈接。由於redis返回的消息報文中除了消息內容自己外還包括消息相關的通道信息,當收到消息後能夠根據不一樣的通道信息去調用不一樣的callback來處理。
另外我的以爲redis的pub/sub仍是有點太單薄(實現才用150行代碼)。在安全,認證,可靠性這方便都沒有太多支持。
from:http://www.cnblogs.com/xhan/archive/2011/02/06/1949473.html
redis是一個支持持久化的內存數據庫,也就是說redis須要常常將內存中的數據同步到磁盤來保證持久化。redis支持兩種持久化方式,一種是 Snapshotting(快照)也是默認方式,另外一種是Append-only file(縮寫aof)的方式。下面分別介紹
Snapshotting
快照是默認的持久化方式。這種方式是就是將內存中數據以快照的方式寫入到二進制文件中,默認的文件名爲dump.rdb。能夠經過配置設置自動作快照持久 化的方式。咱們能夠配置redis在n秒內若是超過m個key被修改就自動作快照,下面是默認的快照保存配置
save 900 1 #900秒內若是超過1個key被修改,則發起快照保存
save 300 10 #300秒內容如超過10個key被修改,則發起快照保存
save 60 10000
下面介紹詳細的快照保存過程
1.redis調用fork,如今有了子進程和父進程。
2. 父進程繼續處理client請求,子進程負責將內存內容寫入到臨時文件。因爲os的寫時複製機制(copy on write)父子進程會共享相同的物理頁面,當父進程處理寫請求時os會爲父進程要修改的頁面建立副本,而不是寫共享的頁面。因此子進程的地址空間內的數 據是fork時刻整個數據庫的一個快照。
3.當子進程將快照寫入臨時文件完畢後,用臨時文件替換原來的快照文件,而後子進程退出。
client 也可使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主線程中保存快照的,因爲redis是用一個主線程來處理全部 client的請求,這種方式會阻塞全部client請求。因此不推薦使用。另外一點須要注意的是,每次快照持久化都是將內存數據完整寫入到磁盤一次,並不 是增量的只同步髒數據。若是數據量大的話,並且寫操做比較多,必然會引發大量的磁盤io操做,可能會嚴重影響性能。
另外因爲快照方式是在必定間隔時間作一次的,因此若是redis意外down掉的話,就會丟失最後一次快照後的全部修改。若是應用要求不能丟失任何修改的話,能夠採用aof持久化方式。下面介紹
Append-only file
aof 比快照方式有更好的持久化性,是因爲在使用aof持久化方式時,redis會將每個收到的寫命令都經過write函數追加到文件中(默認是 appendonly.aof)。當redis重啓時會經過從新執行文件中保存的寫命令來在內存中重建整個數據庫的內容。固然因爲os會在內核中緩存 write作的修改,因此可能不是當即寫到磁盤上。這樣aof方式的持久化也仍是有可能會丟失部分修改。不過咱們能夠經過配置文件告訴redis咱們想要 經過fsync函數強制os寫入到磁盤的時機。有三種方式以下(默認是:每秒fsync一次)
appendonly yes //啓用aof持久化方式
# appendfsync always //每次收到寫命令就當即強制寫入磁盤,最慢的,可是保證徹底的持久化,不推薦使用
appendfsync everysec //每秒鐘強制寫入磁盤一次,在性能和持久化方面作了很好的折中,推薦
# appendfsync no //徹底依賴os,性能最好,持久化沒保證
aof 的方式也同時帶來了另外一個問題。持久化文件會變的愈來愈大。例如咱們調用incr test命令100次,文件中必須保存所有的100條命令,其實有99條都是多餘的。由於要恢復數據庫的狀態其實文件中保存一條set test 100就夠了。爲了壓縮aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis將使用與快照相似的方式將內存中的數據 以命令的方式保存到臨時文件中,最後替換原來的文件。具體過程以下
1. redis調用fork ,如今有父子兩個進程
2. 子進程根據內存中的數據庫快照,往臨時文件中寫入重建數據庫狀態的命令
3.父進程繼續處理client請求,除了把寫命令寫入到原來的aof文件中。同時把收到的寫命令緩存起來。這樣就能保證若是子進程重寫失敗的話並不會出問題。
4.當子進程把快照內容寫入已命令方式寫到臨時文件中後,子進程發信號通知父進程。而後父進程把緩存的寫命令也寫入到臨時文件。
5.如今父進程可使用臨時文件替換老的aof文件,並重命名,後面收到的寫命令也開始往新的aof文件中追加。
須要注意到是重寫aof文件的操做,並無讀取舊的aof文件,而是將整個內存中的數據庫內容用命令的方式重寫了一個新的aof文件,這點和快照有點相似。
from:http://www.cnblogs.com/xhan/archive/2011/02/07/1949640.html
redis主從複製配置和使用都很是簡單。經過主從複製能夠容許多個slave server擁有和master server相同的數據庫副本。下面是關於redis主從複製的一些特色
1.master能夠有多個slave
2.除了多個slave連到相同的master外,slave也能夠鏈接其餘slave造成圖狀結構
3.主從複製不會阻塞master。也就是說當一個或多個slave與master進行初次同步數據時,master能夠繼續處理client發來的請求。相反slave在初次同步數據時則會阻塞不能處理client的請求。
4.主從複製能夠用來提升系統的可伸縮性,咱們能夠用多個slave 專門用於client的讀請求,好比sort操做可使用slave來處理。也能夠用來作簡單的數據冗餘
5.能夠在master禁用數據持久化,只須要註釋掉master 配置文件中的全部save配置,而後只在slave上配置數據持久化。
下面介紹下主從複製的過程
當設置好slave服務器後,slave會創建和master的鏈接,而後發送sync命令。不管是第一次同步創建的鏈接仍是鏈接斷開後的從新連 接,master都會啓動一個後臺進程,將數據庫快照保存到文件中,同時master主進程會開始收集新的寫命令並緩存起來。後臺進程完成寫文件 後,master就發送文件給slave,slave將文件保存到磁盤上,而後加載到內存恢復數據庫快照到slave上。接着master就會把緩存的命 令轉發給slave。並且後續master收到的寫命令都會經過開始創建的鏈接發送給slave。從master到slave的同步數據的命令和從 client發送的命令使用相同的協議格式。當master和slave的鏈接斷開時slave能夠自動從新創建鏈接。若是master同時收到多個 slave發來的同步鏈接命令,只會使用啓動一個進程來寫數據庫鏡像,而後發送給全部slave。
配置slave服務器很簡單,只須要在配置文件中加入以下配置
slaveof 192.168.1.1 6379 #指定master的ip和端口
首先說明下redis的虛擬內存與os的虛擬內存不是一碼事,可是思路和目的都是相同的。就是暫時把不常常訪問的數據從內存交換到磁盤中,從而騰出寶貴的 內存空間用於其餘須要訪問的數據。尤爲是對於redis這樣的內存數據庫,內存老是不夠用的。除了能夠將數據分割到多個redis server外。另外的可以提升數據庫容量的辦法就是使用vm把那些不常常訪問的數據交換的磁盤上。若是咱們的存儲的數據老是有少部分數據被常常訪問,大 部分數據不多被訪問,對於網站來講確實老是隻有少許用戶常常活躍。當少許數據被常常訪問時,使用vm不但能提升單臺redis server數據庫的容量,並且也不會對性能形成太多影響。
redis沒有使用os提供的虛擬內存機制而是本身在用戶態實現了本身的虛擬內存機制,做者在本身的blog專門解釋了其中緣由。http://antirez.com/post/redis-virtual-memory-story.html
主要的理由有兩點
1.os 的虛擬內存是已4k頁面爲最小單位進行交換的。而redis的大多數對象都遠小於4k,因此一個os頁面上可能有多個redis對象。另外redis的集 合對象類型如list,set可能存在與多個os頁面上。最終可能形成只有10%key被常常訪問,可是全部os頁面都會被os認爲是活躍的,這樣只有內 存真正耗盡時os纔會交換頁面。
2.相比於os的交換方式。redis能夠將被交換到磁盤的對象進行壓縮,保存到磁盤的對象能夠去除指針和對象元數據信息。通常壓縮後的對象會比內存中的對象小10倍。這樣redis的vm會比os vm能少作不少io操做。
下面是vm相關配置
vm-enabled yes #開啓vm功能
vm-swap-file /tmp/redis.swap #交換出來的value保存的文件路徑/tmp/redis.swap
vm-max-memory 1000000 #redis使用的最大內存上限,超過上限後redis開始交換value到磁盤文件中。
vm-page-size 32 #每一個頁面的大小32個字節
vm-pages 134217728 #最多使用在文件中使用多少頁面,交換文件的大小 = vm-page-size * vm-pages
vm-max-threads 4 #用於執行value對象換入換出的工做線程數量。0表示不使用工做線程(後面介紹)
redis的vm在設計上爲了保證key的查找速度,只會將value交換到swap文件中。因此若是是內存問題是因爲太多value很小的key形成 的,那麼vm並不能解決。和os同樣redis也是按頁面來交換對象的。redis規定同一個頁面只能保存一個對象。可是一個對象能夠保存在多個頁面中。 在redis使用的內存沒超過vm-max-memory以前是不會交換任何value的。當超過最大內存限制後,redis會選擇較老的對象。若是兩個 對象同樣老會優先交換比較大的對象,精確的公式swappability = age*log(size_in_memory)。 對於vm-page-size的設置應該根據本身的應用將頁面的大小設置爲能夠容納大多數對象的大小。太大了會浪費磁盤空間,過小了會形成交換文件出現碎 片。對於交換文件中的每一個頁面,redis會在內存中對應一個1bit值來記錄頁面的空閒狀態。因此像上面配置中頁面數量(vm-pages 134217728 )會佔用16M內存用來記錄頁面空閒狀態。vm-max-threads表示用作交換任務的線程數量。若是大於0推薦設爲服務器的cpu core的數量。若是是0則交換過程在主線程進行。
參數配置討論完後,在來簡單介紹下vm是如何工做的,
當vm-max-threads設爲0時(Blocking VM)
換出
主線程按期檢查發現內存超出最大上限後,會直接已阻塞的方式,將選中的對象保存到swap文件中,並釋放對象佔用的內存,此過程會一直重複直到下面條件知足
1.內存使用降到最大限制如下
2.swap文件滿了
3.幾乎所有的對象都被交換到磁盤了
換入
當有client請求value被換出的key時。主線程會以阻塞的方式從文件中加載對應的value對象,加載時此時會阻塞因此client。而後處理client的請求
當vm-max-threads大於0(Threaded VM)
換出
當主線程檢測到使用內存超過最大上限,會將選中的要交換的對象信息放到一個隊列中交由工做線程後臺處理,主線程會繼續處理client請求。
換入
若是有client請求的key被換出了,主線程先阻塞發出命令的client,而後將加載對象的信息放到一個隊列中,讓工做線程去加載。加載完畢後工做線程通知主線程。主線程再執行client的命令。這種方式只阻塞請求value被換出key的client
總 的來講blocking vm的方式總的性能會好一些,由於不須要線程同步,建立線程和恢復被阻塞的client等開銷。可是也相應的犧牲了響應性。threaded vm的方式主線程不會阻塞在磁盤io上,因此響應性更好。若是咱們的應用不太常常發生換入換出,並且也不太在乎有點延遲的話則推薦使用blocking vm的方式。關於redis vm的更詳細介紹能夠參考下面連接
http://antirez.com/post/redis-virtual-memory-story.html
http://redis.io/topics/internals-vm
from:http://www.cnblogs.com/xhan/archive/2011/02/07/1949717.html
redis主頁上列出的java 客戶端有JDBC-Redis JRedis Jedis三種,下面分別介紹三種客戶端的優缺點及其餘相關的工具.
|
支持redis版本 | 性能 | 維護 | 推薦 |
JDBC-Redis | not good | |||
JRedis | 1.2.n release 2.0.0 還沒有release版本 |
fast | ||
Jedis | 2.0.0 release | fast | actively developed | 推薦 |
JDBC-Redis
JDBC-Redis is just a JDBC wrapper for JRedis database.
If you plan on using your code with different back-ends then JDBC is a good way to Go. NOTE: It is not a complete JDBC implementation and the NOSQL will bleed through.
If you are going to stay with Redis then I would suggest using the API, which will give you more flexibility. Use a DAO layer pattern to encapsulate your DB Access and down the road that is all you will need to change.
Redis syntax is completely different from standard SQL so using JDBC doesn't help encapsulating different back-ends as you suggest: I would have to write new queries anyway... – muriloq Jun 16 '10 at 14:00
@muriloq - but the mechanical acquiring and releasing resources is standard. – Romain Hippeau
spring wrapper
Spring provides a wrapper around both implementations(Jredis Jedis) and they're providing serialization/deserialization, amongst other things:
Person p = new Person("Joe", "Trader", 33); template.convertAndSet("trader:1", p); Person samePerson = template.getAndConvert("trader:1", Person.class); Assert.assertEquals(p, samePerson);
上面的方法可能已經調整,請參見最新的 http://static.springsource.org/spring-data/data-keyvalue/docs/1.0.0.M2/reference/html/#redis
放棄spring wrapper
項目中原本打算使用spring wrapper,出於如下緣由最終仍是放棄,直接使用Jedis,等有時間在把:
1.spring wrapper的版本是1.0.0.M2,裏面有些bug (*)
2.對shard的支持沒有jedis好
3.依賴spring3.0(主要是spring3.0 core中的convert及serializer),咱們目前大多項目仍是採用spring2.5.6(主要)
4.通過多層封裝後性能仍是會有損耗
spring nosql/cross-store
prototype implementation allowing entities to be stored in multiple types of data stores (i.e. JPA and Neo4j or JPA and Redis etc.)
JOhm
JOhm is a blazingly fast Object-Hash Mapping library for Java inspired by the awesome Ohm. The JOhm OHM is a modern-day avatar of the old ORM's like hibernate with the difference being that we are not dealing with an RDBMS here but with a NoSQL rockstar.
homepage:https://github.com/xetorthio/johm
jedis pool的問題
在使用jedis pool時遇到了這個問題:It seems like server has closed the connection
緣由分析:
1.redis server 關閉了此客戶端的鏈接:server端設置了maxidletime(默認是5分鐘),服務端會不斷循環檢測clinet的最後一次通訊時間(lastinteraction),若是大於maxidletime,則關閉鏈接,並回收相關資源。client在向該鏈接中寫數據後就會因爲server端已經關閉而出現 broken pipe的問題。
2.pool的設置錯誤:
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="20" />
<property name="maxIdle" value="10" />
<property name="maxWait" value="1000" />
</bean>
<!-- jedis shard信息配置 -->
<bean id="jedis.shardInfo" class="redis.clients.jedis.JedisShardInfo">
<constructor-arg index="0" value="*.*.*.*" />
<constructor-arg index="1" value="6379" />
</bean>
<!-- jedis shard pool配置 -->
<bean id="shardedJedisPool" class="redis.clients.jedis.ShardedJedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1">
<list>
<ref bean="jedis.shardInfo" />
</list>
</constructor-arg>
</bean>
<bean id="jedisCommands" factory-bean="shardedJedisPool"
factory-method="getResource" />
上面的這種配法在spring初始化時獲取一次實例化jedisCommands,然後每次的redis的調用時並未從pool中獲取
解決方案:
設置