MySQL 中的悲观锁和乐观锁都是应对并发访问时产生的问题,但它们的工作原理和使用场景不同。
悲观锁是指在数据读取、修改过程中,认为每一次操作都有可能会有其他事务来修改这个数据,因此为了保证访问的安全性采用的一种悲观策略。当一个事务想要对某个数据进行操作时,它会先把该数据加锁,确保其他事务不能同时访问,从而避免了竞争条件下的数据不一致问题。悲观锁适用于需要修改大量数据或者数据更新比较频繁的场景,例如银行转账等操作。
乐观锁是相对于悲观锁的一种方案。它假定数据一般情况下不会出现修改冲突,所以每次不会去先加锁再执行操作,而直接去执行操作,在提交更新前,会判断此期间是否有其他事务对这个同样的数据进行了修改,如果有,则放弃本次修改。乐观锁适用于读多写少,数据争用情况较低的场景。
具体到 MySQL 数据库,InnoDB 存储引擎支持悲观锁和乐观锁的实现:
- 悲观锁可以通过在 SQL 语句末尾添加 FOR UPDATE 关键字,该语句会自动为该行或数据集上锁。例如,SELECT * FROM table1 WHERE id=1 FOR UPDATE;。
- 乐观锁可以通过使用版本控制机制来实现。通常情况下,需要为表添加一个版本号字段,每次更新时将其加一,当发生冲突时,会检查更新前后的版本是否一致,若不一致则认为发生了冲突,回滚操作。示例代码如下:
// 读取原始数据
String sql = "SELECT name, version FROM mytable WHERE id=1";
ResultSet rs = stmt.executeQuery(sql);
rs.next();
String name = rs.getString("name");
int version = rs.getInt("version");
// 修改数据
name = "new_name";
version++;
// 提交修改
sql = "UPDATE mytable SET name='" + name + "', version=" + version + " WHERE id=1 AND version=" + (version-1);
int rows = stmt.executeUpdate(sql);
if (rows == 0) {
// 修改失败,抛出异常或者重试
}
总之,悲观锁和乐观锁各有适用场景,应根据业务需求选择合适的锁策略并加以实现。