一、RedisTemplate和StringRedisTemplate
1、redisTemplate、StringRedisTemplate是springdata提供的管理redis的工具
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
2、StringRedisTemplate与RedisTemplate区别
两者的关系是StringRedisTemplate继承RedisTemplate
SDK默认的序列化策略有两种:
一种是String的序列化策略StringRedisSerializer,一种是JDK的序列化策略JdkSerializationRedisSerializer
StringRedisTemplate默认采用的事String的序列化策略 JdkSerializationRedisSerializer,保存的key和value都是采用此序列化保存的
RedisTemplate默认采用的事JDK的序列化策略StringRedisSerializer,保存的key和value都是采用此序列化策略保存的。RedisTemplate使用的序列类在在操作数据的时候,比如说存入数据会将数据先序列化成字节数组。然后在存入Redis数据库,这个时候打开Redis查看的时候,你会看到你的数据不是以可读的形式展现的,而是以字节数组显示,类似下面
当然从Redis获取数据的时候也会默认将数据当做字节数组转化,这样就会导致一个问题,当需要获取的数据不是以字节数组存在redis当中而是正常的可读的字符串的时候,比如说下面这种形式的数据
RedisTemplate就无法获取导数据,这个时候获取到的值就是NULL。这个时候StringRedisTempate就派上了用场
当Redis当中的数据值是以可读的形式显示出来的时候,只能使用StringRedisTemplate才能获取到里面的数据。
所以当你使用RedisTemplate获取不到数据的时候请检查一下是不是Redis里面的数据是可读形式而非字节数组
总结:
当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可,
但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是
更好的选择
RedisTemplate的使用
https://www.jianshu.com/p/7bf5dc61ca06
两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,
RedisTemplate只能管理RedisTemplate中的数据
2、Jedis是redis.clients的管理工具
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
一、使用RedisTemplate访问Redis数据结构
http://www.jianshu.com/p/7bf5dc61ca06
二、Jedis
windows版
查看redis连接数 :info clients
查看最大连接数: config get maxclients
Redis常用命令
List操作
lpush优先级要比rpush 更高,lpush 直接把消息放在第一个,当第二个lpush进来后,原先第一位被顶替。
lpush stringList "string1"
1.lpush
在key对应 list的头部添加字符串元素
2.rpush
在key对应 list 的尾部添加字符串元素
3.linsert
在key对应 list 的特定位置之前或之后添加字符串元素
redis 127.0.0.1:6379> rpush mylist3 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist3 "world"
(integer) 2
redis 127.0.0.1:6379> linsert mylist3 before "world" "there"
(integer) 3
redis 127.0.0.1:6379> lrange mylist3 0 -1
1) "hello"
2) "there"
3) "world"
redis 127.0.0.1:6379>
在此处我们先插入了一个 hello,然后在 hello 的尾部插入了一个 world,然后又在 world 的
前面插入了 there。
4.lset
设置list中指定下标的元素值(下标从0开始)
redis 127.0.0.1:6379> rpush mylist4 "one"
(integer) 1
redis 127.0.0.1:6379> rpush mylist4 "two"
(integer) 2
redis 127.0.0.1:6379> rpush mylist4 "three"
(integer) 3
redis 127.0.0.1:6379> lset mylist4 0 "four"
OK
redis 127.0.0.1:6379> lset mylist4 -2 "five"
OK
redis 127.0.0.1:6379> lrange mylist4 0 -1
1) "four"
2) "five"
3) "three"
redis 127.0.0.1:6379>
在此处我们依次插入了 one,two,three,然后将标是 0 的值设置为 four,再将下标是-2的值设
置为 five。
5.lrem
从key对应 list 中删除 count 个和 value 相同的元素。
count>0 时,按从头到尾的顺序删除,具体如下:
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist5 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist5 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist5 2 "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist5 0 -1
1) "foo"
2) "hello"
redis 127.0.0.1:6379>
count<0 时,按从尾到头的顺序删除,具体如下:
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist6 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist6 "hello"
(integer) 4
redis 127.0.0.1:6379>lrem mylist6 -2 "hello"
(integer) 2
redis 127.0.0.1:6379> lrange mylist6 0 -1
1) "hello"
2) "foo"
redis 127.0.0.1:6379>
count=0 时,删除全部,具体如下:
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 1
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 2
redis 127.0.0.1:6379> rpush mylist7 "foo"
(integer) 3
redis 127.0.0.1:6379> rpush mylist7 "hello"
(integer) 4
redis 127.0.0.1:6379> lrem mylist7 0 "hello"
(integer) 3
redis 127.0.0.1:6379> lrange mylist7 0 -1
1) "foo"
redis: java.net.SocketException: Connection reset
Caused by: java.lang.ClassCastException: [B cannot be cast to java.util.List
原因:多个线程同时调用了同一个jedis对象,导致内存数据被多个线程竞争,产生数据混乱
原因:多个线程同时调用了同一个jedis对象,导致内存数据被多个线程竞争,产生数据混乱
然后服务就当掉了。后来上网查了一下,发现好多人都遇到了同样的问题,原来是jedis操作redis的时候,对底层执行redis命令做了缓存,所以如果某一次redis操作出现异常,jedis实例中的缓存数据不会被清空,而直接放回连接池中。下一次从池中取出了同一个jedis对象,发送的命令用的还是上一个线程的数据。
所以如果两个线程使用的数据类型不一样,就会发生上面的问题。但是加入两个线程使用的数据类型是一样的,那么系统不会报异常,但是数据可能全是错乱的,后果将不可设想。
从某个角度上来看,这个问题也算是jedis的一个bug,为此我们可以自己封装一层redis操作,并在操作中捕获异常,然后用jedis.close()将redis连接关闭。
查看close()方法的底层实现,可以看到,当线程的jedis连接异常后,jedis对象会被置为invalid,并新建一个jedis对象放入连接池中
Exception in thread "Thread-2512" redis.clients.jedis.exceptions.JedisDataException: RR Protocol error: invalid multibulk length
at redis.clients.jedis.Protocol.processError(Protocol.java:127)
at redis.clients.jedis.Protocol.process(Protocol.java:161)
at redis.clients.jedis.Protocol.read(Protocol.java:215)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)
at redis.clients.jedis.BinaryJedis.ping(BinaryJedis.java:196)
at cn.passiontec.sre.data.jpa.ThreadTest$1.run(ThreadTest.java:14)
Exception in thread "Thread-2620" redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketException: Socket Closed
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:202)
at redis.clients.util.RedisInputStream.readByte(RedisInputStream.java:40)
at redis.clients.jedis.Protocol.process(Protocol.java:151)
at redis.clients.jedis.Protocol.read(Protocol.java:215)
at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:340)
at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:239)
at redis.clients.jedis.BinaryJedis.ping(BinaryJedis.java:196)
at cn.passiontec.sre.data.jpa.ThreadTest$1.run(ThreadTest.java:14)
Caused by: java.net.SocketException: Socket Closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:127)
at redis.clients.util.RedisInputStream.ensureFill(RedisInputStream.java:196)
... 7 more