锁定用于确保事务完整性和数据库一致性。锁定可以防止用户读取其他用户正在更改的数据,并防止多个用户同时更改相同的数据。如果不使用锁定,数据库中的数据可能在逻辑上变得不正确,而针对这些数据进行查询可能会产生想不到的结果。
名词解释
表级锁(锁定整个表)
行级锁(锁定一行)
悲观锁(抽象性,不真实存在这个锁)
乐观锁(抽象性,不真实存在这个锁)
1.表级锁
字面意思就是给这个表加上锁
表级锁,一般是指表结构共享锁锁,是不可对该表执行DDL操作,但对DML操作都不限制。行级锁之前需要先加表结构共享锁。
如果用表级锁,其他客户将不能进行查询操作,因此开发中记得用行级锁
2.行级锁
字面意思理解,就是给某一行加上锁,也就是一条记录加上锁
行级锁,一般是指排它锁,即被锁定行不可进行修改,删除,只可以被其他会话select。行级锁之前需要先加表结构共享锁。
共享锁:对某一资源加共享锁,自身可以读该资源,其他人也可以读该资源(也可以再继续加共享锁,即 共享锁可多个共存),但无法修改。要想修改就必须等所有共享锁都释放完之后。语法为:
select * from table lock in share mode
排他锁:对某一资源加排他锁,自身可以进行增删改查,其他人无法进行任何操作。语法为:
select * from table for update
-- 增删改自动加了排他锁
联系:在 Mysql 中,行级锁并不是直接锁记录,而是锁索引。索引分为主键索引和非主键索引两种,如果一条sql 语句操作了主键索引,Mysql 就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。
InnoDB 行锁是通过给索引项加锁实现的,如果没有索引,InnoDB 会通过隐藏的聚簇索引来对记录加锁。也就是说:如果不通过索引条件检索数据,那么InnoDB将对表中所有数据加锁,实际效果跟表锁一样。因为没有了索引,找到某一条记录就得扫描全表,要扫描全表,就得锁定表。
3.悲观锁
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程)。传统的关系型数据库里面就用到了很多这种锁机制,比如行锁,表锁,读锁,写锁,都是在做操作之前先上锁。
一方:查询语句加 for update;另一方:查询语句加 for update;当进行更新语句的时候,另一方不能进行更新操作
4.乐观锁
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁但是在更新的时候会判断一下在此期间别人有没有更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。类似于write_condition机制。
更新语句设置版本号,在指定版本中更新数据
一方:update account set money=money-200,version=version+1 where id=1 and version=0;
另一方操作同一个版本号,则不能更新数据
另一方:update account set money=money+200,version=version+1 where id=1 and version=0;
保证了修改的数据是和它查询出来的数据是一致的,而其他执行程序未进行修改。当然,如果更新失败,表示在更新操作之前,有其他执行程序已经更新了该库存数,那么就可以尝试重试来保证更新成功。为了尽可能避免更新失败,可以合理调整重试次数。
5.悲观锁和乐观锁使用的场合
如果更新多,查询少,用悲观锁;反之,就用乐观锁,其实就是一对互补锁。
乐观锁更新有可能会失败,甚至是更新几次都失败,这是有风险的。所以如果写入较频繁,对吞吐要求不高,可使用悲观锁。
总结:对于以上,可以看得出来乐观锁和悲观锁的区别。
1.悲观锁使用了排他锁,当程序独占锁时,其他程序就连查询都是不允许的,导致吞吐较低。如果在查询较多的情况下,可使用乐观锁。
2.如果更新多,查询少,用悲观锁;反之,乐观锁
也就是一句话:读用乐观锁,写用悲观锁。