基于mysql樂(lè)觀鎖實(shí)現(xiàn)秒殺的示例代碼
說(shuō)明
如果你的項(xiàng)目流量非常小,完全不用擔(dān)心有并發(fā)的購(gòu)買請(qǐng)求,那么做這樣一個(gè)系統(tǒng)意義不大。但如果你的系統(tǒng)要像12306那樣,接受高并發(fā)訪問(wèn)和下單的考驗(yàn),那么你就需要一套完整的流程保護(hù)措施,來(lái)保證你系統(tǒng)在用戶流量高峰期不會(huì)被搞掛了。
進(jìn)階redis+mq實(shí)現(xiàn):參考springboot + rabbitmq + redis實(shí)現(xiàn)秒殺
嚴(yán)格防止超賣
保證用戶體驗(yàn):高并發(fā)下,別網(wǎng)頁(yè)打不開了,支付不成功了,購(gòu)物車進(jìn)不去了,地址改不了了
防止黑產(chǎn):防止不懷好意的人群通過(guò)各種技術(shù)手段把你本該下發(fā)給群眾的利益全收入了囊中
具體實(shí)現(xiàn)
1、核心
mysql樂(lè)觀鎖防止超賣
樂(lè)觀鎖是指操作數(shù)據(jù)庫(kù)時(shí)(更新操作),想法很樂(lè)觀,認(rèn)為這次的操作不會(huì)導(dǎo)致沖突,在操作數(shù)據(jù)時(shí),并不進(jìn)行任何其他的特殊處理(也就是不加鎖),而在進(jìn)行更新后,再去判斷是否有沖突了。
這里是引用通常實(shí)現(xiàn)是這樣的:在表中的數(shù)據(jù)進(jìn)行操作時(shí)(更新),先給數(shù)據(jù)表加一個(gè)版本(version)字段,每操作一次,將那條記錄的版本號(hào)加1。也就是先查詢出那條記錄,獲取出version字段,如果要對(duì)那條記錄進(jìn)行操作(更新),則先判斷此刻version的值是否與剛剛查詢出來(lái)時(shí)的version的值相等,如果相等,則說(shuō)明這段期間,沒(méi)有其他程序?qū)ζ溥M(jìn)行操作,則可以執(zhí)行更新,將version字段的值加1;如果更新時(shí)發(fā)現(xiàn)此刻的version值與剛剛獲取出來(lái)的version的值不相等,則說(shuō)明這段期間已經(jīng)有其他程序?qū)ζ溥M(jìn)行操作了,則不進(jìn)行更新操作。
2、建表語(yǔ)句
stock商品表
-- ---------------------------- -- Table structure for stock -- ---------------------------- DROP TABLE IF EXISTS `stock`; CREATE TABLE `stock` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL DEFAULT '' COMMENT '名稱', `count` int(11) NOT NULL COMMENT '庫(kù)存', `sale` int(11) NOT NULL COMMENT '已售', `version` int(11) NOT NULL COMMENT '樂(lè)觀鎖,版本號(hào)', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
初始化數(shù)據(jù):

