您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關Java使用Redis實現一個秒殺功能,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
秒殺功能
秒殺場景現在已經非常常見了,各種電商平臺都有秒殺的產品,接下來我們模擬一個秒殺的項目,最終能夠確保高并發下的線程安全。界面比較簡單,但是功能基本實現。
界面
點擊“秒殺點我”按鈕后臺就會輸出秒殺結果。
第一版
使用Redis緩存數據庫,使用一個key-value存儲秒殺商品數量,使用set集合存儲秒殺成功的用戶。我們以商品0101為示例,設置商品的初始數量為200件。不考慮并發問題,實現功能。
html、jsp、servlet文件不重要省略。
package com.redis.secondskill; import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; public class SS0 { public static boolean doSecKill(String uid,String prodid) { JedisPool jedisPool = JedisPollTool.getInstance(); Jedis jedis = jedisPool.getResource(); String productCountStr = "sec:"+prodid+":count"; String productUserStr = "sec:"+prodid+":user"; String productCount = jedis.get(productCountStr); if(null == productCount) { System.out.println("秒殺還沒有開始"); JedisPollTool.distroy(jedisPool, jedis); return false; } if(jedis.sismember(productUserStr, uid)) { System.out.println(uid + "用戶已經秒殺成功"); JedisPollTool.distroy(jedisPool, jedis); return false; } int prodCount = Integer.parseInt(productCount); if(prodCount <= 0) { System.out.println("秒殺結束"); JedisPollTool.distroy(jedisPool, jedis); return false; } jedis.decr(productCountStr); jedis.sadd(productUserStr, uid); JedisPollTool.distroy(jedisPool, jedis); System.out.println(uid + "秒殺成功"); return true; } }
使用linux httpd-tools工具進行并發測試。
ab -n 1000 -c 200 -p /test/file.txt -T "application/x-www-form-urlencoded" 192.168.0.101:8080/redis-demo/ss
結果
從結果大致來看,沒有什么問題,來查看一個后臺Redis的數據
秒殺的結果里面居然有負數,證明賣超了。
第二版
使用Redis的事務,保證沒有超賣的情況發生。
package com.redis.secondskill; import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.Transaction; public class SS1 { public static boolean doSecKill(String uid,String prodid) { JedisPool jedisPool = JedisPollTool.getInstance(); Jedis jedis = jedisPool.getResource(); String productCountStr = "sec:"+prodid+":count"; String productUserStr = "sec:"+prodid+":user"; jedis.watch(productCountStr); //開始監視 String productCount = jedis.get(productCountStr); if(null == productCount) { System.out.println("秒殺還沒有開始"); JedisPollTool.distroy(jedisPool, jedis); return false; } if(jedis.sismember(productUserStr, uid)) { System.out.println(uid + "用戶已經秒殺成功"); JedisPollTool.distroy(jedisPool, jedis); return false; } int prodCount = Integer.parseInt(productCount); if(prodCount <= 0) { System.out.println("秒殺結束"); JedisPollTool.distroy(jedisPool, jedis); return false; } Transaction transaction = jedis.multi(); transaction.decr(productCountStr); transaction.sadd(productUserStr, uid); List<Object> exec = transaction.exec(); if(exec == null || exec.size() == 0) { System.out.println("秒殺失敗,稍后重試"); JedisPollTool.distroy(jedisPool, jedis); return false; } JedisPollTool.distroy(jedisPool, jedis); System.out.println(uid + "秒殺成功"); return true; } }
結果
由于使用了watch和事務,每次的并發線程訪問中只有一個線程能夠提交成功,可以保證不出現超賣的現象,但是對于一些用戶來說是極其不公平的。
第三版
使用Lua腳本來實現,因為Redis是單線程的,又是C語言編寫的,可以使用Lua調用Redis的命令,Lua會具有排他性,所以能夠保證安全。
package com.redis.secondskill; import java.util.HashSet; import java.util.Set; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class SS2 { static String luaScript ="local userid=KEYS[1];\r\n" + "local prodid=KEYS[2];\r\n" + "local qtkey='sec:'..prodid..\":count\";\r\n" + "local usersKey='sec:'..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" ; public static boolean doSecKill(String uid,String prodid) { JedisPool jedisPool = JedisPollTool.getInstance(); Jedis jedis = jedisPool.getResource(); String sha1 = jedis.scriptLoad(luaScript); 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(uid + "搶購成功!!!!"); }else if("2".equals( reString ) ) { System.err.println("該用戶已搶過!!"); }else{ System.err.println("搶購異常!!"); } JedisPollTool.distroy(jedisPool, jedis); return true; } }
結果
看完上述內容,你們對Java使用Redis實現一個秒殺功能有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。