当前位置: 首页 > news >正文

东莞物流网站设计公司金州网站建设

东莞物流网站设计公司,金州网站建设,南昌企业建站系统模板,网站建设工作人员有哪些职责文章目录 一、概述1.1 MySQL锁的由来1.2 锁定义1.3 锁分类 二、共享锁与排他锁2.1 共享锁#xff08;S锁#xff09;2.2 排他锁#xff08;X锁#xff09;2.3 MySQL锁的释放 三、全局锁3.1 介绍3.2 语法3.3 特点 四、表级锁4.1 介绍4.2 表锁4.3 元数据锁#xff08;Meta D… 文章目录 一、概述1.1 MySQL锁的由来1.2 锁定义1.3 锁分类 二、共享锁与排他锁2.1 共享锁S锁2.2 排他锁X锁2.3 MySQL锁的释放 三、全局锁3.1 介绍3.2 语法3.3 特点 四、表级锁4.1 介绍4.2 表锁4.3 元数据锁Meta Data Lock4.4 意向锁Intention Lock 五、行级锁5.1 介绍5.2 行锁 / 记录锁Record Lock5.3 间隙锁Gap Lock5.4 临建锁Next-Key Lock5.5 行锁的粒度粗化 六、页面锁、乐观锁与悲观锁6.1 页面锁6.2 乐观锁6.3 悲观锁 七、加锁规则八、总结 一、概述 1.1 MySQL锁的由来 客户端发往MySQL的一条条SQL语句实际上都可以理解成一个个单独的事务一条sql语句默认就是一个事务。而事务是基于数据库连接的每个数据库连接在MySQL中又会用一条工作线程来维护也意味着一个事务的执行本质上就是一条工作线程在执行当出现多个事务同时执行时这种情况则被称之为并发事务所谓的并发事务也就是指多条线程并发执行。 多线程并发执行自然就会出问题也就是 MySQL基础事务、并发事务四大问题、事务隔离级别——脏写、脏读、不可重复读、幻读 中提到的脏写、脏读、不可重复读及幻读问题。而对于这些问题又可以通过调整事务的隔离级别来避免那为什么调整事务的隔离级别后能避免这些问题产生呢这是因为不同的隔离级别中工作线程执行SQL语句时用的锁粒度、类型不同。 1.2 锁定义 由以上可知数据库的锁机制本身是为了解决并发事务带来的问题而诞生的主要是确保数据库中多条工作线程并行执行时的数据安全性。 锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中除传统的计算资源CPU、RAM、I/O的争用以外数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说锁对数据库而言显得尤其重要也更加复杂。 1.3 锁分类 MySQL的锁机制与索引机制类似都是由存储引擎负责实现的这也就意味着不同的存储引擎支持的锁也并不同这里是指不同的引擎实现的锁粒度不同。但除开从锁粒度来划分锁之外其实锁也可以从其他的维度来划分因此也会造出很多关于锁的名词下面先简单梳理一下MySQL的锁体系 以锁粒度的维度划分 全局锁锁定数据库中的所有表。加上全局锁之后整个数据库只能允许读不允许做任何写操作表级锁每次操作锁住整张表。主要分为三类 表锁分为表共享读锁 read lock、表独占写锁 write lock元数据锁meta data lockMDL基于表的元数据加锁加锁后整张表不允许其他事务操作。这里的元数据可以简单理解为一张表的表结构意向锁分为意向共享锁、意向排他锁这个是InnoDB中为了支持多粒度的锁为了兼容行锁、表锁而设计的使得表锁不用检查每行数据是否加锁使用意向锁来减少表锁的检查 行级锁每次操作锁住对应的行数据。主要分为三类 记录锁 / Record 锁也就是行锁一条记录和一行数据是同一个意思。防止其他事务对此行进行update和delete在 RC、RR隔离级别下都支持间隙锁 / Gap 锁锁定索引记录间隙不含该记录确保索引记录间隙不变防止其他事务在这个间隙进行insert产生幻读。在RR隔离级别下都支持临键锁 / Next-Key 锁间隙锁的升级版同时具备记录锁间隙锁的功能在RR隔离级别下支持 以互斥性的角度划分 共享锁 / S锁不同事务之间不会相互排斥、可以同时获取的锁排他锁 / X锁不同事务之间会相互排斥、同时只能允许一个事务获取的锁共享排他锁 / SX锁MySQL5.7版本中新引入的锁主要是解决SMO带来的问题 以操作类型的维度划分 读锁查询数据时使用的锁写锁执行插入、删除、修改、DDL语句时使用的锁 以加锁方式的维度划分 显示锁编写SQL语句时手动指定加锁的粒度隐式锁执行SQL语句时根据隔离级别自动为SQL操作加锁 以思想的维度划分 乐观锁每次执行前认为自己会成功因此先尝试执行失败时再获取锁悲观锁每次执行前都认为自己无法成功因此会先获取锁然后再执行 放眼望下来是不是看着还蛮多的但总归说来说去其实就共享锁、排他锁两种只是加的方式不同、加的地方不同因此就演化出了这么多锁的称呼。 二、共享锁与排他锁 2.1 共享锁S锁 定义一个事务已获取共享锁当另一个事务尝试对具备共享锁的数据进行读操作时可正常读进行写操作时会被共享锁排斥。 共享锁的意思很简单也就是不同事务之间不会排斥可以同时获取锁并执行。但这里所谓的不会排斥仅仅只是指不会排斥其他事务来读数据但其他事务尝试写数据时就会出现排斥性举个例子理解 事务T1对ID18的数据加了一个共享锁此时事务T2、T3也来读取ID18的这条数据这时T2、T3是可以获取共享锁执行的但此刻又来了一个事务T4它则是想对ID18的这条数据执行修改操作此时共享锁会出现排斥行为不允许T4获取锁执行。 在MySQL中我们可以在SQL语句后加上相关的关键字来使用共享锁语法如下 SELECT ... LOCK IN SHARE MODE; -- MySQL8.0之后也优化了写法如下 SELECT ... FOR SHARE;这种通过在SQL后添加关键字的加锁形式被称为显式锁而实际上为数据库设置了不同的事务隔离级别后MySQL也会对SQL自动加锁这种形式则被称之为隐式锁。 样例做个关于共享锁的小测试先打开两个cmd窗口并于mysql建立连接 -- 窗口1 -- 开启一个事务 begin; -- 获取共享锁并查询 id2 的数据 select * from bank_balance where id2 lock in share mode;-- 窗口2 -- 开启一个事务 begin; -- 获取共享锁并查询 id2 的数据 select * from bank_balance where id2 lock in share mode;-- 尝试修改id2的数据 update bank_balance set balance230 where id2;当窗口1获取了共享锁窗口2执行查询/读操作时 可获取共享锁、正常读但当窗口2执行修改/写操作时 窗口2没反应、未执行成功。而当窗口1中事务A提交后窗口2事务B的写操作才能继续往下执行。 由上可见一个事务已获取共享锁当另一个事务尝试对具备共享锁的数据进行读操作时可正常读进行写操作时会被共享锁排斥。因此从这个实验中可以得知共享锁也具备排他性会排斥其他尝试写的线程当有线程尝试修改同一数据时会陷入阻塞直至持有共享锁的事务结束才能继续执行 2.2 排他锁X锁 上面简单的了解了共享锁之后紧着来看看排他锁排他锁也被称之为独占锁。 当一个线程获取到独占锁后会排斥其他线程进行读写操作如若其他线程也想对共享资源/同一数据进行操作必须等到当前线程释放锁并竞争到锁资源才行。 值得注意的一点是排他锁并不是只能用于写操作对于一个读操作咱们也可以手动地指定为获取排他锁当一个事务在读数据时获取了排他锁那当其他事务来读、写同一数据时都会被排斥。比如事务T1对ID18的这条数据加了一个排他锁此时T2来加排他锁读取这条数据T3来修改这条数据都会被T1排斥。 在MySQL中可以通过如下方式显式获取独占锁 SELECT ... FOR UPTATE;测试 当两个事务同时获取排他锁尝试读取一条相同的数据时其中一个事务就会陷入阻塞直至另一个事务结束才能继续往下执行 但是select * from bank_balance where id2这种普通读 不会被阻塞也就是另一个事务不获取排他锁读数据而是以普通的方式读数据这种方式则可以立刻执行Why是因为读操作默认加共享锁吗并不是因为你尝试加共享锁读这条数据时依旧会被排斥。 可以明显看到第二个事务中尝试通过加共享锁的方式读取这条数据依旧会陷入阻塞状态那前面究竟是因为啥原因才导致的能读到数据呢其实这跟另一种并发控制技术有关即MVCC机制详情可见 MVCC 原理分析、MySQL是如何解决幻读的。 增、删、改都会对数据添加X锁在查询语句中使用for update也会添加X锁 S锁X锁S锁√×X所×× 2.3 MySQL锁的释放 在前面的测试中每次都仅获取了锁但好像从未释放过锁其实MySQL中释放锁的动作都是隐式的毕竟如果交给咱们来释放很容易由于操作不当造成死锁问题发生。因此对于锁的释放工作MySQL自己来干就类似于JVM中的GC机制一样把内存释放的工作留给了自己完成。 但对于锁的释放时机在不同的隔离级别中也并不相同比如在“读未提交”级别中是SQL执行完成后就立马释放锁而在“可重复读”级别中是在事务结束后才会释放。 如果完全按照数据库规范来实现RC隔离级别为了保证其他事务可以读到未提交的数据那就必须得在SQL执行完成后立马释放掉锁这时另一个事务才能读到SQL对应写的数据但在InnoDB引擎中它基于MVCC机制实现了该效果为此InnoDB的RC级别中SQL执行结束后并不会释放锁。 三、全局锁 3.1 介绍 全局锁就是对整个数据库实例加锁加锁后整个实例就处于只读状态后续的DML的写语句DDL语句已经更新操作的事务提交语句都将被阻塞。 其典型的使用场景是做全库的逻辑备份对所有的表进行锁定从而获取一致性视图保证数据的完整性。 为什么全库逻辑备份就需要加全就锁呢 ——我们分析下 不加全局锁 可能存在的问题、以及加了全局锁后的情况。 3.2 语法 # 加全局锁、获取全局锁 flush tables with read lock; # 数据备份。具体指令可见 mysqldump -u 用户名 -p 数据库名 /back/backup.sql# 释放全局锁 unlock tables;数据备份的详细指令可见 MySQLRedisPostgreSQLClickHouse 启动关闭连接常用命令。 3.3 特点 数据库中加全局锁是一个比较重的操作存在以下问题 如果在主库上备份那么在备份期间都不能执行更新业务基本上就得停摆。如果在从库上备份那么在备份期间从库不能执行主库同步过来的二进制日志binlog会导致主从延迟。 在InnoDB引擎中我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。 mysqldump --single-transaction -uroot –p123456 test backup.sql四、表级锁 4.1 介绍 表级锁每次操作锁住整张表。锁定粒度大发生锁冲突的概率最高并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。 表级锁每次操作锁住整张表。主要分为三类 表锁分为表共享读锁 read lock、表独占写锁 write lock元数据锁meta data lockMDL基于表的元数据加锁加锁后整张表不允许其他事务操作。这里的元数据可以简单理解为一张表的表结构意向锁分为意向共享锁、意向排他锁这个是InnoDB中为了支持多粒度的锁为了兼容行锁、表锁而设计的使得表锁不用检查每行数据是否加锁使用意向锁来减少表锁的检查 4.2 表锁 表锁应该是听的最多的一种锁因为实现起来比较简单同时应用范围也比较广泛几乎所有的存储引擎都会支持这个粒度的锁比如常用的MyISAM、InnoDB、Memory等各大引擎都实现了表锁。 使用表锁的开销相对较小加锁快不会产生死锁但是加锁粒度大发生锁冲突的概率更高并发度更低。在innoDB存储引擎中不推荐使用表锁只有在没有事务支持的存储引擎中才会使用如MyISAM 对于表锁分为两类 表共享读锁read lock表独占写锁write lock 语法 加锁lock tables 表名… read/write释放锁unlock tables / 客户端断开连接 特点 A.读锁 B.写锁 如果一个线程获得在一个表上的read锁那么该线程和所有其他线程只能从表中读数据不能进行任何写操作不同的线程可以通过开多个命令行MySQL客户端来实现如果一个线程在一个表上得到一个 WRITE 锁那么只有拥有这个锁的线程可以从表中读取和写表。其它的线程被阻塞 结论: 读锁不会阻塞其他客户端的读但是会阻塞写。写锁既会阻塞其他客户端的读又会阻塞其他客户端的写。 但要注意不同引擎的表锁也在实现上以及加锁方式上有些许不同但归根结底表锁的意思也就以表作为锁的基础将锁加在表上一张表只能存在一个同一类型的表锁。 上面这段话中提到过不同的存储引擎的表锁在使用方式上也有些不同比如InnoDB是一个支持多粒度锁的存储引擎它的锁机制是基于聚簇索引实现的当SQL执行时如果能在聚簇索引命中数据则加的是行锁如无法命中聚簇索引的数据则加的是表锁比如 select * from bank_balance for update;这条SQL就无法命中聚簇索引此时自然加的就是表级别的排他锁但是这个表级锁并不是真正意义上的表锁是一个“伪表锁”但作用是相同的锁了整张表。 而反观MyISAM引擎由于它并不支持聚簇索引所以无法再以InnoDB的这种形式去对表上锁因此如若要在MyISAM引擎中使用表锁又需要使用额外的语法如下 -- MyISAM引擎中获取读锁具备读-读可共享特性 LOCK TABLES table_name READ;-- MyISAM引擎中获取写锁具备写-读、写-写排他特性 LOCK TABLES table_name WRITE;-- 查看目前库中创建过的表锁in_use0表示目前正在使用的表锁 SHOW OPEN TABLES WHERE in_use 0;-- 释放已获取到的锁 UNLOCK TABLES;如上便是MyISAM引擎中获取表级别的共享锁和排他锁的方式但这里的关键词其实叫做READ、WEITE翻译过来也就是读、写的意思因此关于共享锁就是读锁、排他锁就是写锁的说法估计就是因此而来的。 不过MyISAM引擎中获取了锁还需要自己手动释放锁否则会造成死锁现象出现因为如果不手动释放锁就算事务结束也不会自动释放除非当前的数据库连接中断时才会释放。 InnoDB表锁显式获取后必须要自己主动释放否则结合数据库连接池由于数据库连接是长存的就会导致表锁一直被占用。 当你加了read读锁后再尝试加write写锁就会发现无法获取锁当前线程会陷入阻塞反过来也是同理。 4.3 元数据锁Meta Data Lock Meta Data Lock元数据锁也被简称为MDL锁这是基于表的元数据加锁什么意思呢我们在上文讲过表锁是基于整张表加锁行锁是基于一条数据加锁那这个表的元数据是什么呢所有存储引擎的表都会存在一个.frm文件这个文件中主要存储表的结构DDL语句包括表结构的定义信息、创建删除修改表等。而**MDL锁就是基于.frm文件中的元数据加锁**的。这里的元数据 可以简单理解为就是一张表的表结构。 也就是说某一张表涉及到未提交的事务时是不能够修改这张表的表结构的。 MDL加锁过程是系统自动控制无需显式使用在访问一张表的时候会自动加上当对一张表进行增删改查的时候加MDL读锁(共享)当对表结构进行变更操作的时候加MDL写锁(排他)。MDL锁主要作用是维护表元数据的数据一致性在表上有活动事务的时候不可以对元数据进行写入操作。为了避免DML与DDL冲突保证读写的正确性。 MDL是在MySQL5.5版本后再开始支持的一般来说咱们用不上因此也无需手动获取锁主要在 更改表结构时使用比如你要向一张表创建/删除一个索引、修改一个字段的名称/数据类型、增加/删除一个表字段等这类情况。因为毕竟当你的表结构正在发生更改假设此时有其他事务来对表做CRUD操作自然就会出现问题比如我刚删了一个表字段结果另一个事务中又按原本的表结构插入了一条数据这显然会存在风险因此MDL锁在加锁后整张表不允许其他事务做任何操作。 常见的SQL操作所添加的元数据锁 对应SQL锁类型说明lock tables xxx read/write表锁SHARED_READ_ONLY / SHARED_NO_READ_WRITEselect、select … lock in share mode普通读、共享锁SHARED_READ元数据共享锁与SHARED_READ、SHARED_WRITE兼容与EXCLUSIVE互斥insert、update、delete、select … for update增、改、删、排他锁SHARED_WRITE元数据共享锁与SHARED_READ、SHARED_WRITE兼容与EXCLUSIVE互斥alter table …修改表结构EXCLUSIVE元数据排他锁与其他的MDL都互斥 演示 当执行SELECT、INSERT、UPDATE、DELETE等语句时添加的是元数据共享锁SHARED_READ / SHARED_WRITE之间是兼容的 当执行SELECT语句时添加的是元数据共享锁SHARED_READ。此时如果想更改表结构、加元数据排他锁EXCLUSIVE会阻塞排斥。 我们可以通过下面的SQL来查看数据库中的元数据锁的情况 select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;注意版本metadata_locks 表是 MySQL 5.6 版本及之后引入的。如果你正在使用的 MySQL 版本低于 5.6那么该表将不存在。你可以通过运行 SELECT VERSION(); 来检查你的 MySQL 版本。 4.4 意向锁Intention Lock 1介绍 为了避免DML在执行时加的行锁与表锁的冲突在InnoDB中引入了意向锁使得表锁不用检查每行数据是否加锁使用意向锁来减少表锁的检查。 InnoDB引擎是一种支持多粒度锁的引擎而意向锁则是InnoDB中为了支持多粒度的锁为了兼容行锁、表锁而设计的怎么理解这句话呢先来看一个例子 假设一张表中有一千万条数据现在事务T1对ID8888888的这条数据加了一个行锁此时来了一个事务T2想要获取这张表的表级别写锁经过前面的一系列讲解大家应该知道写锁必须为排他锁也就是在同一时刻内只允许当前事务操作如果表中存在其他事务已经获取了锁目前事务就无法满足“独占性”因此不能获取锁。 那思考一下由于T1是对ID8888888的数据加了行锁那T2获取表锁时是不是得先判断一下表中是否存在其他事务在操作但因为InnoDB中有行锁的概念所以表中任何一行数据上都有可能存在事务加锁操作为了能精准的知道答案MySQL就得将整张表的1000W条数据全部遍历一次然后逐条查看是否有锁存在那这个效率自然会非常的低。 有人可能会说慢就慢点怎么了能接受但实际上不仅仅存在这个问题还有另外一个致命问题比如现在MySQL已经判断到了第567W行数据发现前面的数据上都没有锁存在正在继续往下遍历。 要记住MySQL是支持并发事务的也就是MySQL正在扫描后面的每行数据是否存在锁时万一又来了一个事务在扫描过的数据行上加了个锁怎么办比如在第123W条数据上加了一个行锁。那难道又重新扫描一遍嘛这就陷入了死循环行锁和表锁之间出现了兼容问题。 由于行锁和表锁之间存在兼容性问题提出了意向锁。意向锁实际上也是一种特殊的表锁意向锁其实是一种“挂牌告知”的思想好比日常生活中的出租车一般都会有一个牌子表示它目前是“空车”还是“载客”状态而意向锁也是这个思想。 比如当事务T1打算对ID8888888这条数据加一个行锁之前行级别的读锁或写锁就会先加一个表级别的意向锁。此时当事务T2尝试获取一个表级锁时就会先看一下表上是否有意向锁如果有的话再判断一下与自身是否冲突比如表上存在一个意向共享锁目前T2要获取的是表级别的读锁那自然不冲突可以获取。但反之如果T2要获取一个表级的写锁时就会出现冲突T2事务则会陷入阻塞直至T1释放了锁事务结束为止。 2分类 意向共享锁IS由语句select … lock in share mode添加与表锁共享锁read兼容与表锁排他锁write互斥。在准备给表数据添加一个S锁时需要先获得该表的IS锁意向排他锁IX由insert、update、delete、select…for update添加 。与表锁共享锁(read)及排他锁(write)都互斥意向锁之间不会互斥。在准备给表数据添加一个X锁时需要先获得该表的IX锁 一旦事务提交了意向共享锁、意向排他锁都会自动释放。 可以通过以下SQL查看意向锁及行锁的加锁情况 select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;注意版本MySQL之前某些版本不支持data_locks 表即该表可能不存在。可以通过运行 SELECT VERSION(); 来检查你的 MySQL 版本。 3演示 A.意向共享锁与表读锁是兼容的 B.意向排他锁与表读锁、写锁都是互斥的 五、行级锁 5.1 介绍 行级锁每次操作锁住对应的行数据。锁定粒度最小发生锁冲突的概率最低并发度最高。在MySQL诸多的存储引擎中仅有InnoDB引擎支持行锁不考虑那些闭源自研的MyISAM等引擎不支持行锁【因为InnoDB支持聚簇索引——将数据存储与索引放到了一块索引结构的叶子节点保存了行数据。在之前简单聊到过InnoDB中如果能够命中索引数据就会加行锁无法命中则会加表锁】。 InnoDB的数据是基于索引组织的行锁是通过对索引上的索引项加锁来实现的而不是对记录加的锁。对于行级锁主要分为以下三类行锁、间隙锁、临键锁 行锁Record Lock锁定单个行记录的锁防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持间隙锁Gap Lock锁定索引记录间隙不含该记录左右开区间确保索引记录间隙不变防止其他事务在这个间隙进行insert产生幻读。在RR隔离级别下都支持临键锁Next-Key Lock行锁和间隙锁组合同时锁住数据并锁住数据前面的间隙Gap左开右闭。 在RR隔离级别下支持 具体细节如下图 5.2 行锁 / 记录锁Record Lock 行锁Record Lock也称为记录锁一行表数据、一条表记录本身就是同一个含义。锁定单个行记录的锁防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。 锁住一行数据在索引上才能加锁非索引会升级为表级锁。 1介绍 InnoDB实现了以下两种类型的行锁 共享锁S允许一个事务去读一行阻止其他事务获得相同数据集的排它锁。排他锁X允许获取排他锁的事务更新数据阻止其他事务获得相同数据集的共享锁和排他锁。 2语法 -- 获取行级别的共享锁 select * from bank_balance where id 1 lock in share mode;-- 获取行级别的排他锁 select * from bank_balance where id 1 for update;想要使用InnoDB的行锁就是这样写的如果你的SQL能命中索引数据那也就自然加的就是行锁反之则是表锁。但网上很多资料都流传着一个说法InnoDB引擎的表锁没啥用其实这句话会存在些许误导性因为意向锁、自增锁、MDL锁都是表锁也包括InnoDB的行锁是基于索引实现的例如在update语句修改数据时假设where后面的条件无法命中索引那咋加行锁呢此时没办法就必须得加表锁了因此InnoDB的表锁是有用的。 5.3 间隙锁Gap Lock 锁定索引记录间隙不含该记录左右开区间确保索引记录间隙不变防止其他事务在这个间隙进行insert产生幻读。在RR隔离级别下都支持 1间隙锁是对行锁的一种补充主要是用来解决幻读问题的但想要理解它咱们首先来理解啥叫间隙 mysql select * from bank_balance; -------------------------------- | id | user_name | balance | wealth | -------------------------------- | 1 | Jenny | 300 | 1 | | 2 | Tom | 230 | 1 | | 3 | Jack | 500 | 0 | | 9 | Rose | 360 | 0 | --------------------------------上述这张表最后两条数据id字段从3跳到了9那么3~9两者之间的范围则被称为”间隙“而间隙锁主要锁定的就是这块范围。 那为何又说间隙锁是用来解决幻读问题的呢因为幻读的概念是一个事务在执行时另一个事务插入了一条数据从而导致第一个事务操作完成之后发现结果与预想的不一致跟产生了幻觉一样。 好比拿上述表举例子现在要将ID2的用户余额改为100因此事务T1先查到了ID2的3、9两条数据并上锁了然后开始更改用户余额但此时事务T2过来又插入了一条ID6、balance320的数据并提交等T1修改完了3、9两条数据后此时再次查询ID2的数据时结果发现了ID6的这条数据余额并未被修改、数据行比原来还多了。 在上述这个例子中T2因为新增并提交了事务所以T1再次查询时也能看到ID6的这条数据就跟产生了幻觉似的对于这种新增数据专业的叫法称之为幻影数据。 为了防止出现安全问题所以T1在操作之前会对目标数据加锁但在T1事务执行时这条幻影数据还不存在因此就会出现一个新的问题不知道把锁加在哪儿毕竟想要对ID6的数据加锁就是加了个寂寞。普通的行锁无法解决该问题也不能加表锁、太影响性能了此时间隙锁就应运而生主要对间隙区域加锁 2加间隙锁的规则 索引上的等值查询(唯一索引)给不存在的记录加锁时, 优化为间隙锁索引上的等值查询(非唯一普通索引)向右遍历时最后一个值不满足查询需求时next-key lock 退化为间隙锁索引上的范围查询(唯一索引) – 会访问到不满足条件的第一个值为止 注意间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁 select * from bank_balance where id6 lock in share mode;3演示 A.索引上的等值查询(唯一索引)给不存在的记录加锁时, 优化为间隙锁 select * from bank_balance where id6 lock in share mode;当对一个不存在的数据加锁后默认就是锁定前后两条数据之间的区间左右开区间即锁定(3,9)、不包含3、9的区域。当其他事务再尝试向该区间插入数据时就会陷入阻塞只有当持有间隙锁的事务结束后才能继续执行插入操作。 B.索引上的等值查询(非唯一普通索引)向右遍历时最后一个值不满足查询需求时next-key lock 退化为间隙锁 介绍分析一下我们知道InnoDB的B树索引叶子节点是有序的双向链表。 假如我们要根据这个二级索引查询值为18的数据并加上共享锁我们是只锁定18这一行就可以了吗 并不是因为是非唯一索引这个结构中可能有多个18的存在所以在加锁时会继续往后找找到一个不满足条件的值当前案例中也就是29。此时会对18加临键锁并对29之前的间隙加锁。 假设stu表中age为非唯一普通索引 select * from sth where age20 lock in share mode;C.索引上的范围查询(唯一索引) —— 会访问到不满足条件的第一个值为止 查询的条件为id6并添加共享锁。 此时我们可以根据数据库表中现有的数据将数据分为三个部分[6], (6,9], (9,正无穷) 所以数据库数据在加锁是就是将6加了行锁9的临键锁包含9及9之前的间隙正无穷的临键锁(正无穷及之前的间隙) 当对一个不存在的数据加锁后默认就是锁定前后两条数据之间的区间左右开区间即锁定(3,9)、不包含3、9的区域。当其他事务再尝试向该区间插入数据时就会陷入阻塞只有当持有间隙锁的事务结束后才能继续执行插入操作。 不过间隙锁加在不同的位置锁定的范围也并不相同如果加在两条数据之间那么锁定的区域就是两条数据之间的间隙。如果加在上表ID1的数据上锁定的区域则是{1~1}即只锁定ID1的这一行数据毕竟间隙锁的作用是为了保护可能插入的行而不是已有的行因此在这里不会锁定其他不存在的行。 注InnoDB默认的行锁算法为临键锁所以实际情况下对ID1的数据加锁时锁定的区域就是{-∞ ~ 1}即无穷小到1之间的区域。如果加在ID9之后锁定的区域就是{9 ~ ∞}即9之后到无穷大的区域。 5.4 临建锁Next-Key Lock 临键锁是间隙锁的Plus版本或者可以说成是一种由记录锁间隙锁组成的锁 记录锁锁定的范围是表中具体的一条行数据。间隙锁锁定的范围是左右开区间但不包含当前这一条真实数据只锁间隙区域。 而临键锁则是两者的结合体加锁后即锁定左开右闭的区间每个临键锁是左开右闭区间也会锁定当前行数据。 实际上在InnoDB中除开一些特殊情况外当尝试对一条数据加锁时默认加的是临键锁而并非记录锁、间隙锁。也就是说在前面举例幻读问题中当T1要对ID2的用户做修改余额锁定3、9这两条行数据时默认会加的是临键锁也就是当事务T2尝试插入ID6的数据时因为有临建锁存在因此无法再插入这条“幻影数据”也就至少保障了T1事务执行过程中不会碰到幻读问题。 间隙锁和临建锁的目的都是用来解决可重复读的问题如果在读提交级别间隙锁和临建锁都会失效。 5.5 行锁的粒度粗化 行锁并不是一成不变的行锁会在某些特殊情况下发生粗化主要有两种情况 在内存中专门分配了一块空间存储锁对象当该区域满了后就会将行锁粗化为表锁。当做范围性写操作时由于要加的行锁较多此时行锁开销会较大也会粗化成表锁。 当然这两种情况其实很少见因此只需要知道有锁粗化这回事即可这种锁粗化的现象其实在SQLServer数据库中更常见因为SQLServer中的锁机制是基于行记录实现的而MySQL中的锁机制则是基于事务实现的 六、页面锁、乐观锁与悲观锁 上述对MySQL两种较为常见的锁粒度进行了阐述共享锁与排他锁全局锁、表级锁、行级锁接着再来看看页面锁、乐观锁与悲观锁 6.1 页面锁 页面锁是Berkeley DB存储引擎支持的一种锁粒度当然由于BDB引擎被Oracle收购的原因因此MySQL5.1以后不再直接性的支持该引擎需自己整合因此页锁见的也比较少大家稍微了解即可。 表锁以表为粒度锁住的是整个表数据。行锁以行为粒度锁住的是一条数据。页锁以页为粒度锁住的是一页数据。 唯一有些许疑惑的地方就是一页数据到底是多少呢其实我也不大清楚毕竟没用过BDB引擎但我估计就是只一个索引页的大小即16KB左右。 简单了解后页锁后接着来看一看从思想维度划分的两种锁即乐观锁与悲观锁。 6.2 乐观锁 乐观锁即是无锁思想。 乐观锁每次执行都认为只会有自身一条线程操作因此无需拿锁直接执行在最后更新数据时进行比较悲观锁每次执行都认为会有其他线程一起来操作因此每次都需要先拿锁再执行,保证不被其他事务操作可通过select…fot update实现。 编程中的无锁技术或者说乐观锁机制一般都是基于CAS思想实现的而在MySQL中则可以通过version版本号CAS的形式实现乐观锁也就是在表中多设计一个version字段然后在SQL修改时以如下形式操作 UPDATE ... SET version version 1 ... WHERE ... AND version version;也就是每条修改的SQL都在修改后对version字段加一比如T1、T2两个事务一起并发执行时当T2事务执行成功提交后就会对version1因此事务T1的versionversion这个条件就无法成立最终会放弃执行因为已经被其他事务修改过了。 当然一般的乐观锁都会配合轮询重试机制比如上述T1执行失败后再次执行相同语句直到成功为止。 从上述过程中不难看出这个过程中确实未曾添加锁因此也做到了乐观锁/无锁的概念落地但这种形式却并不适合所有情况比如写操作的并发较高时就容易导致一个事务长时间一直在重试执行从而导致客户端的响应尤为缓慢。 因此乐观锁更加适用于读大于写的业务场景频繁写库的业务则并不适合加乐观锁。 6.3 悲观锁 每次执行时都会加锁再执行可通过select…fot update实现。我们之前分析过的 synchronized关键字、AQS-RentrantLock 都属于悲观锁类型即在每次执行前必须获取到锁然后才能继续往下执行而数据库中的排他锁就是一种典型的悲观锁类型。 在数据库中想要使用悲观锁那也就是对一个事务加排他锁for update即可不再重复赘述 七、加锁规则 MySQL中数据加锁的规则可以归纳为以下三种 两个原则 加锁的基本单位是next-key lock前开后闭查找过程中访问到的对象才会加锁 两个优化 索引上的等值查询给唯一索引加锁的时候next-key lock退化为行锁索引上的等值查询向右遍历时且最后一个值不满足等值条件的时候next-key lock退化为间隙锁 一个BUG 唯一索引上的范围查询会访问到不满足条件的第一个值为止 表t中无id7的记录根据原则1加锁单位为next-key locksession A 加锁范围为(5,10] 根据优化2这是一个等值查询(id7)、且id10不满足查询条件next-key lock退化成间隙锁因此最终加锁的范围是(5,10) 所以session B往这个间隙里面插入id8的记录会被锁住但是session C修改id10这是可以的 等值查询上MySQL的优化 索引上的等值查询如果是唯一索引next-key lock会退化为行锁索引上的等值查询(非唯一普通索引)向右遍历时且最后一个值不满足等值条件的时候next-key lock退化为间隙锁 八、总结 总结一下本篇所聊到的不同锁它们之间的冲突与兼容关系 PS表中横向行表示已经持有锁的事务纵向列表示正在请求锁的事务 行级锁对比共享临键锁排他临键锁间隙锁共享临键锁兼容冲突兼容排他临键锁冲突冲突兼容间隙锁兼容兼容兼容 由于临建锁也会锁定相应的行数据因此上表中也不再重复赘述记录锁临建锁兼容的 记录锁都兼容同理冲突的记录锁也会冲突再来看看表级别的锁对比 表级锁对比共享意向锁排他意向锁元数据锁全局锁共享意向锁兼容兼容冲突冲突排他意向锁兼容兼容冲突冲突元数据锁冲突冲突冲突冲突全局锁兼容冲突冲突冲突 放眼望下来其实会发现表级别的锁会有很多很多冲突因为锁的粒度比较大因此很多时候都会出现冲突但对于表级锁咱们只需要关注共享意向锁和共享排他锁即可其他的大多数为MySQL的隐式锁在这里共享意向锁和排他意向锁也可以理解为MyISAM中的表读锁和表写锁。 表中的冲突和兼容究竟是啥意思冲突的意思是当一个事务T1持有某个锁时另一个事务T2来请求相同的锁T2会由于锁排斥会陷入阻塞等待状态。反之同理兼容的意思是指允许多个事务一同获取同一个锁。 参考 黑马程序员相关视频及笔记、MySQL锁机制高并发场景下该如何保证数据读写的安全性
http://www.yingshimen.cn/news/90506/

