悲观锁和乐观锁的概念
概述
悲观锁
1、在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作读某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。
2、悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。
3、简而言之,悲观锁主要用于保护数据的完整性。当多个事务并发执行时,某个事务对数据应用了锁,则其他事务只能等该事务执行完了,才能进行对该数据进行修改操作。
如何使用悲观锁
1、用法:SELECT … FOR UPDATE;
2、获取锁的前提:结果集中的数据没有使用排他锁或共享锁时,才能获取锁,否则将会阻塞。
3、需要注意的是, FOR UPDATE 生效需要同时满足两个条件时才生效:
a)数据库的引擎为 innoDB
b)操作位于事务块中(BEGIN/COMMIT)
乐观锁
乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做
详细解释
悲观锁代码示例
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 这个程序开启一个事务,专门进行查询,
* 并且使用行级锁/悲观锁,锁住相关的记录。
*/
public class Demo {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = jdbc.jdbcUtil.getConnection();
conn.setAutoCommit(false);
String sql = "select ename,job,sal from emp where job = ? for update";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,"MANAGER");
rs = pstmt.executeQuery();
while (rs.next()){
System.out.println(rs.getString("ename")+
","+rs.getString("job")+
","+rs.getDouble("sal"));
}
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
jdbc.jdbcUtil.close(conn,pstmt,rs);
}
}
}
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 这个程序负责修改被锁定的记录。
*/
public class DemoTest {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = jdbc.jdbcUtil.getConnection();
conn.setAutoCommit(false);
String sql = "update emp set sal = sal*1.1 where job = ?";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1,"MANAGER");
int count = pstmt.executeUpdate();
System.out.println(count);
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
e.printStackTrace();
}finally {
jdbc.jdbcUtil.close(conn,pstmt,null);
}
}
}
此处加上断点:
将Demo进行debug:
运行DemoTest:
恢复Demo(Resume Program):
此时DemoTest自动弹出:
总结
悲观锁采用的是「先获取锁再访问」的策略,来保障数据的安全。但是加锁策略,依赖数据库实现,会增加数据库的负担,且会增加死锁的发生几率。此外,对于不会发生变化的只读数据,加锁只会增加额外不必要的负担。在实际的实践中,对于并发很高的场景并不会使用悲观锁,因为当一个事务锁住了数据,那么其他事务都会发生阻塞,会导致大量的事务发生积压拖垮整个系统。
如何选择
在乐观锁与悲观锁的选择上面,主要看下两者的区别以及适用场景就可以了。
1、乐观锁并未真正加锁,效率高。一旦锁的粒度掌握不好,更新失败的概率就会比较高,容易发生业务失败。
2、悲观锁依赖数据库锁,效率低。更新失败的概率比较低。
3、随着互联网三高架构(高并发、高性能、高可用)的提出,悲观锁已经越来越少的被应用到生产环境中了,尤其是并发量比较大的业务场景。