学生选课系统小案例:https://blog.csdn.net/zc_ad/article/details/83578487
Spring Data JPA 之 for update
In the area of concurrency control, the JPA specification supports optimistic and pessimistic locking.
public void lock(Object entity, LockModeType mode);
This method locks the given entity using the named mode. The javax.persistence.LockModeType
enum defines eight modes:
OPTIMISTIC
: Optimistic locking.
OPTIMISTIC_FORCE_INCREMENT
: Optimistic locking. When a transaction is committed, the entity's version column will be incremented even if the entity's state did not change in the transaction.
PESSIMISTIC_READ
: Pessimistic locking. Other transactions may concurrently read the entity, but cannot concurrently update it.
PESSIMISTIC_WRITE
: Pessimistic locking. Other transactions cannot concurrently read or write the entity.
PESSIMISTIC_FORCE_INCREMENT
: Pessimistic locking. Other transactions cannot concurrently read or write the entity. When a transaction is committed, the entity's version column will be incremented even if the entity's state did not change in the transaction.
READ
: A synonym for OPTIMISTIC
.
WRITE
: A synonym for OPTIMISTIC_FORCE_INCREMENT
.
NONE
: No locking is performed.
Entities can also be locked at the time when entity state gets loaded from the datastore. This is achieved by supplying a lock mode to the respective versions of find
and refresh
methods. If an entity state is to be loaded by a query, a lock mode can be passed to the Query.setLockMode
and TypedQuery.setLockMode
methods.
public LockModeType getLockMode(Object entity);
Returns the lock mode currently held by the given entity.
OpenJPA differentiates between PESSIMISTIC_READ
and PESSIMISTIC_WRITE
lock modes only with DB2 databases. While running with other databases, there is no distinction between these two modes because PESSIMISTIC_READ
lock mode is upgraded to PESSIMISTIC_WRITE
.
OpenJPA has additional APIs for controlling entity locking. See Section 3, “ Object Locking ” in the Reference Guide for details.
link: https://openjpa.apache.org/builds/2.2.2/apache-openjpa/docs/jpa_overview_em_locking.html
高并发系统的挑战
在部署分布式系统时,我们通常把多个微服务部署在内网集群中,再用API网关聚合起来对外提供。为了做负载均衡,通常会对每个微服务都启动多个运行实例,通过注册中心去调用。
那么问题来了,因为有多个实例运行都是同一个应用,虽然微服务网关会把每一个请求只转发给一个实例,但当面对高并发时,但它们仍然可能同时操作同一个数据库表,这会不会引发什么问题呢?
悲观锁的问题
比如电商中常见的商品秒杀系统,在用户抢购商品过程中,会有大量并发请求,很可能同时读写一个包含商品剩余数量的表,这种一般要给数据库加锁,否则很容易出现商品超卖错卖的情况。
如果使用数据库自带的锁机制,也就是悲观锁,在写入的时候锁定数据库,其他修改请求到来时就必须等待锁释放,但这就使效率降下来了,而且高并发场景下可能有的请求一直抢不到锁,就会长时间卡在那导致请求失败,然后有大量重试,系统可能会发生连接数耗尽等异常。
乐观锁是个好东西
查阅资料发现乐观锁是个好东西,它是为数据库表增加一个标识数据版本的version字段来实现的,读取数据时把version字段一同读出,写入数据库时比对version字段就知道数据是否被更改过,如果version不相等就说明持有的是过期数据,不能写入,如果相等就可以写入,并把version加一。
乐观锁在写入数据库的时候,才会检查数据是否冲突,如果发现冲突了,就放弃写入,返回写入失败的信息,相比于悲观锁,这是一种轻量级的对数据的锁定方式,能够应对高并发需求。
给数据库添加乐观锁
说乐观锁是个好东西,首先得说 JPA 是个好东西,因为Spring Data JPA已经内置了乐观锁的实现,给数据库表添加乐观锁很简单,添加一个整型字段,并加入@Version注解就可以了,每次提交数据时JPA会自动检查版本。
@Entity
@Table(name = "m_order")
public class Order {
...
@Version
private int version;
...
}
原文链接:https://blog.csdn.net/ta_ab/article/details/77920510