# redis 高级应用--使用 redis 消息队列完成秒杀过期订单处理(1)

redis 高级应用--使用 redis 消息队列完成秒杀过期订单处理(1)

一、redis 消息通知处理代金券过期问题--课程介绍

1、过期问题解决方案的分析

1.1 过期问题描述
1.2 常用解决方案分析

2.redis介绍及入门

2.1 redis的介绍与安装
2.1.1 redis 介绍
2.1.2 redis 的安装
2.2 redis的基本操作
2.3 redis中的订阅与发布

3.redis整合SpringData Redis开发

3.1 RedisTemplate的介绍
3.2 搭建环境

4.在Java程序中监听redis消息

4.1 配置监听redis消息、
4.2 测试消息

5.结合redis的key失效机制和消,息完成过期优惠券处理

5.1 模拟过期代金卷案例

二、redis 消息通知处理代金券过期问题--失效问题的分析

1、过期问题解决方案的分析--过期问题描述

在电商系统中,秒杀,抢购,红包优惠卷等操作,一般都会设置时问限制,比如订单15分钟不付款自动关闭,红包有效期24小时等等。

2、过期问题解决方案的分析--常用解决方案分析

目前企业中最常见的解决方案大致分为两种:

  • 1.使用定时任务处理,定时扫描数据库中过期的数据,然后进行修改。但是为了更加精确的时间控制,定时任务的执行时间会设置的很短,所以会造成很大的数据库压力。

  • 2使用消息通知,当数据失效时发送消息,程序接收到失效消息后对响应的数据进行状态修改。此种方式不会对数据库造成太大的压力,

三、redis入门:安装

1、redis 介绍

1)redis 是一个 key-value 存储系统。和 Memcached 类似,它支持存储的 value 类型相对更多,包括 string(字符串)、list(连表)、set(集合)、zset(sorted set-有序集合)和 hash(哈希类型)。这些数据类型都支持 push/pop、add/remove 及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis 支持各种不同方式的排序。与 memcached 一样,为了保证效率,数据都是缓存在内存中。区别的是 redis 会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了 master-slave(主从)同步。

2)Redis 是一个高性能的 key-value 数据库。redis 的出现,很大程度补偿了 memcached这 类 key/value 存储的不足,在部分场合可以对关系数据 库起到很好的补充作用。它提供了 Java,C/C++,C#,PHP,JavaScnpt,Per,Obiect-C,Pvthon,RubyErlang 等客户端,使用很方便。

3)Redis 支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得 Redis 可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

2、redis 的安装

1)redis 下载地址(windows 版本)

下载地址:https://github.com/MSOpenTech/redis/tags

下载版本:Redis-x64-3.2.100.zip

2)redis 安装:windows 系统下,解压即安装。

3)启动 Redis(默认6379端口)

  • redis 安装目录下,双击运行:
java 复制代码
# 服务器启动命令
redis-server.exe
# 命令行客户端
redis-cli.exe
  • redis 安装目录下,打开 cmd 窗口,带参数启动 redis
java 复制代码
# 启动redis服务端(默认6379端口)
redis-server.exe redis.windows.conf
# 打开另一cmd窗口,启动redis客户端,连接redis服务端,默认6379端口。
redis-cli
127.0.0.1:6379>

四、redis入门:测试 redis

1、redis 的基本操作

1)key 操作

DEL: 删除Key, del key1 key2

EXISTS: 检查key是否存在,EXISTS key

EXPIRE: 设置或者更新到期时间,到期后自动清除,单位秒 设置为-1表示永不过期。EXPIRE key。

TTL: 以秒为单位,返回给定key的剩余生存时间。

KEYS: 查看所有 key

2)String 操作

Get: 获取

SET: 设置(新增 修改)

SETNX: 只有在KEY不存在时设置value。就是新增一个(不包含更新)

3)Hash 操作

HMSET key field value [field value ...]: 同时将多个 field-value (域-值)对设置到哈希表 key 中。

HSET key field value: 将哈希表 key 中的域 field 的值设为 value 。

HMGET key field [field ...]: 返回哈希表 key 中,一个或多个给定域的值。

HGET key field: 返回哈希表 key 中给定域 field 的值。

4)List 操作

LINDEX keyindex: 通过索引获取列表中的元素。

LPUSH key value1 [value2]: 将一个或多个值插入到列表头部。

RPUSH key value1 [value2]: 在列表中添加一个或多个值。

LRANGE key start stop: 获取列表指定范围内的元素。

5)Set 操作

SADD key member [member ...]: 将一个或多个 member 元素加入到集合 key 当中。

SMEMBERS key: 返回集(At +A所有成员。

6)Zset 操作

ZADD key score1 member1 [score2 member2]:向有序集合添加一个或多个成员。

