mysql锁机制

一、mysql总共拥有三种锁
1、行级锁定
1)特点概述:

开销大、加锁慢;会出现死锁;锁颗粒度小,发生并行冲突的概率最小,并发度最高。

2)特点解释:

行级锁定对象的颗粒度很小,也正是因为其锁定颗粒度小,所以发生锁定资源争用的概率也最小,应用程序并发处理能力也相应变高。同时也正是因为锁定资源的颗粒度小,所以每次获取锁和释放锁需要做的工作比较多,开销就大。

2、表级锁定
1)特点概述:

开销小、加锁快;不会出现死锁;锁颗粒度大,发生并行冲突的概率最大,并发度最低。

2)特点解释:

表级别的锁定的颗粒度最大,但是其实现逻辑非常简单,对系统负面影响最小,所以获取锁和释放锁的速度很快,开销最小。因为锁定颗粒度大,那么锁定资源争用的概率很更高,也因此并行度就变低,它一次性对整个表加锁,也避免了死锁。

3)表级锁的模式:

表共享读锁
表独占写锁

3、页级锁定
1)特点概述:

页级锁的锁定粒度介于行级锁和表级锁中间的一种锁,会出现死锁,并发度一般

2)特点解释:

页级锁的锁定粒度介于行级锁和表级锁中间。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。

二、悲观锁和乐观锁

行级锁、表级锁、页级锁三种锁都属于悲观锁,那我们就来看看什么是悲观锁和乐观锁。

1、悲观锁

共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。也就是说:
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
InnoDB中使用悲观锁。
悲观锁先加锁后操作记录,增加了数据库的开销,增加了死锁机会,同时降低了并行性。

1)悲观锁适用场景

悲观锁一般适用于多写的场景。

2)悲观锁流程

在事务操作记录的时候,首先对记录加上排它锁;
如果能够加锁,那么就可以进行操作;
如果无法加锁,那么说明有其它事务对此记录经常操作,那么只能抛出异常。

2、乐观锁

总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。

1)乐观锁适用场景

乐观锁适用于那些查询比较频繁,修改比较少的场景。

2)乐观锁实现方式

使用版本号
一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会自动加1。当有线程想去更新数据值时,会向读取version值,在提交更新时,如果事先读取的version值和数据库中的version值相等,那么就更新,如果不相等就重复刚才的操作。
使用CAS算法
CAS算法是一种无锁算法,在不用锁的情况下能够实现多线程同步操作。

三、数据库引擎采用的锁机制

MyISAM支持表级锁
InnoDB支持行级锁和表级锁,默认是行级锁

1、MyISAM
1)MyISAM并发锁

在下MyISAM下有个concurrent_insert系统变量,concurrent_insert有0,1,2几个值。
0:不允许并发插入,所有插入操作都已经对表加了互斥锁;
1:(MyISAM默认设置)在没有空洞的情况下(表的中间没有被删除的行),一个进程在读的同时,运行另一个进程从表的末尾插入数据。
2:在没有空洞的情况下(表的中间没有被删除的行),允许多个进程对表尾并发插入数据。

2)MyISAM锁的调度

在下MyISAM下,执行select操作前会自动对表加读锁,在执行update、delete、insert操作之前会自动对表加写锁。当A进程对表进行查询操作,B进程对同一张表经常更新操作,即使B进程晚于A进程,B进程也会优先获取锁,也就是说写锁优先级大于读锁。也正因为写锁优先级大于读锁,所以MyISAM不适合大量的更新操作,更新操作多了的话,查询操作就要排队。
MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。
我们一些措施去调整锁的调度顺序:
通过指定启动参数low-priority-updates,使MyISAM引擎默认给予读请求以优先的权利;
执行命令SET LOW_PRIORITY_UPDATES=1,使的更新请求优先级降低;
指定INSERT、UPDATE、DELETE语句的LOW_PRIORITY属性,也能调整相关操作的优先级;

3)总结

在MyISAM中,读锁之间是相互兼容的,但是读锁和写锁、写锁与写锁之间都是互斥的;
因为读锁和写锁是互斥的,所以对于大量的写操作可能会引起读操作在等待。

2、Innodb

Innodb是支持事务的,默认采用行级锁。InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则将使用表锁。

1)InnoDB支持两种行锁:共享锁(读锁S)排他锁(写锁X)

(1)共享锁(读锁S)
共享锁也称为读锁就是允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有。
若事务A对数据加上共享锁,则事务A可以读数据但不能修改此数据,其他事务也只能再对此数据加共享锁,而不能加排他锁(也就意味着多个线程可以对同一个数据共享一个共享锁),其他事务是可以读数据的,但是不能修改。
(2)排他锁(写锁X)
排它锁也称作独占锁、写锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁。
若事务A对数据加上排他锁,事务A可以读数据也可以修改数据,其他事务不能再对此数据加任何锁,直到A释放此数据上的锁。
值得注意的是:在行级锁中,根据mysql的执行计划,虽然有索引,但是如果mysql认为扫描全表会更加高效,它就会使用表级锁而不是行级锁。

2)InnoDB什么时候使用表级锁

在InnoDB表中,默认是使用行级锁,绝大部分是使用行级锁的,但是也有少部分情况使用表级锁。
(1)、 事务需要更新大部分或全部数据,而表内数据又很多,在这个时候使用行级锁,执行效率不仅会很低还可能造成其他事务长时间的锁等待和锁冲突,在这种情况下建议使用表锁来提高该事务的执行速度。
(2)、 事务涉及多个复杂的表,在可能引起死锁、大量事务回滚的情况下也建议使用表级锁,把涉及的所有表锁定可以避免死锁、减少数据库因事务回滚带来的开销、提高性能。

3)在InnoDB中如何优化索引减少锁冲突

使用索引的好处:索引可以加快查询速度,减少对行的锁定,加快锁的释放。
索引优化:
删除重复、复杂的索引。
更新索引统计信息减少索引碎片。
在一个表中不要使用过多的索引。
劲量对那些重复数据比较少的字段加索引。

3、死锁
1)死锁的原因

在Innodb中会造成死锁的原因是因为在Innodb中锁不是一次性全部获得的,而是逐步获得的。当两个事务同时执行,一个锁住了主键索引,在等待其他相关索引。另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁。
发生死锁后,InnoDB一般都能自动检测到,并使一个事务释放锁并回退,另一个事务获得锁,继续完成事务。但在涉及外部锁的情况下,InnoDB并不能完全自动检测到死锁。当大量的事务因为死锁而被挂起是会严重影响mysql性能的,所以最好设置一定的锁等待超时值去避免大量死锁。

2)如何避免死锁

(1)、 如果不同程序并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
(2)、 在同一个事务中,尽可能的一次锁定所需要的所有资源,减少死锁产生概率;
(3)、 对于非常容易产生死锁的业务部分,可以尝试使用表级锁定来减少死锁产生的概率;
(4)、 在事务中,如果要更新数据,应该直接先申请排他锁。如果先申请共享锁,更新的时候再申请排他锁,其他事务可能已经获得了相同记录的共享锁,从而造成锁冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值