1、Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行
2、事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
3、Redis 事务的主要作用就是串联多个命令防止别的命令插队
1、事务中的所有命令都会序列化、按顺序地执行
2、事务在执行的过程中,不会被其他客户端发送来的命令请求所打断
队列中的命令(指令), 在没有提交前都不会实际被执行
事务执行过程中, 如果有指令执行失败,其它的指令仍然会被执行, 没有回滚
1、需求: 请依次向Redis 中, 添加三组数据, k1-v1 k2-v2 k3-v3, 要求使用Redis 的事务完成
1、组队的过程中, 可以通过discard 来放弃组队
2、如果在组队阶段报错, 会导致exec 失败, 那么事务的所有指令都不会被执行
3、如果组队成功, 但是指令有不能正常执行的, 那么exec 提交, 会出现有成功有失败情况,也就是事务得到部分执行, 这种情况下, Redis 事务不具备原子性.
1、基本语法: watch key [key …]
2、在执行multi 之前,先执行watch key1 [key2],可以监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断.
3、这里可以结合乐观锁机制进行理解.
1、基本语法unwatch
2、取消watch 命令对所有key 的监视。
3、如果在执行watch 命令后,exec 命令或discard 命令先被执行了的话,那么就不需要再执行unwatch 了
创建一个web项目
1、一个user 只能购买一张票, 即不能复购
2、不能出现超购,也是就多卖了.
3、不能出现火车票遗留问题/库存遗留, 即火车票不能留下
1、创建Java Web 项目, 参照以前讲过搭建Java Web 项目流程即可
2、引入相关的jar 包和jquery
sec_kill_ticket\web\index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
<base href="<%=request.getContextPath() + "/"%>">
</head>
<body>
<h1>北京-成都 火车票 ! 秒杀!
</h1>
<form id="secKillform" action="secKillServlet" enctype="application/x-www-form-urlencoded">
<input type="hidden" id="ticketNo" name="ticketNo" value="bj_cd">
<input type="button" id="seckillBtn" name="seckillBtn" value="秒杀火车票【北京-成都】"/>
</form>
</body>
<script type="text/javascript" src="script/jquery/jquery-3.1.0.js"></script>
<script type="text/javascript">
$(function () {
$("#seckillBtn").click(function () {
var url = $("#secKillform").attr("action");
console.log("url->" , url)// secKillServlet,完整的url http://localhost:8080/seckill/secKillServlet
console.log("serialize->", $("#secKillform").serialize())
//
$.post(url, $("#secKillform").serialize(), function (data) {
if (data == "false") {
alert("火车票 抢光了:)");
$("#seckillBtn").attr("disabled", true);
}
});
})
})
</script>
</html>
src\com\seckill\redis\SecKillRedis.java
public class SecKillRedis {
/**
* 测试一下是否连通了Redis
*
* @param args
*/
public static void main(String[] args) {
Jedis jedis = new Jedis("192.168.198.130", 6379);
System.out.println(jedis.ping());
jedis.close();
}
//秒杀过程
/**
* @param uid 用户id
* @param ticketNo 票编号, 比如北京-成都的ticketNo "bj_cd"
* @return
*/
public static boolean doSecKill(String uid, String ticketNo) {
//1 uid 和ticketNo 非空判断
if (uid == null || ticketNo == null) {
return false;
}
//2.连接到redis
//解读
//1) 每一个来秒杀的用户, 都会连接一把Reids
Jedis jedis = new Jedis("192.168.198.130", 6379);
//3 拼接key
// 3.1 库存key
String stockKey = "sk:" + ticketNo + ":ticket";
// 3.2 秒杀成功用户key=> 对应的值是set , 可以存放有多个秒杀成功用户的id
String userKey = "sk:" + ticketNo + ":user";
//4 获取库存,如果库存null,秒杀还没有开始
String stock = jedis.get(stockKey);
if (stock == null) {
System.out.println("秒杀还没有开始,请等待..");
jedis.close();
return false;
}
// 5 判断用户是否重复秒杀操作
if (jedis.sismember(userKey, uid)) {
System.out.println(uid + " 不能重复秒杀...");
jedis.close();
return false;
}
//6 判断如果火车票数量,剩余数量小于1,秒杀结束
if (Integer.parseInt(stock) <= 0) {
System.out.println("票已经卖光, 秒杀已经结束了");
jedis.close();
return false;
}
//7.1 火车票数量- 1
jedis.decr(stockKey);
//7.2 把秒杀成功用户添加清单里面
jedis.sadd(userKey, uid);
System.out.println("秒杀成功了..");
jedis.close();
return true;
}
}
src\com\seckill\web\SecKillServlet.java
public class SecKillServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//请求时, 模拟生成一个userId
String userId = new Random().nextInt(10000) + "";
//获取用户要购买的票的编号
String ticketNo = request.getParameter("ticketNo");
//调用秒杀
boolean isOK = SecKillRedis.doSecKill(userId, ticketNo);
//返回结果
response.getWriter().print(isOK);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws
ServletException, IOException {
doPost(request, response);
}
}
–进入cd /run/media/root/CentOS 7 x86_64/Packages
vi postfile
注意保证linux 可以访问到Tomcat 所在的服务器.
–确认Linux 可以ping 通Windows
如果Ping 不通, 确认一下Windows 防火墙是否关闭
--指令, 测试前把Redis 的数据先重置一下
ab -n 1000 -c 100 -p ~/postfile -T application/x-www-form-urlencoded http://192.168.198.1:8080/seckill/secKillServlet
(1) ab 是并发工具程序
(2) -n 1000 表示一共发出1000 次http 请求
(3) -c 100 表示并发时100 次, 你可以理解1000 次请求, 会在10 次发送完毕
(4) -p ~/postfile 表示发送请求时, 携带的参数从当前目录的postfile 文件读取(这个你事先要准备好)
(5) -T application/x-www-form-urlencoded 就是发送数据的编码是基于表单的url 编码
(6) ~的含义: https://blog.csdn.net/m0_67401134/article/details/123973115
(7)http://192.168.198.1:8080/seckill/secKillServlet 就是请求的url, 注意这里的IP:port/uri 必须写正确.
注意我们这里先讲连接池然后在讲解决方法