2、redis 基本操作演示

java 复制代码
# 创建数据 
127.0.0.1:6379> set name redis-test
OK

# 获取数据 
127.0.0.1:6379> get name
"redis-test"

# 查询所有 key 
127.0.0.1:6379> keys *
1) "name"

# 删除 key
127.0.0.1:6379> del name
(integer) 1

# 再次查询所有 key 为空
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379>

五、redis入门:pub,sub 模式消息通知的说明

1、redis 中的订阅与发布

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

2、如图展示:频道ITCAST,以及订阅了此频道的两个客户端(客户端A,客户端B)

六、redis 入门:在 redis 中操作 pub-sub 消息

1、redis 通过两个命令来实现 pub/sub 模式的消息通知

1)publish 主题名称 消息内容 (向指定的主题中发送一条消息 )

2)subscribe 主题名称(订阅某一个主题 )

2、redis 命令 pub/sub 模式的消息通知 演示

java 复制代码
# 打开一客户端(A端),连接服务端,订阅主题 
127.0.0.1:6379> subscribe redis-pub-sub
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "redis-pub-sub"
3) (integer) 1

# 打开另一客户端(B端),连接服务端,发送消息
127.0.0.1:6379> publish redis-pub-sub "verygood"
(integer) 1

# 观察 客户端(A端),接收到新消息
1) "message"
2) "redis-pub-sub"
3) "verygood"

七、redis 入门:redis 中的 key 失效机制

1、redis 中,当 key 失效的时候,会发送一些通知

可以通过订阅某一个主题,接收 key 失效的消息通知(此逼知是 redis 内部的事件处理机制)。

2、如何 订阅主题,接收 key 失效的消息通知?

1)修改 redis 启动的配置文件,开启事件通知。

2)需要订阅的主题名称:

keyevent@dbindex:expired

:存入到的redis服务器:0号数据库

如:订阅的主题: keyevent@0:expired

3、redis 中的 key 失效机制 演示

java 复制代码
# 记事本打开 redis 安装目录下的 redis.windows.conf 配置文件,修改如下:

#  notify-keyspace-events ""
# 开启事件通知
notify-keyspace-events Ex

# 修改完配置文件,保存后,重启 redis 服务端

# 打开一客户端(A端),连接服务端,订阅主题 
127.0.0.1:6379> subscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyevent@0__:expired"
3) (integer) 1

# 打开另一客户端(B端),连接服务端,创建数据,有效时间为10秒
127.0.0.1:6379> set testKey "test"
OK
127.0.0.1:6379> expire testKey 10
(integer) 1
127.0.0.1:6379> ttl testKey
(integer) 3
127.0.0.1:6379> ttl testKey
(integer) -2
127.0.0.1:6379>

# 观察 客户端(A端),接收到新消息
1) "message"
2) "__keyevent@0__:expired"
3) "testKey"

4、redis 消息通知处理代金券过期问题分析:

我们可以借助 redis 的 key 失效通知,用户获取到一个优惠券的时候,将优惠券信息保存到 redis 服务器中,并且设置超时时间。

当 redis 数据失效时,会发送一条消息通知,接收到失效的消息通知后,在 java 代码端可以进行处理。

八、springDataRedis 的介绍与基本操作

1、整合 SpringData Redis 开发

1)我们使用 redis 解决过期优惠券和红包等问题,并且在 java 环境中使用 redis 的消息通知。目前世面比较流行的 java 代码操作 redis 的 AIP 有: jedis 和 RedisTemplate。

2)Jedis 是 Redis 官方推出的一款面向 Java 的客户端,提供了很多接口供 Java 语言调用。

3)SpringData Redis 是 Spring 官方推出,可以算是 Spring 框架集成 Redis 操作的一个子框架,封装了 Redis 的很多命令,可以很方便的使用 Spring 操作 Redis 数据库。由于现代企业开发中都使用 Spring 整合项目,所以在 API 的选择上我们使用 Spring 提供的 SpringData Redis 是比较明智的选择。

2、SpringData Redis 的介绍:

SpringData Redis 是一个开源框架,‌旨在简化 Java 应用程序与 Redis 数据库的集成。‌它提供了对 Redis 的高层抽象,‌使得开发者能够更方便地使用 Redis 的各种功能,‌而无需深入了解 Redis 的底层实现细节。‌SpringData Redis 的主要特点包括:‌

1)简化集成:‌通过简单的配置,‌SpringData Redis 允许开发者将Redis集成到 Spring 应用程序中,‌无需编写大量的底层代码。‌它实现了 Spring 框架的缓存抽象层,‌为 Redis 提供了缓存实现,‌从而减少了开发工作量。‌

