redis 详细介绍

一.redis 环境搭建

 

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 数据类型

 

 

 

 

本文介绍下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排序命令.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对事务的支持目前还比较简单。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 pipeline

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。除了增长复杂外,带来的性能提高不明显。

 

 

 

六.redis 发布订阅

       发布订阅(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须要常常将内存中的数据同步到磁盘来保证持久化。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 主从复制

      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学习笔记之虚拟内存

        首先说明下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 client

    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.

Romain Hippeau

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中获取

解决方案:

设置

相关文章
相关标签/搜索