重庆小潘seo博客

当前位置:首页 > 重庆网络营销 > 小潘杂谈 >

小潘杂谈

mysql数据库中锁机制的详细介绍

时间:2020-09-22 19:40:07 作者:重庆seo小潘 来源:
本篇文章给大家带来的内容是关于mysql数据库中锁机制的详细介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 悲观锁与乐观锁: 悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,

本篇文章给大家带来的内容是关于mysql数据库中锁机制的详细介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

悲观锁与乐观锁: 悲观锁:顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

表级:引擎 MyISAM,直接锁定整张表,在你锁定期间,其它进程无法对该表进行写操作。如果你是写锁,则其它进程则读也不允许

页级:引擎 BDB,表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录

行级:引擎 INNODB, 仅对指定的记录进行加锁,这样其它进程还是可以对同一个表中的其它记录进行操作。

上述三种锁的特性可大致归纳如下: 1) 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。 2) 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。 3) 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

三种锁各有各的特点,若仅从锁的角度来说,表级锁更适合于以查询为主,只有少量按索引条件更新数据的应用,如WEB应用;行级锁更适合于有大量按索引条件并发更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

MySQL表级锁有两种模式: 1、表共享读锁(Table Read Lock)。对MyISAM表进行读操作时,它不会阻塞其他用户对同一表的读请求,但会阻塞 对同一表的写操作; 2、表独占写锁(Table Write Lock)。对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作。

MyISAM表的读和写是串行的,即在进行读操作时不能进行写操作,反之也是一样。但在一定条件下MyISAM表也支持查询和插入的操作的并发进行,其机制是通过控制一个系统变量(concurrent_insert)来进行的,当其值设置为0时,不允许并发插入;当其值设置为1时,如果MyISAM表中没有空洞(即表中没有被删除的行),MyISAM允许在一个进程读表的同时,另一个进程从表尾插入记录;当其值设置为2时,无论MyISAM表中有没有空洞,都允许在表尾并发插入记录。

MyISAM锁调度是如何实现的呢,这也是一个很关键的问题。例如,当一个进程请求某个MyISAM表的读锁,同时另一个进程也请求同一表的写锁,此时mysql将会如优先处理进程呢?通过研究表明,写进程将先获得锁(即使读请求先到锁等待队列)。但这也造成一个很大的缺陷,即大量的写操作会造成查询操作很难获得读锁,从而可能造成永远阻塞。所幸我们可以通过一些设置来调节MyISAM的调度行为。我们可通过指定参数low-priority-updates,使MyISAM默认引擎给予读请求以优先的权利,设置其值为1(set low_priority_updates=1),使优先级降低。

InnoDB锁与MyISAM锁的最大不同在于: 1、是支持事务(TRANCSACTION)。 2、是采用了行级锁。

我们知道事务是由一组SQL语句组成的逻辑处理单元,其有四个属性(简称ACID属性),分别为: 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全都不执行; 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态; 隔离性(Isolation):数据库系统提供一定的隔离机制,保证事务在不受外部并发操作影响的“独立”环境执行; 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能够保持。

并发事务处理带来的问题 相对于串行处理来说,并发事务处理能大大增加数据库资源的利用率,提高数据库系统的事务吞吐量,从而可以支持更多的用户。但并发事务处理也会带来一些问题,主要包括以下几种情况。 1、更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题--最后的更新覆盖了由其他事务所做的更新。例如,两个编辑人员制作了同一文档的电子副本。每个编辑人员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。最后保存其更改副本的编辑人员覆盖另一个编辑人员所做的更改。如果在一个编辑人员完成并提交事务之前,另一个编辑人员不能访问同一文件,则可避免此问题。 2、脏读(Dirty Reads):一个事务正在对一条记录做修改,在这个事务完成并提交前,这条记录的数据就处于不一致状态;这时,另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做”脏读”。 3、不可重复读(Non-Repeatable Reads):一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读”。 4、幻读(Phantom Reads):一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

