有关乐观锁的实现方式,version的获取问题

981475170 2019-02-19 11:32:22
用MySQL的默认隔离级别,可重复读,采用version=1,2,3,4.....来实现乐观锁的,当两个线程A,B同时操作数据库的一行记录,如果都是先开启事务,然后都是先查询出数据,比如说如下A,B来个线程查询结果如下:


Table :person
A:id name version
1 tom 1

B:id name version
1 tom 1
此时A线程先开更新数据
update set name='jack' ,version =version+1 where id=1 and version =1
然后A线程提交事务。
然后B线程也要更新name的值,但是此时的B线程在查询select * from person
得到的结果肯定是
B:id name version
1 tom 1

此时如果B也要更新name的值,那么B线程要获取version的值是如何获取的呢,通过查询肯定不行,因为查询结果如上,那么是在java代码里面把每次更新过后的version值通过方法的参数传递给B线程吗。即我们在控制version都是通过java代码来获取version的值,而不是通过在数据库中查询相应的version值吗。

在网上查的资料都是笼统的说,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,但是如何获取更新过后的version还是没有说明白。请教大神
...全文
1238 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
luj_1768 2019-02-21
  • 打赏
  • 举报
回复
相关的数据服务对于更新是有滞后的,具体的问题很复杂。策略:1. 在日志里对这种对同一数据项的多次更新做记录,在系统维护时,分析判断哪些是有效的,完成数据更新,并且在之后的系统服务时提供更新后的数据;2. 在数据更新后,即时封闭该数据访问权,发系统通告,待系统中相关数据同步完成后,打开该数据访问限制,在系统中对该数据项的服务启用更新后的数据。服务器应用和单机应用是不同的,确实有些复杂。
Lucia0729 2019-02-21
  • 打赏
  • 举报
回复
也就是说乐观锁保证同时只有一个线程修改数据,其他线程修改数据就会爆错
大隐藏于寺 2019-02-20
  • 打赏
  • 举报
回复
引用 7 楼 981475170 的回复:
[quote=引用 5 楼 我爱娃哈哈 的回复:] 乐观锁保证是的数据的一致性,意思就是当两个线程同时在操作一条数据的时候,A用户修改成功之后B用户提交事务是不会报错,但是不会修改A用户修改后的数据,如果B用户要继续修改,需要先重新获取最新的数据再修改。
我的问题是:如果B用户要继续修改,那怎么保证B重新获取的数据是最新的,就是实现方式。 我的 1.假设A,B用户在开启事务后,获取的数据是一致的,A先修改数据,然后提交事务。B在修改数据,那么就会报错,即修改不成功。 2.那么我理解的B获取数据是最新的,是这样产生的,在A用户提交事务之后,B在开启事务,然后获取数据,这个数据应该是最新的。 也就是A用户事务提交之后,B事务在开始,然后获取的数据就是最新的。[/quote] 可以按照假设1来继续展开,如果B修改报错,重试,可以设定重试次数.具体可以参考: https://nanjiwubing123.iteye.com/blog/2261394 在阿里JAVA开发手册中也有这样的约定: 8. 【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用version作为更新依据。 说明:如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3次。
qybao 2019-02-20
  • 打赏
  • 举报
回复
B事务在A提交事务后再重新取得一次数据(也就是B事务提交前再重新取一次数据)
此时B重新取得的数据一定是最新的,除非你用了缓存,那你应该在提交事务时有个刷新缓存的操作
或者你用个触发器来控制,在提交事务的时候比较version,不一致的是否就拒否
qybao 2019-02-19
  • 打赏
  • 举报
回复
引用
然后B线程也要更新name的值,但是此时的B线程在查询select * from person
得到的结果肯定是
B:id name version
1 tom 1

如果A提交了事务,B检索出的结果就不应该是这样,应该是version=2
除非你的每个线程的检索结果都用了缓存,那么A提交事务的时侯就应该刷新缓存
此时你只需判断B最开始取出来的version和A提交事务后再取出来的version是否一致,如果一致B就提交事务,否则做相应的排他处理,这就是所谓的乐观排他
我爱娃哈哈 2019-02-19
  • 打赏
  • 举报
回复
A和B线程同时获取到version为1的数据之后,A先提交了事务,修改了version,那么B那里就是旧的数据了,修改不成功才是乐观锁要做的事情,你这里如果需要B提交事务的时候更新version为2的数据,那么就不要加乐观锁了。
981475170 2019-02-19
  • 打赏
  • 举报
回复
引用 5 楼 我爱娃哈哈 的回复:
乐观锁保证是的数据的一致性,意思就是当两个线程同时在操作一条数据的时候,A用户修改成功之后B用户提交事务是不会报错,但是不会修改A用户修改后的数据,如果B用户要继续修改,需要先重新获取最新的数据再修改。
我的问题是:如果B用户要继续修改,那怎么保证B重新获取的数据是最新的,就是实现方式。 我的 1.假设A,B用户在开启事务后,获取的数据是一致的,A先修改数据,然后提交事务。B在修改数据,那么就会报错,即修改不成功。 2.那么我理解的B获取数据是最新的,是这样产生的,在A用户提交事务之后,B在开启事务,然后获取数据,这个数据应该是最新的。 也就是A用户事务提交之后,B事务在开始,然后获取的数据就是最新的。
maradona1984 2019-02-19
  • 打赏
  • 举报
回复
如果要保证成功执行,那得需要自旋,为了健壮性,最好加上次数限制
我爱娃哈哈 2019-02-19
  • 打赏
  • 举报
回复
乐观锁保证是的数据的一致性,意思就是当两个线程同时在操作一条数据的时候,A用户修改成功之后B用户提交事务是不会报错,但是不会修改A用户修改后的数据,如果B用户要继续修改,需要先重新获取最新的数据再修改。
981475170 2019-02-19
  • 打赏
  • 举报
回复
引用 1 楼 我爱娃哈哈 的回复:
A和B线程同时获取到version为1的数据之后,A先提交了事务,修改了version,那么B那里就是旧的数据了,修改不成功才是乐观锁要做的事情,你这里如果需要B提交事务的时候更新version为2的数据,那么就不要加乐观锁了。
也就是说乐观锁保证同时只有一个线程修改数据,其他线程修改数据就会爆错。
981475170 2019-02-19
  • 打赏
  • 举报
回复
引用 楼主 981475170 的回复:
用MySQL的默认隔离级别,可重复读,采用version=1,2,3,4.....来实现乐观锁的,当两个线程A,B同时操作数据库的一行记录,如果都是先开启事务,然后都是先查询出数据,比如说如下A,B来个线程查询结果如下: Table :person A:id name version 1 tom 1 B:id name version 1 tom 1 此时A线程先开更新数据 update set name='jack' ,version =version+1 where id=1 and version =1 然后A线程提交事务。 然后B线程也要更新name的值,但是此时的B线程在查询select * from person 得到的结果肯定是 B:id name version 1 tom 1 此时如果B也要更新name的值,那么B线程要获取version的值是如何获取的呢,通过查询肯定不行,因为查询结果如上,那么是在java代码里面把每次更新过后的version值通过方法的参数传递给B线程吗。即我们在控制version都是通过java代码来获取version的值,而不是通过在数据库中查询相应的version值吗。 在网上查的资料都是笼统的说,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,但是如何获取更新过后的version还是没有说明白。请教大神
也就是说乐观锁是保证只有一个线程修改数据,其他的线程修改数据爆错。

81,092

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