乐观锁:数据库
原理: 1)经过在数据库表中添加一个版本号(version)字段来实现乐观锁。 2)更新前先获取到该条数据的版本号(v1),而后在更新语句(更新数据&更新版本号)的where条件中添加 version=v1 条件, 1>若知足version=v1条件(即:成功获取乐观锁),则成功更新数据且版本号+1; 2>若不知足version=v1条件(即:获取乐观锁失败),说明该条数据被其它线程修改过了,则更新失败,回滚事务。 特色: 1)不发生获取锁失败的状况下,开销比较小。 2)若获取锁失败,则代码须要回滚,开销比较大。 应用: 乐观锁适用于锁获取失败的几率比较小的场景,即:读取比较频繁、写入较少的场景。 缺点: 只能保证本系统对数据(数据库表)的操做是安全的,外部系统对数据(数据库表)的操做是不可控的。 解决办法:对外部系统设置权限,即外部系统只有普通查询的权限。
悲观锁:安全
原理:使用数据库提供的锁机制(select .. for update)。 注意:使用悲观锁前,必须先关闭MySQL的自动提交属性。 应用: 写入比较频繁的场景。
咱们应该尽可能避免使用长事务:session
1)在一个事务中执行批量操做(eg:循环插入数据、循环删除数据等)会致使该事务的执行时间变长。 2)长事务会致使数据库链接被长时间持有,若是该请求的并发量较高,则极可能出现链接池中的链接被用光的状况,从而致使其它的请求(因没法获取到数据库链接)一直处于等待状态,没法被响应。 3)咱们应该将事务的范围控制在单个操做上。
Hibernate中的乐观锁和悲观锁:并发
概念:指数据库的隔离级别设为read committed时,为了解决不可重复读的问题而采用的两种办法: 1)设定hibernate的事务隔离级别(使用hibernate.connection.isolation配置:取值一、二、四、8) hibernate.connection.isolation = 2(若是不设,则默认依赖数据库自己的级别) 2)采用乐观锁和悲观锁解决不可重复读的问题 1)悲观锁:使用另外一种load方法:load(xx.class , id , LockMode.Upgrade),把读出来的数据加上一把锁,在事务结束前别人没法访问,须要借助数据库中的锁。 注:LockMode.UPGRADE_NOWAIT是 ORACLE 支持的锁的方式 2)乐观锁:在程序中添加一个version字段,用来检查是否被修改过。版本检查使用版本号或者时间戳来检测更新冲突(而且防止更新丢失)。 在实体类中增长version属性(数据库也会对应生成该字段,初始值为0),并在其get方法前加@Version注解,则在操做过程当中每更新一次该行数据则version值加1,便可在事务提交前判断该数据是否被其余事务修改过。 eg: 悲观锁(PessimisticLock): public void testPessimisticLock() { Session session = sf.openSession(); session.beginTransaction(); // LockMode.UPGRADE 的意思就是:在读这条记录的时候,请数据库为我读的这条记录加把锁 Account a = (Account)session.load(Account.class, 1L, LockMode.UPGRADE); int balance = a.getBalance(); //do some caculation balance = balance - 10; a.setBalance(balance); session.getTransaction().commit(); session.close(); } 控制台发出的SQL语句:select ... for update 分析:在select语句后加上了for update,说明这里在数据库中加了一把锁。