标签:可见 ons ext ica java context load 限制 watch
Redis 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis 事务的主要作用就是串联多个命令防止别的命令插队。
组队中某个命令出现了报告错误,执行时整个的所有队列会都会被取消。
如果执行阶段某个命令报出了错误(类比运行时异常),则只有报错的命令不会被执行,而其他的命令都会执行,不会回滚。
有 3 个人有你的账户,同时去参加双十一抢购
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。这样别人想拿这个数据就会 block 直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁。但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis 就是利用这种 check-and-set 机制实现事务的。
在执行 multi 之前,先执行 watch key1 [key2 ...]
,可以监视一个(或多个) key,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
取消 watch 命令对所有 key 的监视。
如果在执行 watch 命令之后,EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行 unwatch 了。
yum install httpd-tools
ab -n 请求数 -c 并发数 -p 请求的数据文件 -T "application/x-www-form-urlencoded" 测试的请求
页面核心代码:
<form id="msform" action="${pageContext.request.contextPath}/doseckill"
enctype="application/x-www-form-urlencoded">
<input type="hidden" id="prodid" name="prodid" value="1101">
<input type="button" id="miaosha_btn" name="seckill_btn" value="秒杀点我"/>
</form>
<script type="text/javascript">
$(function(){
$("#miaosha_btn").click(function(){
var url = $("#msform").attr("action");
$.post(url, $("#msform").serialize(), function(data) {
if(data=="false"){
alert("抢光了!");
$("#miaosha_btn").attr("disabled", true);
}
});
})
})
</script>
doseckill → SecKillServlet:
public class SecKillServlet extends HttpServlet {
protected void doPost(HttpServletRequest request
, HttpServletResponse response) throws ServletException, IOException {
String userid = new Random().nextInt(50000) + "" ;
String prodid = request.getParameter("prodid");
boolean if_success = SecKill_redis.doSecKill(userid, prodid);
response.getWriter().print(if_success);
}
}
SecKill_redis:
public class SecKill_redis {
public static boolean doSecKill(String uid, String prodid) throws IOException {
// 拼接 key
String kcKey = "Seckill:" + prodid + ":kc";
String userKey = "Seckill:" + prodid + ":user";
Jedis jedis = new Jedis("192.168.33.128", 6379);
// 获取库存
String kc = jedis.get(kcKey);
// 1. 秒杀还没开始:库存为 NULL
if(kc == null) {
System.out.println("秒杀还没开始!");
jedis.close();
return false;
}
// 2. 已经秒杀成功:存储秒杀成功的用户 set 中已经有该用户id
if(jedis.sismember(userKey, uid)) {
System.out.println("已经秒杀成功,不能重复秒杀!");
jedis.close();
return false;
}
// 3. 判断库存
// 3.1 若 <= 0,秒杀结束
if(Integer.parseInt(kc) <= 0) {
System.out.println("秒杀已结束!");
jedis.close();
return false;
}
// 3.2 若 > 0,则减库存加人
jedis.decr(kcKey);
jedis.sadd(userKey, uid);
System.out.println("秒杀成功!");
jedis.close();
return true;
}
}
加入事务,监视库存。
public static boolean doSecKill(String uid, String prodid) throws IOException {
// 拼接 key
String kcKey = "Seckill:" + prodid + ":kc";
String userKey = "Seckill:" + prodid + ":user";
Jedis jedis = new Jedis("192.168.33.128", 6379);
// 监视库存
jedis.watch(kcKey);
// 获取库存
String kc = jedis.get(kcKey);
// 1. 秒杀还没开始:库存为 NULL
if(kc == null) {
System.out.println("秒杀还没开始!");
jedis.close();
return false;
}
// 2. 已经秒杀成功:存储秒杀成功的用户 set 中已经有该用户id
if(jedis.sismember(userKey, uid)) {
System.out.println("已经秒杀成功,不能重复秒杀!");
jedis.close();
return false;
}
// 3. 判断库存
// 3.1 若 <= 0,秒杀结束
if(Integer.parseInt(kc) <= 0) {
System.out.println("秒杀已结束!");
jedis.close();
return false;
}
// 3.2 若 > 0,则减库存加人
Transaction transaction = jedis.multi();
transaction.decr(kcKey);
transaction.sadd(userKey, uid);
List<Object> execList = transaction.exec();
if(execList == null || execList.size() == 0) {
System.out.println("秒杀失败!");
jedis.close();
return false;
}
System.out.println("秒杀成功!");
jedis.close();
return true;
}
pool.getResource()
来获取;如果赋值为 -1,则表示不限制;如果 pool 已经分配了 MaxTotal 个 Jedis 实例,则此时 pool 的状态为 exhaustedping()
;如果为 true,则得到的 Jedis 实例均是可用的JedisPool jedispool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = jedispool.getResource();
System.out.println(jedis.ping());
Lua 是一个小巧的脚本语言,Lua 脚本可以很容易的被 C/C++ 代码调用,也可以反过来调用 C/C++ 的函数,Lua 并没有提供强大的库,一个完整的 Lua 解释器不过 200k,所以 Lua 不适合作为开发独立应用程序的语言,而是作为嵌入式脚本语言。
很多应用程序、游戏使用 Lua 作为自己的嵌入式脚本语言,以此来实现可配置性、可扩展性。这其中包括众多游戏插件或外挂。
Lua 脚本在 Redis 中的优势:
Redis 为单线程模型,Lua 脚本执行具有原子性。
Redis 使用单个 Lua 解释器去运行所有脚本,并且,Redis 也保证脚本会以原子性(atomic) 的方式执行:当某个脚本正在运行的时候,不会有其他脚本或 Redis 命令被执行。这和使用 MULTI / EXEC 包围的事务很类似。在其他别的客户端看来,脚本的效果(effect) 要么是不可见的(not visible),要么就是已完成的(already completed)。
另一方面,这也意味着,执行一个运行缓慢的脚本并不是一个好主意。写一个跑得很快很顺溜的脚本并不难,因为脚本的运行开销(overhead) 非常少,但是当你不得不使用一些跑得比较慢的脚本时,请小心,因为当这些蜗牛脚本在慢吞吞地运行的时候,其他客户端会因为服务器正忙而无法执行命令。
SecKillServlet
boolean if_success = SecKill_redisByScript.doSecKill(userid, prodid);
SecKill_redisByScript
public class SecKill_redisByScript {
static String secKillScript ="local userid=KEYS[1];\r\n" +
"local prodid=KEYS[2];\r\n" +
"local qtkey=‘Seckill:‘..prodid..\":kc\";\r\n" +
"local usersKey=‘Seckill:‘..prodid..\":user\";\r\n" +
"local userExists=redis.call(\"sismember\",usersKey,userid);\r\n" +
"if tonumber(userExists)==1 then \r\n" +
" return 2;\r\n" +
"end\r\n" +
"local num= redis.call(\"get\" ,qtkey);\r\n" +
"if tonumber(num)<=0 then \r\n" +
" return 0;\r\n" +
"else \r\n" +
" redis.call(\"decr\",qtkey);\r\n" +
" redis.call(\"sadd\",usersKey,userid);\r\n" +
"end\r\n" +
"return 1" ;
static String secKillScript2 =
"local userExists=redis.call(\"sismember\",\"{sk}:0101:usr\",userid);\r\n" +
" return 1";
public static boolean doSecKill(String uid,String prodid) throws IOException {
JedisPool jedisPool = JedisPoolUtil.getJedisPoolInstance();
Jedis jedis = jedisPool.getResource();
String sha1 = jedis.scriptLoad(secKillScript);
// [1] 加载脚本之后的结果
// [2] 参数个数
// [3 ..] 参数..
Object result = jedis.evalsha(sha1, 2, uid, prodid);
String reString = String.valueOf(result);
if("0".equals(reString)) {
System.err.println("已抢空!");
} else if("1".equals(reString)) {
System.out.println("抢购成功!");
} else if("2".equals(reString)) {
System.err.println("该用户已抢过!");
} else {
System.err.println("抢购异常!");
}
jedis.close();
return true;
}
}
标签:可见 ons ext ica java context load 限制 watch
原文地址:https://www.cnblogs.com/liujiaqi1101/p/13613210.html