Redis 介绍
什么是Redis
- Redis 是用C语言开发的一个开源的高性能键值对( key-value )内存数据库,它是一种 NoSQL 数据库。
- 它是【单进程单线程】的内存数据库,所以说不存在线程安全问题。
- 它可以支持并发 10W QPS,所以说性能非常优秀。之所以单进程单线程性能还这么好,是因为底层采用了【IO多路复用(NIO思想)】
- 相比Memcache这种专业缓存技术,它有更优秀的读写性能,及丰富的数据类型。
- 它提供了五种数据类型来存储【值】:字符串类型(string)、散列类型(hash)、列表类型(list)、集合类型(set)、有序集合类型(sortedset、zset)
Redis官网
- 官网地址:http://redis.io/
- 中文官网地址:http://www.redis.cn/
- 下载地址:http://download.redis.io/releases/
什么是NoSQL?
- NoSQL ,即 Not-Only SQL (不仅仅是 SQL ),泛指非关系型的数据库。
- 什么是关系型数据库?数据结构是一种有行有列的数据库
- NoSQL 数据库是为了解决高并发、高可用、高可扩展、大数据存储问题而产生的数据库解决方案。
- NoSQL 可以作为关系型数据库的良好补充,但是不能替代关系型数据库。
Redis 应用场景
- 内存数据库(登录信息、购物车信息、用户浏览记录等)
- 缓存服务器(商品数据、广告数据等等)(最多使用)
- 解决分布式集群架构中的 session 分离问题( session 共享)
- 任务队列(秒杀、抢购、12306等等)
- 分布式锁的实现
- 支持发布订阅的消息模式
- 应用排行榜(有序集合)
- 网站访问统计
- 数据过期处理(可以精确到毫秒)
Redis 安装及配置
Redis下载
官网地址:http://redis.io/
- 中文官网地址:http://www.redis.cn/
- 下载地址:http://download.redis.io/releases/
Redis安装环境
- Redis 没有官方的 Windows 版本,所以建议在 Linux 系统上安装运行,我们使用
CentOS 7 ( Linux 操作系统的一个系列)作为安装环境。
Redis 安装
第一步:安装 C 语言需要的 GCC 环境
yum install -y gcc-c++
yum install -y wget
第二步:下载并解压缩 Redis 源码压缩包
wget http://download.redis.io/releases/redis-5.0.4.tar.gz
tar -zxf redis-5.0.4.tar.gz
第三步:编译 Redis 源码,进入 redis-3.2.9 目录,执行编译命令
cd redis-5.0.4
make
Redis启动
前端启动
- 启动命令: redis-server ,直接运行 bin/redis-server 将以前端模式启动
./redis-server
- 启动缺点:客户端窗口关闭则 redis-server 程序结束,不推荐使用此方法
- 启动图例:
后端启动(守护进程启动)
- 第一步:修改 redis.conf
vim redis.conf
# 将`daemonize`由`no`改为`yes`
daemonize yes
# 默认绑定的是回环地址,默认不能被其他机器访问
# bind 127.0.0.1
# 是否开启保护模式,由yes该为no
protected-mode no
- 第二步:启动服务
./redis-server redis.conf
后端启动的关闭方式
./redis-cli shutdown
其他命令说明
- redis-server :启动 redis 服务
- redis-cli :进入 redis 命令客户端
- redis-benchmark : 性能测试的工具
- redis-check-aof : aof 文件进行检查的工具
- redis-check-dump : rdb 文件进行检查的工具
- redis-sentinel : 启动哨兵监控服务
Redis客户端
Redis命令行客户端
- 命令格式
./redis-cli -h 127.0.0.1 -p 6379
- 参数说明
-h:redis服务器的ip地址
-p:redis实例的端口号
- 默认方式
如果不指定主机和端口也可以
默认主机地址是127.0.0.1
默认端口是6379
./redis-cli
Java客户端Jedis
Jedis介绍
- Redis不仅使用命令来操作,而且可以使用程序客户端操作。现在基本上主流的语言都有客户端支持,比如java、C、C#、C++、php、Node.js、Go等。
- 在官方网站里列一些Java的客户端,有Jedis、Redisson、Jredis、JDBC-Redis、等其中官方推荐使用Jedis和Redisson。 在企业中用的最多的就是Jedis,下面我们就重点学习下Jedis。
- Jedis同样也是托管在github上,地址:https://github.com/xetorthio/jedis
添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
单实例连接
@Test
public void testJedis() {
//创建一个Jedis的连接
Jedis jedis = new Jedis("127.0.0.1", 6379);
//执行redis命令
jedis.set("mytest", "hello world, this is jedis client!");
//从redis中取值
String result = jedis.get("mytest");
//打印结果
System.out.println(result);
//关闭连接
jedis.close();
}
连接池连接
@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();
}
连接redis集群
@Test public void testJedisCluster() throws Exception {
//创建一连接,JedisCluster对象,在系统中是单例存在
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("192.168.242.129", 7001));
nodes.add(new HostAndPort("192.168.242.129", 7002));
nodes.add(new HostAndPort("192.168.242.129", 7003));
nodes.add(new HostAndPort("192.168.242.129", 7004));
nodes.add(new HostAndPort("192.168.242.129", 7005));
nodes.add(new HostAndPort("192.168.242.129", 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();
}
Jedis整合spring
配置spring配置文件applicationContext.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">
<!-- 连接池配置 -->
<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="jedisPool" class="redis.clients.jedis.JedisPool" destroy-method="close">
<constructor-arg name="poolConfig" ref="jedisPoolConfig" />
<constructor-arg name="host" value="192.168.10.135" />
<constructor-arg name="port" value="6379" />
</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.10.135"></constructor- arg>
<constructor-arg index="1" value="7001">
</constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructor- arg>
<constructor-arg index="1" value="7002"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructor- arg>
<constructor-arg index="1" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructor- arg>
<constructor-arg index="1" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructor- arg>
<constructor-arg index="1" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg index="0" value="192.168.10.135"></constructor- arg>
<constructor-arg index="1" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
<constructor-arg index="1" ref="jedisPoolConfig"></constructor-arg>
</bean>
</beans>
Jedis整合springBoot
- 在pom.xml文件中添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.2</version>
</dependency>
- 在springboot的配置文件中加入redis的配置信息
#redis jedis配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
#spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
#spring-session 使用
spring.session.store-type=none
- 创建jedis配置文件,配置文件的作用是在项目启动的时候将jedis注入,接着我们就可以在其他类中获取到JedisPool类的信息
@Configuration
public class JedisConfig extends CachingConfigurerSupport{
private Logger logger = LoggerFactory.getLogger(JedisConfig.class);
/**
* SpringSession 需要注意的就是redis需要2.8以上版本,然后开启事件通知,在redis配置文件里面加上
* notify-keyspace-events Ex
* Keyspace notifications功能默认是关闭的(默认地,Keyspace 时间通知功能是禁用的,因为它或多或少会使用一些CPU的资源)。
* 或是使用如下命令:
* redis-cli config set notify-keyspace-events Egx
* 如果你的Redis不是你自己维护的,比如你是使用阿里云的Redis数据库,你不能够更改它的配置,那么可以使用如下方法:在applicationContext.xml中配置
* <util:constant static-field="org.springframework.session.data.redis.config.ConfigureRedisAction.NO_OP"/>
* @return
*/
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.pool.max-active}")
private int maxActive;
@Value("${spring.redis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.pool.max-wait}")
private long maxWaitMillis;
@Bean
public JedisPool redisPoolFactory(){
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
jedisPoolConfig.setMaxTotal(maxActive);
jedisPoolConfig.setMinIdle(minIdle);
JedisPool jedisPool = new JedisPool(jedisPoolConfig,host,port,timeout,null);
logger.info("JedisPool注入成功!");
logger.info("redis地址:" + host + ":" + port);
return jedisPool;
}
}
此时通过注解即可获取jedisPool 对象,进行Redis操作
@Aotowired
private JedisPool jedisPool;
Spring Data Redis
- Spring-data-redis是spring大家族的一部分,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。
spring-data-redis针对jedis提供了如下功能:
- 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
- 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
ValueOperations:简单K-V操作
SetOperations:set类型数据操作
ZSetOperations:zset类型数据操作
HashOperations:针对map类型的数据操作
ListOperations:针对list类型的数据操作 - 提供了对key的“bound”(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key,即BoundKeyOperations:
BoundValueOperations
BoundSetOperations
BoundListOperations
BoundSetOperations
BoundHashOperations - 将事务操作封装,有容器控制。
- 针对数据的“序列化/反序列化”,提供了多种可选择策略(RedisSerializer)
RedisTemplate中API使用
pom.xml依赖
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件或者@value读取配置
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1ms
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000ms
RedisTemplate实例化
自动注入 或者@Bean 重写序列化
- @Bean重写
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate setRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
- 使用
@Autowired
RedisTemplate redisTemplate;
Redis数据类型
Redis 中存储数据是通过 key-value 格式存储数据的,其中 value 可以定义五种数据类型:
- String(字符类型)
- Hash(散列类型)
- List(列表类型)
- Set(集合类型)
- SortedSet(有序集合类型,简称zset)
注意:在 redis 中的命令语句中,命令是忽略大小写的,而 key 是不忽略大小写的
string 类型
命令
赋值
- 语法:
SET key value
- 示例:
127.0.0.1:6379> set test 123
OK
取值
- 语法:
GET key
- 示例:
127.0.0.1:6379> get test
"123“
取值并赋值
- 语法:
GETSET key value
- 示例:
127.0.0.1:6379> getset s2 222
"111"
127.0.0.1:6379> get s2
"222"
数值增减
注意事项
- 当value为整数数据时,才能使用以下命令操作数值的增减。
- 数值递增都是【原子】操作。
- redis中的每一个单独的命令都是原子性操作。当多个命令一起执行的时候,就不能保证原子性,不过我们可以使 用事务和lua脚本来保证这一点。
递增数字
- 语法(increment)
INCR key
- 示例:
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
- 示例:
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
减少指定的整数
- 语法:
DECRBY key decrement
仅当不存在时赋值
使用该命令可以实现【分布式锁】的功能
- 语法:
setnx key value
- 示例:
redis> EXISTS job # job 不存在
(integer) 0
redis> SETNX job "programmer" # job 设置成功
(integer) 1
redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败
(integer) 0
redis> GET job # 没有被覆盖
"programmer"
向尾部追加值
APPEND 命令,向键值的末尾追加 value 。
如果键不存在则将该键的值设置为 value ,即相当于 SET key value 。返回值是追加后字符串的总长度。
- 语法:
APPEND key value
- 示例:
127.0.0.1:6379> set str hello
OK
127.0.0.1:6379> append str " world!"
(integer) 12
127.0.0.1:6379> get str
"hello world!"
获取字符串长度
STRLEN 命令,返回键值的长度,如果键不存在则返回0。
- 语法:
STRLEN key
同时设置/获取多个键值
- 语法:
MSET key value [key value …]
MGET key [key …]
应用场景之自增主键
- 需求:商品编号、订单号采用 INCR 命令生成。
- 设计: key 命名要有一定的设计
- 实现:定义商品编号 key : items:id
hash 类型
hash类型介绍
hash 类型也叫散列类型,它提供了字段和字段值的映射。字段值只能是字符串类型,不支持散列类型、集合类型等其它类型。如下:
命令
赋值
HSET 命令不区分插入和更新操作,当执行插入操作时 HSET 命令返回 1 ,当执行更新操作时返回 0 。
设置一个字段值
- 语法:
HSET key field value
- 示例:
127.0.0.1:6379> hset user username zhangsan
(integer) 1
设置多个字段值
- 语法:
HMSET key field value [field value ...]
当字段不存在时赋值
类似 HSET ,区别在于如果字段存在,该命令不执行任何操作
- 语法:
HSETNX key field value
- 示例:
127.0.0.1:6379> hsetnx user age 30 # 如果user中没有age字段则设置age值为30,否则不做任何操作
(integer) 0
取值
获取一个字段值
- 语法:
HGET key field
- 示例:
127.0.0.1:6379> hget user username
"zhangsan“
获取多个字段值
- 语法:
HMGET key field [field ...]
获取所有字段值
- 语法:
HGETALL key
- 示例:
127.0.0.1:6379> hgetall user
1) "age"
2) "20"
3) "username"
4) "lisi"
删除字段
可以删除一个或多个字段,返回值是被删除的字段个数
- 语法:
HDEL key field [field ...]
增加数字
- 语法:
HINCRBY key field increment
判断字段是否存在
- 语法:
HEXISTS key field
只获取字段名或字段值
- 语法:
HKEYS key
HVALS key
- 示例:
127.0.0.1:6379> hmset user age 20 name lisi
OK
127.0.0.1:6379> hkeys user
1) "age"
2) "name"
127.0.0.1:6379> hvals user
1) "20"
2) "lisi"
获取字段数量
- 语法:
HLEN key
string类型和hash类型的区别
hash类型适合存储那些对象数据,特别是对象属性经常发生【增删改】操作的数据。 string类型也可以存储对象数据,将java对象转成json字符串进行存储,这种存储适合【查询】操作。
list 类型
ArrayList与LinkedList的区别
- ArrayList 使用数组方式存储数据,所以根据索引查询数据速度快,而新增或者删除元素时需要设计到位移操作,
所以比较慢。 - LinkedList 使用双向链表方式存储数据,每个元素都记录前后元素的指针,所以插入、删除数据时只是更改前后元素的指针指向即可,速度非常快。然后通过下标查询元素时需要从头开始索引,所以比较慢,但是如果查询前几个元
素或后几个元素速度比较快。
list类型介绍
- Redis 的列表类型( list 类型)可以 存储一个有序的字符串列表 ,常用的操作是向列表两端添加元素,或者获得列表的某一个片段。
- 列表类型内部是使用双向链表( double linked list )实现的,所以向列表两端添加元素的时间复杂度为0(1) ,获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是极快的。
命令
LPUSH/RPUSH
- 语法:
LPUSH key value [value ...]
RPUSH key value [value ...]
LRANGE
获取列表中的某一片段。将返回start
、stop
之间的所有元素(包含两端的元素),索引从0
开始。索引可以 是负数,如:“-1
”代表最后边的一个元素。
- 语法:
LRANGE key start stop
LPOP/RPOP
从列表左边弹出一个元素,会分两步完成:
-
第一步是将列表左边的元素从列表中移除
-
第二步是返回被移除的元素值。
-
语法:
LPOP key
RPOP key
LLEN
获取列表中元素的个数
LREM
-
删除列表中指定个数的值
-
LREM 命令会删除列表中前 count 个值为 value 的元素,返回实际删除的元素个数。根据 count 值的不同,该命令的执行方式会有所不同:
- 当count>0时, LREM会从列表左边开始删除。
- 当count<0时, LREM会从列表后边开始删除。
- 当count=0时, LREM删除所有值为value的元素。
- 语法:
LREM key count value
LINDEX
- 获得指定索引的元素值
- 语法:
LINDEX key index
设置指定索引的元素值
- 语法
LSET key index value
LTRIM
-
只保留列表指定片段,指定范围和LRANGE一致
-
语法:
LTRIM key start stop
LINSERT
- 向列表中插入元素。
- 该命令首先会在列表中从左到右查找值为pivot的元素,然后根据第二个参数是BEFORE还是AFTER来决定将value插 入到该元素的前面还是后面。
- 语法:
LINSERT key BEFORE|AFTER pivot value
RPOPLPUSH
- 将元素从一个列表转移到另一个列表中
- 语法:
RPOPLPUSH source destination
应用之商品评论列表
- 需求:
用户针对某一商品发布评论,一个商品会被不同的用户进行评论,存储商品评论时,要按时间顺序排序。 用户在前端页面查询该商品的评论,需要按照时间顺序降序排序。 - 分析:
使用list存储商品评论信息,KEY是该商品的ID,VALUE是商品评论信息列表 - 实现:
商品编号为 1001 的商品评论 key 【 items: comment:1001 】
> LPUSH items:comment:1001 '{"id":1,"name":"商品不错,很 好!!","date":1430295077289}'
set 类型
set类型介绍
set 类型即集合类型,其中的数据是不重复且没有顺序。
集合类型和列表类型的对比:
集合类型 | 列表类型 | |
---|---|---|
存储内容 | 至少2的32次方-1个字符串 | 至少2的32次方-1个字符串 |
有序性 | 否 | 是 |
唯一性 | 是 | 否 |
- 集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在等,由于集合类型的 Redis 内部是使用值为 空的散列表实现,所有这些操作的时间复杂度都为 0(1) 。
- Redis 还提供了多个集合之间的交集、并集、差集的运算
命令
此处省略
zset类型 (sortedset)
zset介绍
-
在 set 集合类型的基础上,有序集合类型为集合中的每个元素都 关联一个分数 ,这使得我们不仅可以完成插入、删除和判断元素是否存在在集合中,还能够获得分数最高或最低的前N个元素、获取指定分数范围内的元素等与分数有关的操作。
-
在某些方面有序集合和列表类型有些相似:
二者都是有序的。
二者都可以获得某一范围的元素。 -
但是,二者有着很大区别:
1、列表类型是通过链表实现的,获取靠近两端的数据速度极快,而当元素增多后,访问中间数据的速度会变慢。
2、有序集合类型使用散列表实现,所有即使读取位于中间部分的数据也很快。
3、列表中不能简单的调整某个元素的位置,但是有序集合可以(通过更改分数实现)
4、有序集合要比列表类型更耗内存。
命令
此处省略
应用之商品销售排行榜
- 需求
根据商品销售量对商品进行排行显示 - 设计
定义商品销售排行榜(sorted set集合),Key为items:sellsort,分数为商品销售量。
写入商品销售量
- 商品编号 1001 的销量是 9 ,商品编号 1002 的销量是 10
127.0.0.1:6379> ZADD items:sellsort 9 1001 10 1002
- 商品编号 1001 的销量加 1
127.0.0.1:6379> ZINCRBY items:sellsort 1 1001
- 商品销量前 10 名
127.0.0.1:6379> ZREVRANGE items:sellsort 0 9 withscores
Redis消息模式
队列模式
使用list类型的lpush和rpop实现消息队列
注意事项:
- 消息接收方如果不知道队列中是否有消息,会一直发送rpop命令,如果这样的话,会每一次都建立一次连接,这样显然不好。
- 可以使用brpop命令,它如果从队列中取不出来数据,会一直阻塞,在一定范围内没有取出则返回null;
发布订阅模式
- 订阅消息(subscribe)
示例:
subscribe kkb-channel
- 发布消息
示例:
publish kkb-channel “hello world”
- Redis发布订阅命令
序号 | 命令及描述 |
---|---|
1 | PSUBSCRIBE pattern [pattern …] 订阅一个或多个符合给定模式的频道。 |
2 | PUBSUB subcommand [argument [argument …]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。 |
4 | PUNSUBSCRIBE [pattern [pattern …]] 退订所有给定模式的频道。 |
5 | SUBSCRIBE channel [channel …] 订阅给定的一个或多个频道的信息。 |
6 | UNSUBSCRIBE [channel [channel …]] 指退订给定的频道。 |
Redis事务
Redis事务介绍
- Redis 的事务是通过 MULTI 、 EXEC 、 DISCARD 和 WATCH 、UNWATCH这五个命令来完成的。
- Redis 的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
- Redis 将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis 不支持回滚操作。
事务命令
MULTI
用于标记事务块的开始。 Redis会将后续的命令逐个放入队列中,然后使用EXEC命令原子化地执行这个命令序列。
- 语法:
multi
EXEC
在一个事务中执行所有先前放入队列的命令,然后恢复正常的连接状态
- 语法:
exec
DISCARD
清除所有先前在一个事务中放入队列的命令,然后恢复正常的连接状态。
- 语法:
discard
WATCH
当某个[事务需要按条件执行]时,就要使用这个命令将给定的[键设置为受监控]的状态。
- 语法:
watch key [key…]
注意事项:使用该命令可以实现 Redis 的乐观锁。
UNWATCH
清除所有先前为一个事务监控的键
- 语法:
unwatch
命令图解
事务演示
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 111
QUEUED
127.0.0.1:6379> hset set1 name zhangsan
QUEUED
127.0.0.1:6379> exec
1) OK
2) (integer) 1
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s2 222
QUEUED
127.0.0.1:6379> hset set2 age 20
QUEUED
127.0.0.1:6379> discard
OK
127.0.0.1:6379> exec (error) ERR EXEC without MULTI
127.0.0.1:6379> watch s1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set s1 555
QUEUED 127.0.0.1:6379> exec # 此时在没有exec之前,通过另一个命令窗口对监控的s1字段进行修改
(nil)
127.0.0.1:6379> get s1
111
Redis 不支持事务回滚(为什么呢)
- 大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
- Redis 为了性能方面就忽略了事务回滚。