innodb和myisam的区别是innodb支持事务和行锁
按照数据操作类型来分:
读锁:读锁也可以叫(共享锁)字母S
写锁:(排它锁)字母x
#创建读锁和写锁 #开启事务 begin; #设置加锁的方式 对读取的几率加s锁(读锁) select .... for share; #或 select ..... lock in share Mode; #对读取的记录加x锁 (写锁)( select .... for update; commit; #或rollback;
备注:对于innodb来说 读锁和写锁可以加在表上或者行上
按照锁粒度角度划分
表锁:
表级别的s锁 和x锁 (myisam)
#查看当前表有没有加锁 shou open tables; #对表t加表级别的 S锁 LOCK TABLES t READ ; #对表t加表级别的 X锁 LOCK TABLES t WRITE ; #释放锁 unlock tables;
备注:innodb也可以但是会性能低
表级别的意向锁 innodb
分为:意向共享锁 和 意向排它锁
#事务要获取某些行的 S 锁,必须先获得表的 IS 锁 意向共享锁 SELECT column FROM table ... LOCK IN SHARE MODE; #事务要获取某些行的 X 锁,必须先获得表的 IX 锁 意向排他锁 SELECT column FROM table ... FOR UPDATE;
自增锁 auto_increment
元数据锁(MDL锁)
当对一个表做增删改查拆作的时候,加MDL读锁:当要对表做结构变更操作的时候,加MDL写锁。”读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查,读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性,解决了DML和DDL操作之间的一致性问题:不需要显式使用,在访问一个表的时候会被自动加上。
行锁:
记录锁:(LOCK_REC_NOT_GAP)记录锁是有S锁和X锁之分的,称之为 S型记录锁 和 X型记录锁 。
间隙锁:(LOCK_GAP) 防止插入幻影记录
临键锁:(LOCK_ORDINARY )既想锁住某条记录 ,又想阻止其他事务在该记录前边的 间隙插入新记录
插入意向锁:(LOCK_INSERT_INTENTION)说一个事务在 插入一条记录时需要判断一下插入位置是不是被别的事务加了 gap锁 ( next-key锁 也包含 gap锁 ),如果有的话,插入操作需要等待,直到拥有 gap锁 的那个事务提交。但是InnoDB规定事务在等待的时候也需要在内存中生成一个锁结构,表明有事务想在某个 间隙 中 插入 新记录,但是 现在在等待
页锁:
按照态度划分
悲观锁:悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会 阻塞 直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,当其他线程想要访问数据时,都需要阻塞挂起。Java中 synchronized 和 ReentrantLock 等独占锁就是悲观锁思想的实现。
select.... for update语句执行过程中所有扫描的行都会被锁上,因此在MySQL中用悲观锁必须确定使用了索引,而不是全表扫描,否则将会把整个表锁住。
乐观锁:乐观锁认为对同一数据的并发操作不会总发生,属于小概率事件,不用每次都对数据上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,也就是不采用数据库自身的锁机制,而是通过程序来实现。在程序上,我们可以采用 版本号机制 或者 CAS机制 实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。在Java中 java.util.concurrent.atomic 包下的原子变量类就是使用了乐观锁的一种实现方式:CAS实现的。
1. 乐观锁的版本号机制
在表中设计一个 版本字段 version ,第一次读的时候,会获取 version 字段的取值。然后对数据进行更新或删除操作时,会执行 UPDATE ... SET version=version+1 WHERE version=version 。此时如果已经有事务对这条数据进行了更改,修改就不会成功。
2. 乐观锁的时间戳机制
时间戳和版本号机制一样,也是在更新提交的时候,将当前数据的时间戳和更新之前取得的时间戳进行比较,如果两者一致则更新成功,否则就是版本冲突。你能看到乐观锁就是程序员自己控制数据并发操作的权限,基本是通过给数据行增加一个戳(版本号或者时间戳),从而证明当前拿到的数据是否最新。
按照加锁方式
显性加锁:通过特定的语句进行加锁,我们一般称之为显示加锁
如:select .... lock in share mode
select .... for update
隐性加锁:
情景一:对于聚簇索引记录来说,有一个 trx_id 隐藏列,该隐藏列记录着最后改动该记录的 事务 id 。那么如果在当前事务中新插入一条聚簇索引记录后,该记录的 trx_id 隐藏列代表的的就是当前事务的 事务id ,如果其他事务此时想对该记录添加 S锁 或者 X锁 时,首先会看一下该记录trx_id 隐藏列代表的事务是否是当前的活跃事务,如果是的话,那么就帮助当前事务创建一个 X 锁 (也就是为当前事务创建一个锁结构, is_waiting 属性是 false ),然后自己进入等待状态(也就是为自己也创建一个锁结构, is_waiting 属性是 true )。
情景二:对于二级索引记录来说,本身并没有 trx_id 隐藏列,但是在二级索引页面的 PageHeader 部分有一个 PAGE_MAX_TRX_ID 属性,该属性代表对该页面做改动的最大的 事务id ,如果PAGE_MAX_TRX_ID 属性值小于当前最小的活跃 事务id ,那么说明对该页面做修改的事务都已经提交了,否则就需要在页面中定位到对应的二级索引记录,然后回表找到它对应的聚簇索引记
录,然后再重复 情景一 的做法。
其他锁:
全局锁:就是对 整个数据库实例 加锁。当你需要让整个库处于 只读状态 的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。全局锁的典型使用 场景 是:做 全库逻辑备份 。
Flush tables with read lock #创建指令
死锁:是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环
一种策略是,直接进入等待,直到超时。这个超时时间可以通过参数innodb_lock_wait_timeout来设置。另一种策略是,发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务(将持有最少行级排他锁的事务进行回滚),让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为on ,表示开启这个逻辑