mysql关于锁问题的延伸

乐观锁认为对同一数据的并发操作不会总发生,属于小概率事件,不用每次都对数据上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,也就是 不采用数据库自身的锁机制,而是通过 程序来实现 。在程序上,我们可以采用 版本号机制 或者 CAS 机制 实现。 乐观锁适用于多读的应用类型, 这样可以提高吞吐量 。在 Java 中 java.util.concurrent.atomic 包下的原子变量类就是使用了乐观锁的一种实现方式:CAS 实现的。

1. 乐观锁的版本号机制 : 在表中设计一个 版本字段 version ,第一次读的时候(jpa “findByxxx”或者@query(select * from xx表 where x.id = xx)读取整行数据记录包含version),会获取 version 字段的取值。然后对数据进行更新或删除操作时,会执行 UPDATE ... SET version=version+1 WHERE version=version 。此时如果已经有事务对这条数据进行了更改,修改就不会成功(不成功就报异常,或者返回影响的行数0等)。

2. 乐观锁的时间戳机制 : 时间戳和版本号机制一样,也是在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行比较,如果两者一致则更新成功,否则就是版本冲突。你能看到乐观锁就是程序员自己控制数据并发操作的权限,基本是通过给数据行增加一个戳(版本号或者时间戳),从而证明当前拿到的数据是否最新。

3、举例优化:如:使用spring+mvc对app的接口api有两个功能:

A传进来userid用户ID,对对应的用户赠送积分;

B传进来userid和goodsid物品id,对对应的用户兑换物品,然后消费积分。

假设短时间内,用户1积分为10,他消费10积分换了个卫生纸,用户2赠送给用户10积分,正常情况下,用户a应该最后是10积分+卫生纸也对换了。

但是如果在接口A获取用户1积分为10,此时接口B也获取用户1积分为10,那么实际用户1最终的积分,是看接口A和接口B谁最后操作完:如果A后操作完,用户1积分为原本10+用户2赠送10 = 20积分;如果B接口后操作完,用户1积分为原本10 - 消费环物品的10 = 0积分;这两个都错误的。

解决方式:就是将A、B对接口的更改由jpa提供的“save”、“saveAndFlush”改为@query“UPDATE ... SET version=version+1 WHERE version=version”方式。这样,无论A、B哪个先更改了余额之后,由于version自动加一,后来的接口更改是无法提交的,可以返回操作失败,或者在执行一次失败接口的重新“查用户1余额然后->进行业务流程 ->更改用户1余额”的操作。

这个例子中,乐观锁未使用真正的java提供的“锁”接口,但是却实现了“锁”的效果,数据一致性.

你可能感兴趣的:(mysql,Java服务端类,数据库,java,开发语言)