文章目录
源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all
一、说明
1.1 Redis 客户端说明
关于spring 整合 mybatis 本用例提供两种整合方法:
- jedis: 官方推荐的java客户端,能够胜任redis的大多数基本使用;
- redisson:也是官方推荐的客户端,比起jedis提供了更多高级的功能,比如分布式锁、集合数据切片等功能。同时提供了丰富而全面的中英文版本的wiki。
注:关于redis其他语言官方推荐的客户端可以在客户端该网页查看,其中官方推荐的用了黄色星星⭐️标注。
1.2 Redis可视化软件
推荐Redis Desktop Manager 作为可视化查看工具,可以直观看到用例中测试关于存储实体对象序列化的情况。
1.3 项目结构说明
- jedis和redisson的配置和单元测试分别位于resources和test下对应的包中,其中集群的配置文件以cluster结尾。所有配置按照需要在springApplication.xml用import导入。
- 实体类Programmer.java用于测试Redisson序列化与反序列化
springapplication.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--jedis 连接单机redis-->
<import resource="classpath:jedis/jedis.xml"/>
<!--jedis 连接集群redis-->
<!--<import resource="classpath:jedis/jedisCluster.xml"/>-->
<!--redisson 连接单机redis-->
<import resource="classpath:redisson/redisson.xml"/>
<!--redisson 连接集群redis-->
<!--<import resource="classpath:redisson/redissonCluster.xml"/>-->
</beans>
1.3 依赖说明
除了spring的基本依赖外,需要导入jedis 和 redisson 对应的客户端依赖包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.9.1</version>
</dependency>
<!--redisson 中部分功能依赖了netty -->
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.32.Final</version>
</dependency>
二、spring 整合 jedis
2.1 新建基本配置文件
redis.host=127.0.0.1
redis.port=6379
# 连接超时时间
redis.timeout=2000
# 最大空闲连接数
redis.maxIdle=8
# 最大连接数
redis.maxTotal=16
2.2 单机配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--指定配置文件的位置-->
<context:property-placeholder location="classpath:jedis/jedis.properties"/>
<!--初始化连接池配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxTotal" value="${redis.maxTotal}"/>
</bean>
<!--配置jedis连接池-->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port}"/>
<constructor-arg name="timeout" value="${redis.timeout}"/>
</bean>
<!--把 jedis 创建与销毁交给spring 来管理-->
<bean id="jedis" factory-bean="jedisPool" factory-method="getResource" destroy-method="close"/>
</beans>
2.3 集群配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd">
<!--指定配置文件的位置-->
<context:property-placeholder location="classpath:jedis/jedis.properties"/>
<!--初始化连接池配置-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}"/>
<property name="maxTotal" value="${redis.maxTotal}"/>
</bean>
<!--配置jedis连接池(集群)-->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg name="nodes">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6379"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6380"/>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="127.0.0.1"/>
<constructor-arg name="port" value="6381"/>
</bean>
</set>
</constructor-arg>
<constructor-arg name="timeout" value="${redis.timeout}"/>
</bean>
</beans>
2.4 单机版本测试用例
1.需要注意的是,对于jedis而言,单机版本和集群版本注入的实例是不同的;
2.jedis本身并不支持序列化于反序列化操作,如果需要存储实体类,需要序列化后存入。(redisson本身就支持序列化于反序列化,详见下文)
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class JedisSamples {
@Autowired
private Jedis jedis;
@Test
public void Set() {
jedis.set("hello", "spring");
}
@Test
public void Get() {
String s = jedis.get("hello");
System.out.println(s);
}
@Test
public void setEx() {
String s = jedis.setex("spring", 10, "我会在10秒后过期");
System.out.println(s);
}
}
2.5 集群版本测试用例
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class JedisClusterSamples {
@Autowired
private JedisCluster jedisCluster;
@Test
public void Set() {
jedisCluster.set("hello", "spring");
}
@Test
public void Get() {
String s = jedisCluster.get("hello");
System.out.println(s);
}
@Test
public void setEx() {
String s = jedisCluster.setex("spring", 10, "我会在10秒后过期");
System.out.println(s);
}
}
三、spring 整合 redisson
2.1 单机配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:redisson="http://redisson.org/schema/redisson"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://redisson.org/schema/redisson
http://redisson.org/schema/redisson/redisson.xsd">
<redisson:client>
<!--更多可配置项见官方文档 2.6.2. 通过JSON、YAML和Spring XML文件配置单节点模式
<a href="https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#26-%E5%8D%95redis%E8%8A%82%E7%82%B9%E6%A8%A1%E5%BC%8F"> -->
<redisson:single-server
address="redis://127.0.0.1:6379"
idle-connection-timeout="10000"
ping-timeout="1000"
connect-timeout="10000"
timeout="3000"
retry-attempts="3"
retry-interval="1500"
connection-minimum-idle-size="10"
connection-pool-size="64"
database="0"
/>
</redisson:client>
</beans>
2.2 集群配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:redisson="http://redisson.org/schema/redisson"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://redisson.org/schema/redisson http://redisson.org/schema/redisson/redisson.xsd">
<!-- 最基本配置 -->
<redisson:client>
<!--集群更多配置参数见官方文档 2.4.2 通过JSON、YAML和Spring XML文件配置集群模式
<a href="https://github.com/redisson/redisson/wiki/2.-%E9%85%8D%E7%BD%AE%E6%96%B9%E6%B3%95#24-%E9%9B%86%E7%BE%A4%E6%A8%A1%E5%BC%8F"> -->
<redisson:cluster-servers>
<redisson:node-address value="redis://127.0.0.1:6379"/>
<redisson:node-address value="redis://127.0.0.1:6380"/>
<redisson:node-address value="redis://127.0.0.1:6381"/>
</redisson:cluster-servers>
</redisson:client>
</beans>
2.3 存储基本类型测试用例
- 这里需要注意的是,对于Redisson而言, 单机和集群最后在使用的时候注入的都是RedissonClient,这和jedis是不同的。
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class RedissonSamples {
@Autowired
private RedissonClient redissonClient;
@Test
public void Set() {
// key 存在则更新 不存在则删除
RBucket<String> rBucket = redissonClient.getBucket("redisson");
rBucket.set("firstValue");
}
@Test
public void Get() {
// key 存在则更新 不存在则删除
RBucket<String> rBucket = redissonClient.getBucket("redisson");
System.out.println(rBucket.get());
}
@Test
public void SetEx() {
// key 存在则更新 不存在则删除
RBucket<String> rBucket = redissonClient.getBucket("redissonEx");
rBucket.set("我在十秒后会消失", 10, TimeUnit.SECONDS);
}
@After
public void close() {
redissonClient.shutdown();
}
}
2.4 存储实体对象测试用例
@RunWith(SpringRunner.class)
@ContextConfiguration({"classpath:springApplication.xml"})
public class RedissonObjectSamples {
@Autowired
private RedissonClient redissonClient;
// Redisson的对象编码类是用于将对象进行序列化和反序列化 默认采用Jackson
@Test
public void Set() {
RBucket<Programmer> rBucket = redissonClient.getBucket("programmer");
rBucket.set(new Programmer("xiaoming", 12, 5000.21f, new Date()));
//存储结果: {"@class":"com.heibaiying.bean.Programmer","age":12,"birthday":["java.util.Date",1545714986590],"name":"xiaoming","salary":5000.21}
}
@Test
public void Get() {
RBucket<Programmer> rBucket = redissonClient.getBucket("programmer");
System.out.println(rBucket.get());
}
@After
public void close() {
redissonClient.shutdown();
}
}
附:Redis的数据结构和操作命令
1.1 预备
1.1.1 全局命令
查看所有键: keys *
查看键总数:dbsize
检查键是否存在:exists key
删除键:del key [key …] 支持删除多个键
-
键过期:expire key seconds
ttl命令会返回键的剩余过期时间, 它有3种返回值:
- 大于等于0的整数: 键剩余的过期时间。
- -1: 键没设置过期时间。
- -2: 键不存在
键的数据结构 type key
1.1.2 数据结构和内部编码
type命令实际返回的就是当前键的数据结构类型, 它们分别是:string(字符串) 、 hash(哈希) 、 list(列表) 、 set(集合) 、 zset(有序集合)
1.1.3 单线程架构
- 纯内存访问, Redis将所有数据放在内存中, 内存的响应时长大约为100纳秒, 这是Redis达到每秒万级别访问的重要基础。
- 非阻塞I/O, Redis使用epoll作为I/O多路复用技术的实现, 再加上Redis自身的事件处理模型将epoll中的连接、 读写、 关闭都转换为事件, 不在网络I/O上浪费过多的时间, 如图2-6所示。
- 单线程避免了线程切换和竞态产生的消耗。
1.2 字符串
作用 | 格式 | 参数或示例 |
---|---|---|
设置值 | set key value [ex seconds][px milliseconds][nx|xx] setnx setex | ex seconds: 为键设置秒级过期时间。 px milliseconds: 为键设置毫秒级过期时间。 nx: 键必须不存在, 才可以设置成功, 用于添加。 xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。 |
获取值 | get key | r如果获取的键不存在 ,则返回nil(空) |
批量设置 | mset key value [key value …] | mset a 1 b 2 c 3 d 4 |
批量获取值 | mget key [key …] | mget a b c d |
计数 | incr key decr key incrby key increment(指定数值自增) decrby key decrement(指定数值自减) incrbyfloat key increment (浮点数自增) |
值不是整数, 返回错误。 值是整数, 返回自增或自减后的结果。 键不存在,创建键,并按照值为0自增或自减, 返回结果为1。 |
追加值 | append key value | 向字符串的默认追加值 |
字符串长度 | strlen key | 获取字符串长度,中文占用三个字节 |
设置并返回原值 | getset key value | |
设置指定位置的租字符串 | setrange key offeset value | |
获取部分字符串 | getrange key start end |
1.3 哈希
作用 | 格式 | 参数或示例 |
---|---|---|
设置值 | hset key field value | hset user:1 name tom hset user:1 age 12 |
获取值 | hget key field | hget user:1 name |
删除field | hdel key field [field …] | |
计算field个数 | hlen key | |
批量设置或获取field-value | hmget key field [field] hmset key field value [field value…] |
hmset user:1 name mike age 12 city tianjin hmget user:1 name city |
判断field是否存在 | hexists key field | |
获取所有field | hkeys key | |
获取所有value | hvals key | |
获取所有的filed-value | hgetall key | 如果哈希元素个数比较多, 会存在阻塞Redis的可能。 获取全部 可以使用hscan命令, 该命令会渐进式遍历哈希类型 |
计数 | hincrby key field hincrbyfloat key field |
1.4 列表
作用 | 格式 | 参数或示例 |
---|---|---|
增 | 左侧插入:lpush key value [value …] 右侧插入:rpush key value [value …] 某个指定元素前后插入:linsert key before|after pivot value | |
查 | 获取指定范围内的元素列表:lrange key start end 获取列表指定索引下标的元素:lindex key index 获取列表指定长度:llen key | lrange listkey 0 -1 |
删 | 从列表左侧弹出元素:lpop key 从列表右侧弹出元素:rpop key 删除指定元素:lrem key count value 截取列表:ltrim key start end | count>0, 从左到右, 删除最多count个元素。 count<0, 从右到左, 删除最多count绝对值个元素。 count=0, 删除所有 |
改 | 修改指定索引下标的元素:lset key index newValue | |
阻塞操作 | blpop key [key …] timeout brpop key [key …] timeout | key[key…]: 多个列表的键。 timeout: 阻塞时间|等待时间(单位: 秒) |
1.5 集合
集合(set) 类型也是用来保存多个的字符串元素, 但和列表类型不一样的是, 集合中不允许有重复元素, 并且集合中的元素是无序的, 不能通过索引下标获取元素。
集合内操作:
作用 | 格式 | 参数或示例 |
---|---|---|
添加元素 | sadd key element [element …] | 返回结果为添加成功的元素个数 |
删除元素 | srem key element [element …] | 返回结果为成功删除的元素个数 |
计算元素个数 | scard key | |
判断元素是否在集合中 | sismember key element | |
随机返回 | srandmember key [count] | 随机从集合返回指定个数元素,count 默认为1 |
从集合随机弹出元素 | spop key | srandmember 不会从集合中删除元素,spop 会 |
获取集合中所有元素 | smembers key | 可用sscan 代替 |
集合间操作:
作用 | 格式 |
---|---|
求多个集合的交集 | sinter key [key …] |
求多个集合的并集 | suinon key [key …] |
求多个集合的差集 | sdiff key [key …] |
将交集、并集、差集的结果保存 | sinterstore destination key [key …] suionstore destination key [key …] sdiffstore destination key [key …] |
1.6 有序集合
有序集合中的元素可以排序。 但是它和列表使用索引下标作为排序依据不同的是, 它给每个元素设置一个分数(score) 作为排序的依据。
集合内操作:
作用 | 格式 | 参数或示例 |
---|---|---|
添加成员 | zadd key score member [score member …] | nx: member必须不存在, 才可设置成功, 用于添加。 xx: member必须存在, 才可以设置成功, 用于更新。 ch: 返回此次操作后, 有序集合元素和分数发生变化的个数 incr: 对score做增加, 相当于后面介绍的zincrby。 |
计算成员个数 | zcard key | |
计算某个成员的分数 | zscore key member | |
计算某个成员的排名 | zrank key member zrevrank key member | zrank是从分数从低到高返回排名, zrevrank反之。 |
删除成员 | zrem key member [member …] | |
增加成员分数 | zincrby key increment member | zincrby user:ranking 9 tom |
返回指定排名范围的成员 | zrange key start end [withscores] zrange key start end [withscores] | zrange是从低到高返回, zrevrange反之。 |
返回指定分数范围内的成员 | zrangebyscore key min max [withscores][limit offset count] zrevrangebyscore key max min [withscores][limit offset count] | 其中zrangebyscore按照分数从低到高返回, zrevrangebyscore反之。 [limit offset count]选项可以限制输出的起始位置和个数: 同时min和max还支持开区间(小括号) 和闭区间(中括号) , -inf和+inf分别代表无限小和无限大 |
删除指定排名内的升序元素 | zremrangerank key start end | |
删除指定分数范围的成员 | zremrangebyscore key min max |
集合间操作:
作用 | 格式 |
---|---|
交集 | zinterstore destination numkeys key [key …] [weights weight [weight …]] [aggregate sum|min|max] |
并集 | zunionstore destination numkeys key [key …] [weights weight [weight …]] [aggregate sum|min|max] |
- destination: 交集计算结果保存到这个键。
- numkeys: 需要做交集计算键的个数。
- key[key…]: 需要做交集计算的键。
- weights weight[weight…]: 每个键的权重, 在做交集计算时, 每个键中的每个member会将自己分数乘以这个权重, 每个键的权重默认是1。
- aggregate sum|min|max: 计算成员交集后, 分值可以按照sum(和) 、min(最小值) 、 max(最大值) 做汇总, 默认值是sum。
1.7 键管理
1.7.1 单个键管理
1.键重命名
rename key newkey
为了防止被强行rename, Redis提供了renamenx命令, 确保只有newKey不存在时候才被覆盖。
2. 随机返回键
random key
3.键过期
- expire key seconds: 键在seconds秒后过期。
- expireat key timestamp: 键在秒级时间戳timestamp后过期。
- pexpire key milliseconds: 键在milliseconds毫秒后过期。
- pexpireat key milliseconds-timestamp键在毫秒级时间戳timestamp后过期
注意:
- 如果expire key的键不存在, 返回结果为0
- 如果设置过期时间为负值, 键会立即被删除, 犹如使用del命令一样
- persist key t命令可以将键的过期时间清除
- 对于字符串类型键, 执行set命令会去掉过期时间, 这个问题很容易在开发中被忽视
- Redis不支持二级数据结构(例如哈希、 列表) 内部元素的过期功能, 例如不能对列表类型的一个元素做过期时间设置
- setex命令作为set+expire的组合, 不但是原子执行, 同时减少了一次网络通讯的时间
1.7.2 键遍历
1. 全量键遍历
keys pattern
2. 渐进式遍历
scan cursor [match pattern] [count number]
- cursor是必需参数, 实际上cursor是一个游标, 第一次遍历从0开始, 每次scan遍历完都会返回当前游标的值, 直到游标值为0, 表示遍历结束。
- match pattern是可选参数, 它的作用的是做模式的匹配, 这点和keys的模式匹配很像。
- count number是可选参数, 它的作用是表明每次要遍历的键个数, 默认值是10, 此参数可以适当增大。
1.7.3 数据库管理
1.切换数据库
select dbIndex
2.flushdb/flushall
flushdb/flushall命令用于清除数据库, 两者的区别的是flushdb只清除当前数据库, flushall会清除所有数据库。
附:源码Gitub地址:https://github.com/heibaiying/spring-samples-for-all