当前位置: 首页 > 后端技术 > PHP

库存扣款mysql解决方案探讨

时间:2023-03-30 03:31:23 PHP

1.你想实现什么功能?点击产品购买按钮;扣除存货;扣除用户余额;将产品添加到用户的背包中;2.可能有高并发场景?同一用户同时开两个客户端,购买同一个产品;同一用户同时开两个客户端,购买不同的产品;两个不同的用户同时购买同一个产品;两个不同的用户,同时购买不同的产品;同一个用户开两个客户端,同时购买多个产品,一个客户购买产品a和b,另一个客户购买产品b,不同的用户同时购买多个产品,一个购买产品a和b,并且另一个购买产品b和a。三、不同场景的逻辑分析及解决方案1、场景一:同一个用户打开不同的客户端,购买同一个产品(1)上图中的问题,如何解决?(2)需要明白商品库存和用户库存是两个线程同时操作的;相当于两个线程同时修改同一个资源,这时候就想到了锁机制。(3)对同一个要操作的资源加锁,只有释放了锁才能被另一个线程修改;(4)selectstockfromtablewhereid=1forupdate;//给要操作的记录加排他锁(5)updatetablesetstock=remainStockwhereid=1;//此时这条记录已经被加锁,并且可以更新股票(6)只有交易结束后才能释放锁。当当前事务还没有提交时,另一个线程一直在等待锁进程,直到释放锁并获取到锁。2.场景二:同一个用户打开两个客户端,同时购买不同的商品(1)购买不同商品时,两个线程操作同一个资源没有库存扣库存的问题。(2)但是对于同一个用户,在扣除余额的时候,会出现两个线程操作同一个资源的问题,所以可以复用场景1的方案,直接对该资源加排他锁。3.场景三:两个不同的用户同时购买同一个商品(1)这个问题其实是不同线程操作同一个资源的问题,可以通过加锁来解决。4.场景四:两个不同的用户同时购买不同的产品;(1)这种场景是最安全的,不会出现操作同一个资源的问题。5、场景五:同一用户同时开通两个客户端,同时购买多个产品。一个客户购买产品a和b,另一个客户购买产品b和a(1)一个线程循环减少产品a和b的库存,另一个线程轩轩减少产品b和a的库存;(2)此时线程2给记录a加排它锁时,另一个线程2给记录b加排它锁;(3)当两个线程都进入第二个循环时,线程1需要减少b的库存,线程2需要减少a的库存;(4)当前线程1持有记录a的锁,等待记录b释放锁。线程2持有记录b的锁,等待记录a释放锁。两个线程互相等待对方释放锁,导致死锁;(5)所以我们可以根据前面的加排他锁查询的方法,然后更新库存可能会导致死锁得出结论(6)如果要解决,可以将所有要加锁的记录加锁在同一个order(7)select*fromtablewhereidin(a,b)orderbyidasc;//先对商品记录进行排序,然后锁定减少库存6.场景六:不同用户同时购买多个商品,一个购买产品a和b,另一个购买产品b和a(1)这个其实是场景5的问题,排序加锁解决死锁问题3.思考和优化1.上面其实讨论了两个核心问题:(1)一个是多个线程同时操作同一个资源的处理方式(加锁)(2)另外一个问题是加锁过程导致的死锁解决(排序加锁顺序)2、我们上面讨论的库存减少方式就是先查询,再执行UPDATE,那么有没有办法将这两个语句放在一起?(1)updatetablesetstock=stock-numwhereid=xxxandstock>=num;//直接在update语句中查询更新,同时保证stock不会减为0,防止超卖问题4.总结1.并发问题其实就是解决多个进程同时操作同一个资源的问题---锁2.在加锁的过程中,也要注意不要造成死锁------给3.注意这些讨论都是基于事务中事务的实现目前的4.innodb默认的隔离级别是第三级,可以重复读取。两个事务的修改对彼此是不可见的。不同进程操作同一个资源时,更要注意加锁的问题