当前位置:首页 > 算法 > 正文内容

MySQL乐观锁 实现秒杀案例,增加version字段来判断(jfinal框架)

canca6年前 (2020-04-01)算法508

MySQL乐观锁和悲观锁的主要区别在于:

悲观锁在查询的时候就认为别人会和我抢,我查询的时候就先锁起来。

乐观锁在查询的时候认为别人不会和我抢,我在更新的时候判断一下就可以了。

所以如果在高并发的情况下,博主认为乐观锁并不适合做这种业务,因为是先查询,导致数据库有很多的更新失败,处理异常再次执行的情况,相当于再次查询执行业务处理。

MySQL悲观锁的实现情况请参考这篇文章。 MySQL悲观锁 select for update实现秒杀案例

这次博主的例子还是在Jfinal框架中。想了解Jfinal框架的朋友可以去 这个地址看看:


不要在乎代码质量,博主仅仅只是模拟这种请求。


主要源码:

package com.demo.index;
 
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
 
import com.jfinal.core.Controller;
import com.jfinal.plugin.activerecord.Db;
 
/**
 * 秒杀/MySQL 乐观锁实现方式
 * 
 * @author sun
 */
public class SecKillForOLController extends Controller {
     
    public static int goodsTotalNum = 100;
 
    public void index() {
        //每一次新的请求都会模拟抢购100件商品,所以先恢复商品的数量,和版本号,这个可以不要,也可以手动去更新数据库
        Db.update("update t_goods set num = ?, version = 1 where id = 1", goodsTotalNum);
         
        SecKillOL sk = new SecKillOL();
        // 模拟120个人抢购。 模拟每60人分别在不同的服务器进行发起请求,这样我们的synchronized锁才能体现出来
        //如果我们只new一个SecKillOl,那么相当于synchronized锁了。MySQL乐观锁无法体现出来。
        for (int i = 1; i <= 60; i++) {
            new Thread(sk, i + " 号人").start();
        }
        SecKillOL sk2 = new SecKillOL();
        for (int i = 61; i <= 120; i++) {
            new Thread(sk2, i + " 号人").start();
        }
        renderHtml("<center><h2>抢购成功!</h2></center>");
    }
}
 
/**
 * 这里我们只是演示数据库悲观锁的实现方式,至于事务等其他的都不添加了。
 * 乐观锁本来就不太适合秒杀等业务处理,因为乐观的本质就是: 我很乐观,我先查询,我不认为别人会和我抢。
 * @author sun
 */
class SecKillOL implements Runnable {
 
    @Override
    public void run() {
        synchronized (this) {
            //如果version字段对不上,回滚进行再一次提交,我这里就设置轮询十次,如果还是没有对上,则相当于抢购失败
            //这块的乐观锁异常处理根据实际情况来做,比如你也可以增加while(true)直到成功为止。
            for(int i=0;i<10;i++){
                Connection conn = null;
                PreparedStatement ps = null;
                try {
                    conn = Db.use().getConfig().getConnection();
                    ps = conn.prepareStatement("select * from t_goods where id = 1");
                    ResultSet rs = ps.executeQuery();
                    Integer num = 0;
                    Integer version = null;
                    while (rs.next()) {
                        num = rs.getInt("num");
                        version = rs.getInt("version");
                    }
                    if (num > 0) {
                        PreparedStatement ps2 = conn.prepareStatement("update t_goods set num=num-1, version=version+1 where id = 1 and version = ?");
                        ps2.setInt(1, version);
                        int result = ps2.executeUpdate();
                        if(result>0){
                            System.out.println(Thread.currentThread().getName() + " 抢到了第" + (SecKillForOLController.goodsTotalNum - num + 1) + "商品");
                            break;
                        }else{
                            System.err.println(Thread.currentThread().getName() + " 有人抢到了我前边,导致我无法进行更新,我再尝试一遍");
                        }
                    }else{
                        System.err.println(Thread.currentThread().getName() + " 太悲剧了,没抢到商品...");
                        break;
                    }
                } catch (SQLException e1) {
                    e1.printStackTrace();
                } finally {
                    try {
                        ps.close();
                    } catch (SQLException e) {}
                    try {
                        conn.close();
                    } catch (SQLException e) {}
                }
            }
        }
    }
}
代码里有些jfinal的东西,看不懂的不要紧,只要知道是操作sql就行。

