标签:脏读 需要 ble 支持 sam 隔离 show alt mic
一. 事务四要素
数据库事务正确执行的四个基本要素包括原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability),简称ACID。目前要实现ACID主要有两种方式:一种是Write ahead logging,也就是日志式的方式(现代数据库均基于这种方式);另一种是Shadow paging。
二. MyISAM表锁定
所有用户行为最后执行的基本单位是单条操作命令(SELECT、INSERT、UPDATE、DELETE等),它们都是原子操作。按照事务术语,MyISAM表总能高效地工作在AUTOCOMMIT=1模式下,原子操作通常能提供可比较的完整性以及更好的性能。这意味着,你能确信在每个特性更新运行的同时,其他用户不能干涉它,而且不会出现自动回滚(如果你不小心,对于事务性表,这种情况可能发生),MySQL服务器还能保证不存在脏读。
一般而言,所有由事务解决的完整性问题均能用LOCK TABLES或原子更新解决,从而确保了服务器不会自动中断,后者是事务性数据库系统的常见问题。MyISAM只支持表级锁定,允许多重读或一次写。LOCK TABLES可以锁定用于当前线程的表,如果表被其它线程锁定,则造成堵塞,直到可以获取所有锁定为止。UNLOCK TABLES可以释放被当前线程保持的任何锁定。当线程发布另一个LOCK TABLES时,或当与服务器的连接被关闭时,所有由当前线程锁定的表被隐含地解锁。如果您想要确保在同一业务流中的SELECT和UPDATE之间没有其它线程访问修改数据,就必须使用LOCK TABLES。如果你获得了对某一表的READ LOCAL锁定(与写锁定相对),该表允许在表尾执行并行插入,当其他客户端执行插入操作时,允许执行读操作。新插入的记录不会被有读锁定属性的客户端看到,直至解除了该锁定为止。使用INSERT DELAYED,能够将插入项置于本地队列中,直至锁定解除,不会让客户端等待插入完成。
三. InnoDB事务与锁定
在 InnoDB 中,所有用户行为都在事务内发生,事务是其执行的基本单位。如果自动提交模式被允许,即 AUTOCOMMIT = 1,每个SQL语句都将以一个单独的事务来运行;如果自动提交模式被用 SET AUTOCOMMIT = 0 关闭,那么我们可以认为一个用户总是有一个事务打开着,一个SQL COMMIT或ROLLBACK语句结束当前事务并且一个新事务开始,两个语句都释放所有在当前事务中被设置的InnoDB锁定。
InnoDB除了表级锁定之外,还支持更细粒度的行级锁定,且行锁和表锁多重粒度可共存。
1. 在一个事务获取表t的某行的S锁之前, 他必须获取表t的一个IS锁或更强的锁 2. 在一个事务获取表t某行的X锁之前, 他必须获取一个t的IX锁 3. 这些规则可以总结为如下锁类型兼容矩阵: X IX S IS X Conflict Conflict Conflict Conflict IX Conflict Compatible Conflict Compatible S Conflict Conflict Compatible Compatible IS Conflict Compatible Compatible Compatible
4. 一个锁如果和已经存在的锁兼容, 就可以授权给请求他的事务, 但如果和已存在的锁不兼容则不行
一个事务必须等待直到冲突的锁被释放.如果一个锁请求和一个已经存在的锁冲突, 并且一直不能被授权, 就会造成死锁。一旦死锁发生,InnoDB会选择其中一个报错并释放其持有的锁,直至解除死锁。意向锁并不会阻塞任何事情,除非是对全表的请求(例如, LOCK TABLES ... WRITE), IX和IS锁的主要目的是表示有人正在或者准备锁定一行
四. 行锁与表锁优劣对比
行级锁定的优点:
行级锁定的缺点:
MySQL表锁定机制:当一个锁定被释放时,锁定可被写锁定队列中的线程得到,然后是读锁定队列中的线程。这意味着,如果你在一个表上有许多更新,SELECT语句将等待直到没有更多的更新。表更新通常情况认为比表检索更重要,因此给予它们更高的优先级,但这应确保更新一个表的活动不能“饿死”,即使该表上有很繁重的SELECT活动
对WRITE,MySQL使用的表锁定方法原理如下:
对READ,MySQL使用的表锁定方法原理如下:
在以下情况下,表锁定优于行级锁定:
UPDATE tbl_name SET column=value WHERE unique_key_col=key_value; DELETE FROM tbl_name WHERE unique_key_col=key_value;
SELECT 结合并行的INSERT语句,并且只有很少的UPDATE或DELETE语句
在整个表上有许多扫描或GROUP BY操作,没有任何写操作
表锁定的注意事项:
使用SET LOW_PRIORITY_UPDATES=1语句可指定具体连接中的所有更新应使用低优先级;
或用LOW_PRIORITY属性给与一个特定的INSERT、UPDATE或DELETE语句较低优先级;
或用HIGH_PRIORITY属性给与一个特定的SELECT语句较高优先级;
或为max_write_lock_count系统变量指定一个低值来启动mysqld来强制MySQL在具体数量的插入完成后临时提高所有等待一个表的SELECT语句的优先级;
或用--low-priority-updates启动mysqld,这将给所有更新(修改)一个表的语句以比SELECT语句低的优先级
如果你有关于INSERT结合SELECT的问题,切换到使用新的MyISAM表,因为它们支持并发的SELECT和INSERT;
如果你对同一个表混合插入和删除,INSERT DELAYED将会有很大的帮助;
如果你对同一个表混合使用SELECT和DELETE语句出现问题,DELETE的LIMIT选项可以有所帮助;
对SELECT语句使用SQL_BUFFER_RESULT可以帮助使表锁定时间变短;
可以更改mysys/thr_lock.c中的锁代码以使用单一的队列,在这种情况下,写锁定和读锁定将具有相同的优先级;
如果不混合更新与需要在同一个表中检查许多行的选择,可以进行并行操作;
可以使用LOCK TABLES来提高速度,因为在一个锁定中进行许多更新比没有锁定的更新要快得多,另外将表中的内容切分为几个表也可以有所帮助
五. 选择MyISAM
一般而言,使用行级锁定的存储引擎,应看看应用程序做什么并且混合使用什么样的select和update。例如,大多数Web应用程序执行许多select,而很少进行delete,只对key的值进行更新,并且只插入少量行。在MySQL中对于使用表级锁定的存储引擎,表锁定时不会死锁的,这通过总是在一个查询开始时立即请求所有必要的锁定并且总是以同样的顺序锁定表来管理。
可以通过检查table_locks_waited和table_locks_immediate状态变量来分析系统上的表锁定争夺:
mysql> SHOW STATUS LIKE ‘Table%‘; +-----------------------+---------+ | Variable_name | Value | +-----------------------+---------+ | Table_locks_immediate | 1151552 | | Table_locks_waited | 15324 | +-----------------------+---------+
如果数据文件不包含空块(从表的中部删除或更新的行可能导致空洞),INSERT语句不冲突,可以自由为MyISAM表混合并行的INSERT和SELECT语句而不需要锁定,你可以在其它客户正读取MyISAM表的时候插入行,记录总是插入在数据文件的尾部;如果不能同时插入,为了在一个表中进行多次INSERT和SELECT操作,可以在临时表中插入行并且立即用临时表中的记录更新真正的表,这也适合做批量延迟插入:
mysql> LOCK TABLES real_table WRITE, insert_table WRITE; mysql> INSERT INTO real_table SELECT * FROM insert_table; mysql> TRUNCATE TABLE insert_table; mysql> UNLOCK TABLES;
六. 选择InnoDB
InnoDB使用行锁定,可能存在死锁。这是因为,在SQL语句处理期间,InnoDB自动获得行锁定,而不是在事务启动时获得。对于InnoDB和BDB(BerkeleyDB)表,如果你用LOCK TABLES显式锁定表,MySQL只使用表锁定,建议不要使用LOCK TABLES,因为InnoDB使用自动行级锁定而BDB使用页级锁定来保证事务隔离。
InnoDB支持外键约束。
一般说来,以多读为主也附带少量写首选MyISAM,否则选择InnoDB或其他引擎会更佳。
七. 悲观锁与乐观锁
悲观锁和乐观锁不是数据库中的标准概念,而只是一种通俗说法。
LOCK TABLES a WRITE; INSERT INTO a VALUES (1,23),(2,34),(4,33); INSERT INTO a VALUES (8,26),(6,29); UNLOCK TABLES;
锁定表可以加速用多个语句执行的INSERT操作,因为索引缓存区仅在所有INSERT语句完成后刷新到磁盘上一次。一般有多少INSERT语句即有多少索引缓存区刷新,如果能用一个语句插入所有的行,就不需要锁定;对于事务表,应使用BEGIN和COMMIT代替LOCK TABLES来加快插入
首 步:执行一次查询 select some_column as old_value from some_table where id = id_value (假设该值在当前业务处理过程中不会被其他并发事务修改)
... 第n步:old_value参与中间业务处理,比如old_value被自己修改 new_value = f(old_value)。这期间可能耗时很长,但不会为持有 some_column 而申请所在的行或表锁定,因此其他并发事务可以获得该锁
... 尾 步:执行条件更新 update some_table set some_column = new_value where id = id_value and some_column = old_value (条件更新中检查old_value是否被修改)
标签:脏读 需要 ble 支持 sam 隔离 show alt mic
原文地址:http://www.cnblogs.com/lxh1197412986/p/6752994.html