stock_order訂單表
-- ---------------------------- -- Table structure for stock_order -- ---------------------------- DROP TABLE IF EXISTS `stock_order`; CREATE TABLE `stock_order` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `sid` int(11) NOT NULL COMMENT '庫(kù)存ID', `name` varchar(30) NOT NULL DEFAULT '' COMMENT '商品名稱', `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3、業(yè)務(wù)流程

代碼實(shí)現(xiàn)
1、pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mysql-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
2、model

可通過(guò)逆向工程進(jìn)行配置,參考idea+mybatis逆向工程
4、dao
public interface StockMapper {
Stock checkStock(Integer id);//校驗(yàn)庫(kù)存
int updateSale(Stock stock);//扣除庫(kù)存
}
public interface StockOrderMapper {
//創(chuàng)建訂單
void createOrder(StockOrder order);
}
5、sql
商品校驗(yàn)和減庫(kù)存
<select id="checkStock" parameterType="java.lang.Integer" resultType="com.yy.msserver.model.vo.Stock">
select * from stock where id = #{id}
</select>
<update id="updateSale" parameterType="com.yy.msserver.model.vo.Stock" >
update stock
set sale = #{sale,jdbcType=INTEGER} + 1,
version = #{version,jdbcType=INTEGER} + 1,
count = #{count,jdbcType=INTEGER} - 1
where id = #{id,jdbcType=INTEGER}
AND count > 0 AND version = #{version}
</update>
下訂單
<insert id="createOrder" parameterType="com.yy.msserver.model.vo.StockOrder">
insert into stock_order (sid, name,
create_time)
values (#{sid,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR},
#{createTime,jdbcType=TIMESTAMP})
</insert>
6、service
public interface StockOrderService {
public Integer createOrder(Integer id);
}7、實(shí)現(xiàn)
/**
* @author code
* @Date 2022/6/24 9:25
* Description 訂單實(shí)現(xiàn)
* Version 1.0
*/
@Service
public class StockOrderServiceImpl implements StockOrderService {
@Autowired
private StockOrderMapper stockOrderMapper;
@Autowired
private StockMapper stockMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public Integer createOrder(Integer id) {
//校驗(yàn)庫(kù)存
Stock stock = checkStock(id);
if(stock.getCount()>0){
System.out.println("當(dāng)前庫(kù)存:" + stock.getCount());
//扣庫(kù)存
if(updateSale(stock) == 1){
return createOrder(stock);
}else {
return 0;
}
}
return 0;
}
//校驗(yàn)庫(kù)存
private Stock checkStock(Integer id) {
return stockMapper.checkStock(id);
}
//扣庫(kù)存
private int updateSale(Stock stock){
return stockMapper.updateSale(stock);
}
//下訂單
private Integer createOrder(Stock stock){
StockOrder order = new StockOrder();
order.setSid(stock.getId());
order.setCreateTime(new Date());
order.setName(stock.getName());
stockOrderMapper.createOrder(order);
return order.getId();
}
}
8、測(cè)試
模擬100人參與活動(dòng)
@SpringBootTest
class MsServerApplicationTests {
@Autowired
private StockOrderService stockOrderService;
@Test
void contextLoads() throws InterruptedException {
// 庫(kù)存初始化為10,這里通過(guò)CountDownLatch和線程池模擬100個(gè)并發(fā)
int threadTotal = 100;
ExecutorService executorService = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadTotal);
for (int i = 0; i < threadTotal ; i++) {
int uid = i;
executorService.execute(() -> {
try {
stockOrderService.createOrder(1);
} catch (Exception e) {
e.printStackTrace();
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
}
}
9、結(jié)果

商品表

訂單表

到此這篇關(guān)于基于mysql樂(lè)觀鎖實(shí)現(xiàn)秒殺的示例代碼的文章就介紹到這了,更多相關(guān)mysql樂(lè)觀鎖秒殺內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
linux版mysql8配置表名不區(qū)分大小寫問(wèn)題
文章介紹了MySQL 8的安裝步驟,包括配置忽略大小寫、備份數(shù)據(jù)、停止和刪除數(shù)據(jù)庫(kù)文件、配置my.cnf文件、初始化、啟動(dòng)服務(wù)和登錄設(shè)置密碼,還討論了在配置遠(yuǎn)程連接時(shí)遇到的常見(jiàn)問(wèn)題,特別是MySQL 8版本中由于密碼加密方法變化導(dǎo)致的問(wèn)題解決方法2024-11-11
MySQL中報(bào)錯(cuò):Can’t find file: ‘./mysql/plugin.frm’的解決方法
這篇文章主要給大家介紹了關(guān)于在MySQL中報(bào)錯(cuò):Can't find file: './mysql/plugin.frm'的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11
MyCAT上新增一個(gè)庫(kù)及MyCAT報(bào)錯(cuò)1184的問(wèn)題及解決
這篇文章主要介紹了MyCAT上新增一個(gè)庫(kù)及MyCAT報(bào)錯(cuò)1184的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11
Mysql 5.7.18安裝方法及啟動(dòng)MySQL服務(wù)的過(guò)程詳解
這篇文章主要介紹了Mysql 5.7.18安裝方法及啟動(dòng)MySQL服務(wù)的過(guò)程,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-05-05
Linux下MySQL安裝配置 MySQL配置參數(shù)詳解
Linux下MySQL安裝配置 MySQL配置參數(shù)詳解,在linux下配置mysql的朋友可以參考下。2011-07-07
Mysql 5.6使用配置文件my.ini來(lái)設(shè)置長(zhǎng)時(shí)間連接數(shù)據(jù)庫(kù)的問(wèn)題
這篇文章主要介紹了Mysql 5.6使用配置文件my.ini來(lái)設(shè)置長(zhǎng)時(shí)間連接數(shù)據(jù)庫(kù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-07-07
結(jié)合PHP腳本添加和查詢MySQL數(shù)據(jù)的基本教程
這篇文章主要介紹了結(jié)合PHP腳本添加和查詢MySQL數(shù)據(jù)的基本教程,即在PHP程序中使用基本的SELECT FROM和INSERT INTO語(yǔ)句,需要的朋友可以參考下2015-12-12

