锁机制详解

-- 加全局锁
FLUSH TABLES WITH READ LOCK;

-- 打开全局锁
UNLOCK TABLES;

-- 说明:
-- 1、全局锁是针对整个数据库,加锁后只能执行读操作,所有写操作都会被阻塞,直到打开全局锁才会解除阻塞。
-- 2、全局锁使用场景主要是全库备份,它带来的问题是业务需要暂停,如果做了主从复制还会导致主从同步延迟,所以一般不会轻易使用全局锁。为
--    了保证数据一致性和完整性,使用mysqldump进行全库备份时可以加入“--single-transaction”参数,这样既可以不用加全局锁也可以保证数
--    据一致性和完整性。





-- 加表共享读锁
LOCK TABLES `表名` READ;
-- 说明:
-- 1、所有客户端都只能读不能写。
-- 2、执行加锁操作的客户端再执行写操作会报错,但是其它客户端执行写操作只会阻塞不会报错。

-- 加表独占写锁
LOCK TABLES `表名` WRITE;
-- 说明:
-- 1、执行加锁操作的客户端可以读也可以写,但是其它客户端执行读写操作都会阻塞。

-- 打开表锁(表共享读锁和表独占写锁都是用这个命令打开锁)
UNLOCK TABLES;





-- 共享锁『 LOCK IN SHARE MODE 』,简称读锁或S锁
START TRANSACTION;
SELECT * FROM `user` WHERE `uid` = '8' LOCK IN SHARE MODE;
-- 说明:
-- 1、共享锁并不互斥,即不同客户端可以对同一条记录加共享锁。
-- 2、一个客户端对某条记录加了共享锁,那么就只有该客户端能对该记录执行写操作,其它客户端只能对该记录执行读操作,写操作都会被阻塞。

-- 排他锁『 FOR UPDATE 』,简称写锁或X锁
START TRANSACTION;
SELECT * FROM `user` WHERE `uid` = '8' FOR UPDATE;
-- 说明:
-- 1、执行INSERT、UPDATE、DELETE操作时会自动加排他锁,不需要写“FOR UPDATE”来手动加排他锁。(温馨提示:SELECT操作默认不加任何锁。)
-- 2、排他锁与任何类型行锁(就是共享锁和排他锁)都是互斥的,即一个客户端对某条记录加了排他锁,那么其它客户端如果再对该记录加共享锁或
--    排他锁就会被阻塞(注意:是其它客户端不能再加锁,不是不能读取记录)。

-- 注意事项:
-- 1、读操作如果不手动加锁,那么将不受任何类型行锁影响,即无论记录加了任何类型行锁都可读。
-- 2、加行锁是基于索引的,如果SQL不走索引,那么行锁就会升级为表锁。
-- 3、加共享锁要注意死锁问题,举例:事务T1和事务T2先后对同一条记录R加共享锁,然后事务T1更新记录R,由于事务T2的共享锁,事务T1的更新操
--    作会被阻塞,此时事务T2也更新记录R,由于事务T1的共享锁,事务T2的更新操作也会被阻塞,很明显事务T1和事务T2都在等对方打开共享锁,此
--    时就会发生死锁。





-- 关于悲观锁和乐观锁:
-- ■悲观锁和乐观锁确切来说不是锁,而是两种并发控制思路,它们并不是数据库自带的锁功能,也没有对应的加锁SQL语句或关键字,它们甚至可以
--   纯粹依靠代码来实现。
-- ■悲观锁会假设每次读写数据时都出现最坏情况,即每次读写数据时都有其它客户端要修改数据(故悲观锁比较适用于写多读少的场景),所以在读
--   写数据时需要加锁阻塞其它客户端的写操作,实现方式主要是使用数据库自带的共享锁和排他锁。
-- ■乐观锁会假设每次读写数据时都没有其它客户端要修改数据,所以乐观锁不会刻意使用数据库本身的锁机制,而是依靠数据本身来保证数据的正确
--   性(故乐观锁比较适用于读多写少的场景),其实现方式主要有版本号机制和CAS算法两种。版本号机制非常简单,它也是乐观锁最常使用的实现方
--   式,就是给表增加一个int类型的version字段,每次UPDATE记录都让version字段值自增1,具体来说就是在UPDATE记录前先获取version字段值(假
--   设为9527),在UPDATE记录时把version字段也加入WHERE条件,下面是SQL示例:
--   UPDATE `user` SET `name` = '张三', `version` = `version` + 1 WHERE `uid` = '8' AND `version` = '9527';
--   这样就可以保证在获取version字段值到执行UPDATE操作这段时间里记录数据没有被其它客户端修改过,从而实现了加锁功能。
--   CAS算法实现乐观锁通常会依赖缓存系统,可以使用Redis::watch()和Redis::multi(),或者使用Memcached::cas(),具体实现方式可在网上搜索
--   相关文章,这里不再赘述。

Copyright © 2024 码农人生. All Rights Reserved