打印结果:

1 号人 抢到了第1商品
55 号人 抢到了第2商品
61 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
54 号人 抢到了第3商品
61 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
61 号人 抢到了第4商品
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
120 号人 抢到了第5商品
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
119 号人 抢到了第6商品
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
117 号人 抢到了第7商品
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
118 号人 抢到了第8商品
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
116 号人 抢到了第9商品
115 号人 抢到了第10商品
53 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
......
11 号人 抢到了第98商品
10 号人 抢到了第99商品
66 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
9 号人 有人抢到了我前边,导致我无法进行更新,我再尝试一遍
66 号人 抢到了第100商品
9 号人 太悲剧了,没抢到商品...
69 号人 太悲剧了,没抢到商品...
8 号人 太悲剧了,没抢到商品...
67 号人 太悲剧了,没抢到商品...
7 号人 太悲剧了,没抢到商品...
65 号人 太悲剧了,没抢到商品...
6 号人 太悲剧了,没抢到商品...
68 号人 太悲剧了,没抢到商品...
64 号人 太悲剧了,没抢到商品...
63 号人 太悲剧了,没抢到商品...
5 号人 太悲剧了,没抢到商品...
62 号人 太悲剧了,没抢到商品...
4 号人 太悲剧了,没抢到商品...
3 号人 太悲剧了,没抢到商品...
2 号人 太悲剧了,没抢到商品...
60 号人 太悲剧了,没抢到商品...
59 号人 太悲剧了,没抢到商品...
58 号人 太悲剧了,没抢到商品...
57 号人 太悲剧了,没抢到商品...
56 号人 太悲剧了,没抢到商品...

t_goods表结构:

CREATE TABLE `d_sunjs_test`.`t_goods`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `num` int(11) NULL DEFAULT NULL,
  `version` int(11) NULL DEFAULT 1 COMMENT '乐观锁',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

乐观锁的实现方式是通常在表里增加一列version或者时间戳之类的:

如果A、B两个用户抢购同一个商品(只有一件),这两个用户如果都进行了查询(如果version的初始值是1),那么两个用户都拿到了这个值,在更新的时候,如果B先更新的,更新语句类似: update t_goods set num=num-1,version=version+1 where id = 1 and version = 上边的version值:1; 那么这条语句更新肯定没问题。如果A再更新,那么where后边的条件就不满足了,所以导致更新失败。 这就是所谓的乐观锁。





扫描二维码推送至手机访问。

版权声明:本文由Ant.Master's Blog发布,如需转载请注明出处。

本文链接:https://iant.work/post/708.html

标签: 算法
分享给朋友:

“MySQL乐观锁 实现秒杀案例,增加version字段来判断(jfinal框架)” 的相关文章

MySQL悲观锁 select for update实现秒杀案例(jfinal框架)

为了方便测试,博主使用最新的jfinal框架,里边的东西就修改了一下a_little_config.txt,配置数据库链接的,启动直接打开DemoConfig.java,main方法启动即可(记得修改端口),这样就不用再去写jdbc了。感兴趣的朋友可以去了解一下:主要源码(模拟120人同时抢购100...

不同概率的抽奖

需求很简单,为了更加形象,这里我们列一个表格来显示我们抽奖的物品和对应的概率(没有边框,大家凑合着看看吧,不想改造Octopress的样式了)序号物品名称物品ID抽奖概率1物品1P10.22物品2P20.13物品3P30.44物品4P40.35物品5P50.06物品6P6-0.17物品7P70.00...

MySQL Redis分布式锁实现秒杀案例(jfinal框架)

我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:    第一个为key,我们使用key来当锁,因为key是唯一...

zookeeper分布式锁实现秒杀案例(jfinal框架)

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。ZooKeeper的架构通过冗余服务实现高可用性。...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。