美文网首页
select+update引发的一些思考(1)矛盾抛出

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

作者: lqsss | 来源:发表于2018-02-22 10:47 被阅读0次

    介绍

    最近接触了分布式事务,二阶段锁定、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)矛盾抛出

          本文链接:https://www.haomeiwen.com/subject/tleotftx.html