select+update引发的一些思考(1)矛盾抛出

介绍

最近接触了分布式事务,二阶段锁定、2pc、数据库相关知识(事务隔离、日志等)有点懵懂。
忽然想到高并发业务场景(例如抢票、电商之类的),多线程出现超出剩余票数或库存,超卖的情形是如何解决的。

一个简单的模型

    public boolean product(Long producId) {
        Product product = SELECT * FROM TABLE WHERE producId = xxx;
        if (product.getNum() > 0) {
            int updateRow = UPDATE TABLE SET num = product.getNum() - 1 WHERE producId = xxx;
            if(updateRow>0){
                return true; //更新成功
            }
        }
        return false;
    }

select查询该商品,若有剩余商品,则进行-1更新操作,不出现并发的情形就不会出错。
如果多线程的话,会出现更新丢失的情况,假设库存现在为1,一个事务发现人
网上查阅后,一些解决办法:

悲观锁、乐观锁

悲观锁

每次拿到数据都认为会更新(悲观),于是每次拿数据则上锁。
java的synchronized就是悲观锁的一种实现。
InnoDB在读取行时有两种行锁:读共享锁和写独占锁。

读共享锁:SELECT * FROM TABLE WHERE producId = xxx LOCK IN SHARE MODE;

如果事务A先获得了读共享锁,那么事务B之后仍然可以读取加了读共享锁的行数据,但必须等事务A commit或者roll back之后才可以更新或者删除加了读共享锁的行数据。

写独占锁:SELECT * FROM TABLE WHERE producId = xxx LOCK FOR UPDATE;

如果事务A先获得了某行的写共享锁,那么事务B就必须等待事务A commit或者roll back之后才可以访问行数据。

这里需要写独占锁

    public boolean product(Long producId) {
        Product product = SELECT * FROM TABLE WHERE producId = xxx LOCK FOR UPDATE;
        if (product.getNum() > 0) {
            int updateRow = UPDATE TABLE SET num = product.getNum() - 1 WHERE producId = xxx;
            if(updateRow>0){
                return true; //更新成功
            }
        }
        return false;
    }

乐观锁

顾名思议,每次读取数据都不认为别人会进行修改,所以不会上锁,但是会在更新提交的时候判断下在此期间是否有更新

    public boolean product(Long producId) {
        int updateRow = 0;
        while (updateRow == 0) {
            Product product = SELECT * FROM TABLE WHERE producId = xxx;
            if (product.getNum() > 0) {
                updateRow = UPDATE TABLE SET num = product.getNum() - 1 WHERE producId = xxx AND num = product.getNum();
                if (updateRow > 0) {
                    return true; //更新成功
                }
            }else{
                return false;
            }
        }
        return false;
    }

通过在UPDATE操作加上限定num = product.getNum(),如果num和通过查找SELECT操作得到的num不一致,说明事务并发有所影响,此时业务需要回滚或者重新执行。

Q:

  1. UPDATE的行锁问题?
  2. commit之前有所误解,事务提交和数据写入有所弄混(redo、undo、写入磁盘等)
  3. 由上述问题引发事务隔离与锁的问题
  4. 二阶段锁定和那些事务执行中的锁 概念有所弄混

你可能感兴趣的:(select+update引发的一些思考(1)矛盾抛出)