通常情况下,大家最容易想到的解决办法就是在程序中加锁,本文分享一个比加锁更为友好的方式
场景:数据表member中有列money,拿两个线程举例,分别叫线程1和线程2,两个线程同时从member表中get到一个对象,这个对象中的money=20,线程1握着这个对象准备给money列+100元,线程2握着这个对象准备给money列-50元,线程1先干完活update到数据库后,member表的money变成20+100=120,接着线程2也干完活了,update到数据库后,member表的money变成20-50=-30,最终member表中的money变成-30,这显然是不正确的,正确的money值应该为20+100-50=70
如何来解决这个问题呢?上面只是举例两个线程,现实中可能会有更多的线程、更复杂的场景,会把money更新的面目全非
解决思想:第一个干完活的线程update后,其他线程手里攥的对象全部过期失效
解决办法:可以给member表新增一个列,列名就叫rowver吧,类型是timestamp,这种类型会在每次update之后,自动变成一个新值,新增了这一列后每个线程get到的对象都带有一个属性rowver,注意timestamp类型在程序中对应的类型是byte[],也就是字节数值
--新增列
alter table member add rowver timestamp not null
然后update语句就可以修改成
--改造update语句
update member set money=money+@money where userid=@userid and rowver=@rowver
这种机制还是行级别的,是不是很棒!比程序加锁的方式温柔多了
使用新机制后,再还原一下上面的场景:线程1 update后列rowver值变了,线程2 磨磨唧唧慢慢悠悠再来update的时候,发现影响的行数为0,为啥?因为线程2手里攥着的对象的rowver值已经过期了