什么是Redis
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库。它通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止Redis支持的键值数据类型如下:
- 字符串类型
- 散列类型
- 列表类型
- 集合类型
- 有序集合类型
Redis的应用场景
- 缓存(数据查询、短连接、新闻内容、商品内容等等)。(最多使用)
- 分布式集群架构中的session分离。
- 聊天室的在线好友列表。
- 任务队列。(秒杀、抢购、12306等等)
- 应用排行榜。
- 网站访问统计。
- 数据过期处理(可以精确到毫秒)
Redis的安装和配置
关于Redis的安装和配置可查看Linux下的服务搭建https://blog.youkuaiyun.com/ititii/article/details/80172225,在这篇文章对Redis的持久化方案和Redis的服务搭建、主从复制以及Redis的集群模拟搭建做出了简要的介绍。
Redis数据类型
- string类型
- 赋值
语法:set key
eg:127.0.0.1:6379> set s1 111
OK
- 取值
语法:get key
eg:127.0.0.1:6379> get s1
"111"
- 设置多个值
语法:mset key value [key value ...]
eg:127.0.0.1:6379> mset s2 222 s3 333
OK
- 获取多个值
语法:mget key [key ...]
eg:127.0.0.1:6379> mget s1 s2 s3
1) "111"
2) "222"
3) "333"
- 取值并赋值
取值并赋值的意思:先取出 原来的值,并对原来的值进行从新赋值。
语法:getset key value
eg:127.0.0.1:6379> getset s1 1111
"111"
- 删除
语法:del key
eg:127.0.0.1:6379> del s3
(integer) 1
- 数值增减
当存储的字符串是整数时,Redis提供了一个实用的命令INCR,其作用是让当前键值递增,并返回递增后的值。
递增:
语法:incr key
eg:127.0.0.1:6379> incr num
(integer) 1
127.0.0.1:6379> incr num
(integer) 2
127.0.0.1:6379> incr num
(integer) 3
增加指定的整数
语法:incrby key increment
eg:127.0.0.1:6379> incrby num 2
(integer) 5
127.0.0.1:6379> incrby num 2
(integer) 7
127.0.0.1:6379> incrby num 2
(integer) 9
递减:
语法:decr key
eg:127.0.0.1:6379> decr num
(integer) 8
127.0.0.1:6379> decr num
(integer) 7
127.0.0.1:63
递减指定的整数
语法:decrby key increment
eg:127.0.0.1:6379> decrby num 2
(integer) 4
127.0.0.1:6379> decrby num 2
(integer) 2
127.0.0.1:6379> decrby num 2
(integer) 0
- 向尾部追加值
append的作用是向键值的末尾追加value。如果键不存在则将该键的值设置为value,即相当于 set key value。返回值是追加后字符串的总长度。
语法:append key value
eg:127.0.0.1:6379> set s3 hell
OK
127.0.0.1:6379> append s3 o
(integer) 5
127.0.0.1:6379> get s3
"hello"
- 获取字符串长度
语法:strlen key
eg:127.0.0.1:6379> strlen s3
(integer) 5
应用:商品可以采用字符的数字递增的形式来作为商品的主键生成策略(这样就可以避免多个数据库中的商品信息主键重复)
string类型的问题:
假设一个pojo类型的对象以Json序列化的形式存储到Redis中,如果要修改pojo类型的对象的属性的值,如果采用string类型传输就会变得比较麻烦已经浪费资源,毕竟有时只需要修改其中某个属性,采用string类型就需要对其中的数据进行全部传输和修改。
- hash类型
- hash类型介绍
hash叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。
- 赋值
hset命令不区分插入和更新操作,当执行插入操作时HSET命令返回1,当执行更新操作时返回0。
语法:hset key field value
eg:127.0.0.1:6379> hset user username zhangsan
(integer) 1
127.0.0.1:6379> hset user username lisi
(integer) 0
- 插入多个值
语法:hmset key field value [field value ....]
eg:127.0.0.1:6379> hmset user age 13 telephone 13333333
OK
- 取值
语法:hget key field
eg:127.0.0.1:6379> hget user username
"lisi"
- 取多个值
语法:hmget key field [field...]
eg:127.0.0.1:6379> hmget user username age telephone
1) "lisi"
2) "13"
3) "13333333"
- 获取所有字段值
语法:hgetall key
eg:127.0.0.1:6379> hgetall user
1) "username"
2) "lisi"
3) "age"
4) "13"
5) "telephone"
6) "13333333"
- 删除字段
可以删除一个或多个字段,返回值是被删除的字段个数
语法:hdel key field [field ...]
eg:127.0.0.1:6379> hdel user age telephone
(integer) 2
- 增加数字
语法:hincrby key field increment
eg:127.0.0.1:6379> hincrby user age 2
(integer) 2
127.0.0.1:6379> hincrby user age 2
(integer) 4
- 判断字段是否存在
语法:hexists key field
eg:127.0.0.1:6379> hexists user age
(integer) 1(存在为1)
127.0.0.1:6379> hexists user ae
(integer) 0(不存在为0)
- 只获取字段名或字段值
语法:hkeys key
hvals key
eg:127.0.0.1:6379> hkeys user
1) "username"
2) "age"
127.0.0.1:6379> hvals user
1) "lisi"
2) "4"
- 获取字段数量
语法:hlen key
eg:127.0.0.1:6379> hlen user
(integer) 2
应用:存储商品信息;
- List类型
- ArrayList 和LinkedList区别:
ArrayList使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,所以比较慢。
LinkedList使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元素或后几个元素速度比较快。 - redis的list介绍
列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
列表类型内部是使用双向链表(double linked list)实现的,所以向列表两端添加元素的时间复杂度为0(1),获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。 - 向列表两端增加元素
- 向列表左边增加元素:
- 向列表右边增加元素
左边:
语法:lpush key value [value]
eg:127.0.0.1:6379> lpush list 1 2 3
(integer) 3
右边:
语法:rpush key value [value ...]
eg:127.0.0.1:6379> rpush list a b c
(integer) 6
- 查看列表
lrange命令是列表类型最常用的命令之一,获取列表中的某一片段,将返回start、stop之间的所有元素(包含两端的元素),索引从0开始。索引可以是负数,如:“-1”代表最后边的一个元素。
语法:lrange key start stop
eg:127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
4) "a"
5) "b"
6) "c"
- 从列表两端弹出元素
lpop命令从列表左边弹出一个元素,会分两步完成:
第一步是将列表左边的元素从列表中移除
第二步是返回被移除的元素值
语法:lpop key
rpop key
eg:127.0.0.1:6379> lpop list
"3"
127.0.0.1:6379> rpop list
"c"
- 获取列表中元素的个数
语法:llen key
eg:127.0.0.1:6379> llen list
(integer) 4
- 删除列表中指定的值
lrem命令会删除列表中前count个值为value的元素,返回实际删除的元素个数。根据count值的不同,该命令的执行方式会有所不同:
当count>0时, LREM会从列表左边开始删除。
当count<0时, LREM会从列表后边开始删除。
当count=0时, LREM删除所有值为value的元素。
语法: lrem key count value
eg:127.0.0.1:6379> lrem list 2 2
(integer) 2
- 获得/设置指定索引的元素值
获取指定索引的值:
语法:lindex key index
eg:127.0.0.1:6379> lindex list 3
"1"
设置指定索引的值:
语法:lset list index value
eg:127.0.0.1:6379> lset list 3 3
OK
- 只保留列表指定片段
语法:ltrim key start stop
eg:127.0.0.1:6379> ltrim list 0 5
OK
- 向列表中插入元素
该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是before还是after来决定将value插入到该元素的前面还是后面。
语法:linsert key before|after pivot value
eg:127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "2"
3) "1"
127.0.0.1:6379> linsert list after 3 4
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "4"
3) "2"
4) "1"
- 将元素从一个列表转移到另一个列表中
语法:rpoplpush source destination
eg:127.0.0.1:6379> rpoplpush list newlist
"1"
127.0.0.1:6379> lrange newlist 0 -1
1) "1"
127.0.0.1:6379> lrange list 0 -1
1) "3"
2) "4"
3) "2"
应用:商品列表的评论;
- Set类型
- redis set介绍
集合中的数据是不重复且没有顺序。集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的Redis内部是使用值为空的散列表实现,所有这些操作的时间复杂度都为0(1)。
Redis还提供了多个集合之间的交集、并集、差集的运算。 - 增加/删除元素
增加元素:
语法:sadd key number [number]
eg:127.0.0.1:6379> sadd set a b c
(integer) 3
127.0.0.1:6379> sadd set a
(integer) 0
删除元素:
语法:srem key number [number]
eg:127.0.0.1:6379> srem set c d
(integer) 1
- 获得集合中的所有元素
语法:smembers key
eg:127.0.0.1:6379> smembers set
1) "b"
2) "a”
- 判断元素是否在集合中
语法:sismember key member
eg:127.0.0.1:6379> sismember set a
(integer) 1
127.0.0.1:6379> sismember set h
(integer) 0
- 集合的差集运算A-B
语法:sdiff key key
eg:127.0.0.1:6379> sadd setA 1 2 3
(integer) 3
127.0.0.1:6379> sadd setB 2 3 4
(integer) 3
127.0.0.1:6379> sdiff setA setB
1) "1"
127.0.0.1:6379> sdiff setB setA
1) "4"
- 集合的交集运算 A ∩ B
语法:sinter key key
eg:127.0.0.1:6379> sinter setA setB
1) "2"
2) "3"
- 集合的并集运算 A ∪ B
语法:sunion key key
eg:127.0.0.1:6379> sunion setA setB
1) "1"
2) "2"
3) "3"
4) "4"
- 获得集合中元素的个数
语法:scard key
eg:127.0.0.1:6379> smembers setA
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> scard setA
(integer) 3
- 从集合中弹出一个元素
注意:由于集合是无序的,所有SPOP命令会从集合中随机选择一个元素弹出
语法:spop key
eg:127.0.0.1:6379> spop setA
"1“
- SortedSet类型zset
- 4.5.1redis sorted set介绍
在集合类型的基础上,有序集合类型为集合中的每个元素都关联一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
在某些方面有序集合和列表类型有些相似。
- 1、二者都是有序的。
- 2、二者都可以获得某一范围的元素。
但是,二者有着很大区别:
- 1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
- 2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
- 3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
- 4、有序集合要比列表类型更耗内存。
- 增加元素
向有序集合中加入一个元素和该元素的分数,如果该元素已经存在则会用新的分数替换原有的分数。返回值是新加入到集合中的元素个数,不包含之前已经存在的元素。
语法:zadd key score member [score member ...]
eg:127.0.0.1:6379> zadd scoreboard 80 zhangsan 89 lisi 94 wangwu
(integer) 3
127.0.0.1:6379> zadd scoreboard 97 lisi
(integer) 0
- 获取元素的分数
语法:zscore key member
eg:127.0.0.1:6379> zscore scoreboard lisi
"97"
- 删除元素
移除有序集key中的一个或多个成员,不存在的成员将被忽略。
当key存在但不是有序集类型时,返回一个错误。
语法:zrem key member [member...]
eg:127.0.0.1:6379> zrem scoreboard lisi
(integer) 1
- 获得排名在某个范围的元素列表
获得排名在某个范围的元素列表
按照元素分数从小到大的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
语法:zrange key start stop
eg:127.0.0.1:6379> zrange scoreboard 0 2
1) "zhangsan"
2) "wangwu"
3) "lisi“
按照元素分数从大到小的顺序返回索引从start到stop之间的所有元素(包含两端的元素)
语法:zrevrange key start stop
eg:127.0.0.1:6379> zrevrange scoreboard 0 2
1) " lisi "
2) "wangwu"
3) " zhangsan “
如果需要获得元素的分数的可以在命令尾部加上WITHSCORES参数
eg:127.0.0.1:6379> zrange scoreboard 0 1 WITHSCORES
1) "zhangsan"
2) "80"
3) "wangwu"
4) "94"
- 获得指定分数范围的元素
语法:zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
eg:127.0.0.1:6379> ZRANGEBYSCORE scoreboard 90 97 WITHSCORES
1) "wangwu"
2) "94"
3) "lisi"
4) "97"
127.0.0.1:6379> ZRANGEBYSCORE scoreboard 70 100 limit 1 2
1) "wangwu"
2) "lisi"
- 增加某个元素的分数
语法:zincrby key increment member
eg:127.0.0.1:6379> ZINCRBY scoreboard 4 lisi
"101“
- 获得集合中元素的数量
语法:zcard key
eg:127.0.0.1:6379> ZCARD scoreboard
(integer) 3
- 获得指定分数范围内的元素个数
语法 :zcount key min max
eg:127.0.0.1:6379> ZCOUNT scoreboard 80 90
(integer) 1
- 按照排名范围删除元素
语法: zremrangebyrank key start stop
eg:
127.0.0.1:6379> ZREMRANGEBYRANK scoreboard 0 1
(integer) 2
127.0.0.1:6379> ZRANGE scoreboard 0 -1
1) "lisi"
- 按照分数范围删除元素
语法:zremrangebyscore key min max
eg:127.0.0.1:6379> zadd scoreboard 84 zhangsan
(integer) 1
127.0.0.1:6379> ZREMRANGEBYSCORE scoreboard 80 100
(integer) 1
- 获取元素的排名
从小到大:
语法:zrank key member
eg:127.0.0.1:6379> ZRANK scoreboard lisi
(integer) 0
从大到小:
语法:zrevrank key member
eg:127.0.0.1:6379> ZREVRANK scoreboard zhangsan
(integer) 1
应用:商品销售排名;
- keys命令
- 设置key的生存时间
Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁。
EXPIRE key seconds 设置key的生存时间(单位:秒)key在多少秒后会自动删除
TTL key 查看key生于的生存时间
PERSIST key 清除生存时间
PEXPIRE key milliseconds 生存时间设置单位为:毫秒
eg:
192.168.101.3:7002> set test 1 设置test的值为1
OK
192.168.101.3:7002> get test 获取test的值
"1"
192.168.101.3:7002> EXPIRE test 5 设置test的生存时间为5秒
(integer) 1
192.168.101.3:7002> TTL test 查看test的生于生成时间还有1秒删除
(integer) 1
192.168.101.3:7002> TTL test
(integer) -2
192.168.101.3:7002> get test 获取test的值,已经删除
(nil)
Jedis客户端
- java进行单实例连接:
public class JedisClient {
@Test
public void testJedis() {
// 创建一个Jedis的连接
Jedis jedis = new Jedis("192.168.211.128", 6379);
// 执行redis命令
jedis.hset("user1", "age", "18");
// 从redis中取值
String result = jedis.hget("user1", "age");
// 打印结果
System.out.println(result);
// 关闭连接
jedis.close();
}
}
- Java进行连接池连接:
@Test
public void testJedisPool() {
//创建一连接池对象
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//从连接池中获得连接
Jedis jedis = jedisPool.getResource();
String result = jedis.get("mytest");
System.out.println(result);
//关闭连接
jedis.close();
//关闭连接池
jedisPool.close();
}
- spring整合jedis
spring配置文件application.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" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="false" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis单机 通过连接池 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool"
destroy-method="close">
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<constructor-arg name="host" value="192.168.242.130" />
<constructor-arg name="port" value="6379" />
</bean>
</beans>
测试代码:
@Test
public void testJedisPool() {
JedisPool pool = (JedisPool) applicationContext.getBean("jedisPool");
Jedis jedis = null;
try {
jedis = pool.getResource();
jedis.set("name", "lisi");
String name = jedis.get("name");
System.out.println(name);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (jedis != null) {
// 关闭连接
jedis.close();
}
}
}
- JedisCluster连接Redis集群
@Test
public void testJedisCluster() throws Exception {
//创建一连接,JedisCluster对象,在系统中是单例存在
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.211.128", 7001));
nodes.add(new HostAndPort("192.168.211.128", 7002));
nodes.add(new HostAndPort("192.168.211.128", 7003));
nodes.add(new HostAndPort("192.168.211.128", 7004));
nodes.add(new HostAndPort("192.168.211.128", 7005));
nodes.add(new HostAndPort("192.168.211.128", 7006));
JedisCluster cluster = new JedisCluster(nodes);
//执行JedisCluster对象中的方法,方法和redis一一对应。
cluster.set("cluster-test", "my jedis cluster test");
String result = cluster.get("cluster-test");
System.out.println(result);
//程序结束时需要关闭JedisCluster对象
cluster.close();
}
- spring 整合JedisCluster
<!-- 连接池配置 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大连接数 -->
<property name="maxTotal" value="30" />
<!-- 最大空闲连接数 -->
<property name="maxIdle" value="10" />
<!-- 每次释放连接的最大数目 -->
<property name="numTestsPerEvictionRun" value="1024" />
<!-- 释放连接的扫描间隔(毫秒) -->
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<!-- 连接最小空闲时间 -->
<property name="minEvictableIdleTimeMillis" value="1800000" />
<!-- 连接空闲多久后释放, 当空闲时间>该值 且 空闲连接>最大空闲连接数 时直接释放 -->
<property name="softMinEvictableIdleTimeMillis" value="10000" />
<!-- 获取连接时的最大等待毫秒数,小于零:阻塞不确定的时间,默认-1 -->
<property name="maxWaitMillis" value="1500" />
<!-- 在获取连接的时候检查有效性, 默认false -->
<property name="testOnBorrow" value="true" />
<!-- 在空闲时检查有效性, 默认false -->
<property name="testWhileIdle" value="true" />
<!-- 连接耗尽时是否阻塞, false报异常,ture阻塞直到超时, 默认true -->
<property name="blockWhenExhausted" value="false" />
</bean>
<!-- redis集群 -->
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg index="0">
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.211.128"></constructor-arg>
<constructor-arg index="1" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.211.128"></constructor-arg>
<constructor-arg index="1" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.211.128"></constructor-arg>
<constructor-arg index="1" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.211.128"></constructor-arg>
<constructor-arg index="1" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.211.128"></constructor-arg>
<constructor-arg index="1" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.211.128"></constructor-arg>
<constructor-arg index="1" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
</bean>