相关文章:

  • 电子商务网站建设推广分析济南地区做企业网站的公司
  • jq动画效果网站全局代理ip
  • 网站建设及运行情况介绍在线设计平台市场分析
  • 衡阳做网站怎么在微信上卖东西
  • 电子邮箱网站建设大型门户网站建设推广
  • 一般卖机械行业的做哪些网站阿里云虚拟主机怎么建设网站
  • 图片点击切换网站模板成都网站品牌设计案例
  • 怎么做企业功能网站做外贸找客户最好用的网站
  • 网站代理做反向互联网广告推广是什么
  • 做经营行网站需要什么手续那个网站可以找人做设计师
  • 双语言网站模版更新wordpress咋办
  • 郑州企业的网站建设山东电力建设网站
  • 免费微信网站怎么做浅谈企业网站建设的目标
  • python 微信网站开发怎么做网站背景图
  • unity可以做网站吗成都门户网站有哪些
  • 企业网站的基本功能有哪些wordpress是php模板吗
  • 网站建设全部流程包括备案浙江城乡与住房建设部网站
  • 网站备案会过期吗学做网站去哪学
  • 郑州的网站建设58找工作 招聘网最新招聘
  • 哪个网站是做安全教育怀柔营销型网站建设
  • 优酷网站怎么做的做表格的网站
  • 外贸网站设计制作建设网站的颜色
  • 东莞网站设计多少钱php做网站最容易
  • 哪些公司做外贸网站新楼盘
  • 没有网站如何做营销电脑安装wordpress
  • 网站开发产品需求说明关于教育网站的策划书
  • 网站免费正能量直接进入老狼设计公司logo的网站
  • 嘉兴手机网站开发费用网站关键词更新
  • 网站建设电话营销话术怎样进行文化建设
  • 网站建设 地址 上海石门二路巴中区建设局网站