事务隔离级别 在上面讲到的并发事务处理带来的问题中,“更新丢失”通常是应该完全避免的。但防止更新丢失,并不能单靠数据库事务控制器来解决,需要应用程序对要更新的数据加必要的锁来解决,因此,防止更新丢失应该是应用的责任。 “脏读”、“不可重复读”和“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。数据库实现事务隔离的方式,基本上可分为以下两种。 1、一种是在读取数据前,对其加锁,阻止其他事务对数据进行修改。 2、另一种是不用加任何锁,通过一定机制生成一个数据请求时间点的一致性数据快照(Snapshot),并用这个快照来提供一定级别(语句级或事务级)的一致性读取。从用户的角度来看,好像是数据库可以提供同一数据的多个版本,因此,这种技术叫做数据多版本并发控制(MultiVersion Concurrency Control,简称MVCC或MCC),也经常称为多版本数据库。

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上 “串行化”进行,这显然与“并发”是矛盾的。同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读”并不敏感,可能更关心数据并发访问的能力。 为了解决“隔离”与“并发”的矛盾,ISO/ANSI SQL92定义了4个事务隔离级别,每个级别的隔离程度不同,允许出现的副作用也不同,应用可以根据自己的业务逻辑要求,通过选择不同的隔离级别来平衡 “隔离”与“并发”的矛盾。表20-5很好地概括了这4个隔离级别的特性。

读数据一致性及允许的并发副作用 隔离级别读数据一致性脏读不可重复读幻读 未提交读(Read uncommitted)最低级别,只能保证不读取物理上损坏的数据是是是 已提交度(Read committed)语句级 否是是 可重复读(Repeatable read)事务级 否否是 可序列化(Serializable)最高级别,事务级否否否

最后要说明的是:各具体数据库并不一定完全实现了上述4个隔离级别,例如,Oracle只提供Read committed和Serializable两个标准隔离级别,另外还提供自己定义的Read only隔离级别;SQL Server除支持上述ISO/ANSI SQL92定义的4个隔离级别外,还支持一个叫做“快照”的隔离级别,但严格来说它是一个用MVCC实现的Serializable隔离级别。MySQL支持全部4个隔离级别,但在具体实现时,有一些特点,比如在一些隔离级别下是采用MVCC一致性读,但某些情况下又不是 InnoDB有两种模式的行锁: 1)共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。( Select * from table_name where ……lock in share mode) 2)排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。(select * from table_name where…..for update) 为了允许行锁和表锁共存,实现多粒度锁机制;同时还有两种内部使用的意向锁(都是表锁),分别为意向共享锁和意向排他锁。 1)意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。 2)意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。InnoDB行锁模式兼容性列表 请求锁模式是否兼容 当前锁模式XIXSIS X冲突冲突冲突冲突 IX冲突兼容冲突兼容 S冲突冲突兼容兼容 IS冲突兼容兼容兼容 如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。 意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT语句,InnoDB会自动给涉及数据集加排他锁(X);对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。 1、共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE。 2、排他锁(X):SELECT * FROM table_name WHERE … FOR UPDATE。 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与oracle不同,后者是通过在数据块中对相应数据行加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁! 在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。