2)高性能:‌通过使用连接池和高度封装的 RedisTemplate 类,‌SpringData Redis 提供了高性能的数据访问,‌同时支持多种数据序列化方式,‌以满足不同的性能需求。‌

3)支持多种数据结构:‌SpringData Redis 支持 Redis 的所有数据结构类型,‌包括 String、‌List、‌Set、‌Hash 和 ZSet 等,‌使得开发者能够灵活地处理各种数据结构。‌

4)易于扩展:‌SpringData Redis 支持 Lettuce 和 Jedis 两种客户端实现,‌提供了灵活的配置选项,‌便于根据项目需求进行选择和配置。‌

5)文档和社区支持:‌SpringData Redis 拥有丰富的文档和活跃的社区支持,‌为开发者提供了解决问题的资源和途径。‌

通过使用 SpringData Redis,‌开发者可以更加专注于业务逻辑的实现,‌而不必过多关注底层数据存储和访问的复杂性。‌此外,‌SpringData Redis 还支持 Spring Boot 的集成,‌使得在 Spring Boot 项目中集成 Redis 变得更加简单和方便。

3、打开 idea,创建 spring-data-redis-test 的 maven 工程。

java 复制代码
	--> idea --> File 
	--> New --> Project 
	--> Maven 
		Project SDK: ( 1.8(java version "1.8.0_131" ) 
	--> Next 
	--> Groupld : ( djh.it )
		Artifactld : ( spring-data-redis-test )
		Version : 1.0-SNAPSHOT
	--> Name: ( spring-data-redis-test )
		Location: ( ...\spring-data-redis-test\ )	
	--> Finish	

4、 在工程 spring-data-redis-test (模块)中的 pom.xml 中导入依赖

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>djh.it</groupId>
    <artifactId>spring-data-redis-test</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <spring.version>4.2.4.RELEASE</spring.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
        <springdataredis.version>1.7.4.RELEASE</springdataredis.version>
        <mysql.version>5.1.6</mysql.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.6.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
<!--            <version>2.2</version>-->
            <version>2.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.8</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.4.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>${springdataredis.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.9</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

    </dependencies>

    <build>
        <finalName>spring-data-redis-test</finalName>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                        <showWarnings>true</showWarnings>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>

    </build>

</project>
<!-- D:\java-test\idea2019\spring-data-redis-test\pom.xml -->

5、在工程 spring-data-redis-test (模块)中,创建配置文件 applicationContext-redis.xml

java 复制代码
<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd ">

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <!-- 设置key序列化方式 -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property>
        <!-- 设置value序列化方式 -->
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>
        </property>
        <!-- 注入连接工厂 -->
        <property name="connectionFactory" ref="connectionFactory"></property>
    </bean>

    <!-- 创建connectionFactory交给spring容器管理 -->
    <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="127.0.0.1"></property>
        <property name="port" value="6379"></property>
        <property name="database" value="0"></property>
        <property name="poolConfig" ref="poolConfig"></property>
    </bean>

    <!-- 创建连接池的基本配置 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <!-- 最大连接数 -->
        <property name="MaxTotal" value="10"></property>
    </bean>

</beans>

6、在工程 spring-data-redis-test (模块)中,创建测试类 RedisTest.java,进行测试。

java 复制代码
/**
 *  D:\java-test\idea2019\spring-data-redis-test\src\test\java\djh\it\redis\test\RedisTest.java
 *
 *  2024-7-23 测试类 RedisTest.java
 */
package djh.it.redis.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext-redis.xml")
public class RedisTest {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Test
    public void testRedis(){
        redisTemplate.opsForValue().set("redisTest001", "redisTest001");
    }
}

7、打开一客户端(A端),连接服务端,尝试获取数据

java 复制代码
127.0.0.1:6379> keys *
1) "redisTest001"
127.0.0.1:6379> get redisTest001
"redisTest001"
127.0.0.1:6379>
相关推荐
Ahern_20 分钟前
Oracle 普通表至分区表的分区交换
大数据·数据库·sql·oracle
夜半被帅醒39 分钟前
MySQL 数据库优化详解【Java数据库调优】
java·数据库·mysql
不爱学习的啊Biao1 小时前
【13】MySQL如何选择合适的索引?
android·数据库·mysql
破 风1 小时前
SpringBoot 集成 MongoDB
数据库·mongodb
Rverdoser1 小时前
MySQL-MVCC(多版本并发控制)
数据库·mysql
m0_748233641 小时前
SQL数组常用函数记录(Map篇)
java·数据库·sql
dowhileprogramming1 小时前
Python 中的迭代器
linux·数据库·python
0zxm2 小时前
08 Django - Django媒体文件&静态文件&文件上传
数据库·后端·python·django·sqlite
Minxinbb7 小时前
MySQL中Performance Schema库的详解(上)
数据库·mysql·dba
mmsx8 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库