查询表级锁争用情况 表锁定争夺: 可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺: mysql> show status like ‘table%’; +——————— +——-+ | Variable_name| Value | +——————— +——-+ | Table_locks_immediate | 2979| | Table_locks_waited| 0| +——————— +——-+ 2 rows in set (0.00 sec))如果Table_locks_waited的值比较高,则说明存在着较严重的表级锁争用情况。 InnoDB行锁争夺:可以通过检查InnoDB_row_lock状态变量来分析系统上的行锁的争夺情况: mysql> show status like ‘innodb_row_lock%’; +——————————-+——-+ | Variable_name| Value | +——————————-+——-+ | InnoDB_row_lock_current_waits | 0| | InnoDB_row_lock_time| 0| | InnoDB_row_lock_time_avg| 0| | InnoDB_row_lock_time_max| 0| | InnoDB_row_lock_waits| 0| +——————————-+——-+ 5 rows in set (0.01 sec)MyISAM写锁实验: 对MyISAM表的读操作,不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;对MyISAM表的写操作,则会阻塞其他用户对同一表的读和写操作;MyISAM表的读操作与写操作之间,以及写操作之间是串行的!根据如表20-2所示的例子可以知道,当一个线程获得对一个表的写锁后,只有持有锁的线程可以对表进行更新操作。其他线程的读、写操作都会等待,直到锁被释放为止。 USER1: mysql> lock table film_text write;当前session对锁定表的查询、更新、插入操作都可以执行: mysql> select film_id,title from film_text where film_id = 1001;USER2: mysql> select film_id,title from film_text where film_id = 1001;等待 USER1: 释放锁: mysql> unlock tables;USER2: 获得锁,查询返回: InnoDB存储引擎的共享锁实验 USER1: mysql> set autocommit = 0; USER2: mysql> set autocommit = 0;USER1: 当前session对actor_id=178的记录加share mode 的共享锁: mysql> select actor_id,first_name,last_name from actor where actor_id = 178lock in share mode;USER2: 其他session仍然可以查询记录,并也可以对该记录加share mode的共享锁: mysql> select actor_id,first_name,last_name from actor where actor_id = 178lock in share mode;USER1: 当前session对锁定的记录进行更新操作,等待锁: mysql> update actor set last_name = ‘MONROE T’ where actor_id = 178;等待 USER2: 其他session也对该记录进行更新操作,则会导致死锁退出: mysql> update actor set last_name = ‘MONROE T’ where actor_id = 178;ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction USER1: 获得锁后,可以成功更新: mysql> update actor set last_name = ‘MONROE T’ where actor_id = 178; Query OK, 1 row affected (17.67 sec) Rows matched: 1Changed: 1Warnings: 0InnoDB存储引擎的排他锁例子 USER1: mysql> set autocommit = 0; USER2: mysql> set autocommit = 0;USER1: 当前session对actor_id=178的记录加for update的排它锁: mysql> select actor_id,first_name,last_name from actor where actor_id = 178 for update;USER2: 其他session可以查询该记录,但是不能对该记录加共享锁,会等待获得锁: mysql> select actor_id,first_name,last_name from actor where actor_id = 178;USER1: 当前session可以对锁定的记录进行更新操作,更新后释放锁: mysql> update actor set last_name = ‘MONROE T’ where actor_id = 178;USER2: 其他session获得锁,得到其他session提交的记录: mysql> select actor_id,first_name,last_name from actor where actor_id = 178 for update;更新性能优化的几个重要参数 bulk_insert_buffer_size 批量插入缓存大小,这个参数是针对MyISAM存储引擎来说的.适用于在一次性插入100-1000+条记录时,提高效率.默认值是8M.可以针对数据量的大小,翻倍增加. concurrent_insert 并发插入,当表没有空洞(删除过记录),在某进程获取读锁的情况下,其他进程可以在表尾部进行插入. 值可以设0不允许并发插入, 1当表没有空洞时,执行并发插入, 2不管是否有空洞都执行并发插入. 默认是1针对表的删除频率来设置. delay_key_write 针对MyISAM存储引擎,延迟更新索引.意思是说,update记录时,先将数据up到磁盘,但不up索引,将索引存在内存里,当表关闭时,将内存索引,写到磁盘.值为 0不开启, 1开启.默认开启. delayed_insert_limit, delayed_insert_timeout, delayed_queue_size 延迟插入,将数据先交给内存队列,然后慢慢地插入.但是这些配置,不是所有的存储引擎都支持,目前来看,常用的InnoDB不支持, MyISAM支持.根据实际情况调大,一般默认够用了。以上就是mysql数据库中锁机制的详细介绍的详细内容,更多请关注小潘博客其它相关文章!