MySQL的八股文自述
1.1 讲讲MVCC
MVCC(多版本并发控制):多版本控制—锁机制可以控制并发操作,但是系统开销较大,而MVCC可在大多数情况下替代行锁,使用MVCC 能降低其系统开销。
MVCC 是通过保存数据在某一个时间点的快照来实现的,不同存储引擎的MVCC 是不同的。InnoDB 的 MVCC 是通过在每行记录后面保存两个隐藏的列来实现的,这两个列,分别保存了这个行的创建时间,以及这个行的删除时间。这里存储的并不是真正的实际时间,而是系统的版本号。
MVCC 只适用于MySQL隔离级别的 读已提交 和 可重复读。
MVCC 的创建版本和删除版本只在事务提交后才会产生。
在InnoDB引擎中,使用MVCC(多版本并发控制)。InnoDB为每行记录添加了一个版本号(系统版本号),每当修改数据时,版本号加一。在读取事务开始时,系统会给事务一个当前版本号,事务会读取版本号<=当前版本号的数据,这时就算另一个事务插入一个数据,并立马提交,新插入这条数据的版本号会比读取事务的版本号高,因此读取事务读的数据还是不会变。
1.2 为什么MySQL的索引采用b+树,为什么不采用其他的数据结构呢?
要跟面试官回答该问题,就需要先跟面试官讲讲能够作为MySQL索引的结构有哪些。
参考文章:为什么 MySQL索引要用 B+tree
以下就是我的回答:
我们知道,**索引的常用结构有:**二叉树、红黑树、Hash表、B-Tree、B+Tree 这几种。
1)为什么不采用二叉树呢:
原因:因为假设此刻用普通二叉树记录id系列,我们在每插入一行记录的同时还要维护二叉树索引字段。如果此时找id = 7这一行记录需要找7次,这跟扫描全表也没有什么大的区别。显而易见,二叉树对于这种依次递增的数据列,其实是不适合作为索引的数据结构。
2)为什么不采用Hash表呢:
因为Hash索引不适用于范围查找。
3)为什么不采用红黑树呢:
因为当MySQL数据量很大的时候,索引的体积也会很大,可能内存放不下,所以需要从磁盘上进行相关读写,如果树的层级太高了,则读写磁盘的次数(I/O的交互)就会越多,性能就会越差。红黑树目前唯一的不足点就是它的树的高度是不可控的。
4)那为什么要用B+树,而不用B树呢:
B树 只适合随机检索,而B+树 同时支持随机检索和顺序检索;
B+树 空间利用率更高,可减少I/O次数,磁盘读写代价更低。 一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘I/O消耗。B+树 的内部结点并没有指向关键字具体信息的指针,只是作为索引使用,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素;
B+树的查询效率更加稳定。B树 搜索有可能会在非叶子结点结束,越靠近根节点的记录查找时间越短,只要找到关键字即可确定记录的存在,其性能等价于在关键字全集内做一次二分查找。而在B+树 中,顺序检索比较明显,随机检索时,任何关键字的查找都必须走一条从根节点到叶节点的路,所有关键字的查找路径长度相同,导致每一个关键字的查询效率相当;
B-树 在提高了磁盘IO性能的同时并没有解决元素遍历的效率低下的问题。B+树 的叶子节点使用指针顺序连接在一起,只要遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树 不支持这样的操作;
增删文件(节点)时,效率更高。因为B+树 的叶子节点包含所有关键字,并以有序的链表结构存储,这样可很好提高增删效率。
总结:
① B树 的单个结点存储的元素比B+树多,自然在整个过程中的磁盘I/O交互就会比 B+树 多,增加了性能开销;
② 相对B-tree来说,B+树所有的查询最终都会在叶子结点上,这也是B+树性能稳定的原因的一个体现;
③ B+树所有的叶子结点都是通过双向链表相连,范围查询非常方便,这也是B+树最明显的优势。
1.3 建立索引的时候应该注意什么?
适合索引的列是出现在where子句中的列,或者连接子句中指定的列
较频繁作为查询条件的字段才去创建索引
应该在非空字段(not null)的列上建立索引
定义有外键的数据列一定要建立索引
使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
基数较小的类,索引效果较差,没有必要在此列建立索引
不要过度索引。索引需要额外的磁盘空间,并降低写操作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
不能有效地区别数据的列不适合做索引,如性别
更新比较频繁字段不适合创建索引
1.4 为了避免查找的时候走全表,那应该怎么办呢?
- 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描;
- 应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描;
- 应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描;
- in 和 not in 也要慎用,否则会导致全表扫描;
- 如果在 where 子句中使用参数,也会导致全表扫描;
- 应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描;
- 不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引;
- 很多时候用 exists 代替 in 是一个好的选择
1.5 一条sql查询语句是如何实现的呢?

1.6 一条sql更新语句是如何执行的呢?
执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
引擎将这行新数据更新到内存中,同时将这个更新操作记录到 redo log 里面,此时 redo log 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log 改成**提交(commit)**状态,更新完成。

1.7 sql语句执行的速度很慢,原因是什么呢?
可以参考:腾讯面试:一条SQL语句执行得很慢的原因有哪些?—不看后悔系列
一个 SQL 执行的很慢,我们要分两种情况讨论:
1、大多数情况下很正常,偶尔很慢,则有如下原因:
(1)、数据库在刷新脏页,例如 redo log 写满了需要同步到磁盘。
(2)、执行的时候,遇到锁,如表锁、行锁。
2、这条 SQL 语句一直执行的很慢,则有如下原因:
(1)、没有用上索引:例如该字段没有索引;由于对字段进行运算、函数操作导致无法用索引。
(2)、数据库选错了索引。
1.8 MyISAM 和 InnoDB使用的锁有哪些呢?以及他们之间的差别?
- MyISAM和InnoDB存储引擎使⽤的锁:
MyISAM采⽤表级锁 (table-level locking)。
InnoDB⽀持⾏级锁(row-level locking)和表级锁,默认为⾏级锁
- 表级锁和⾏级锁对⽐:
①表级锁: MySQL中锁定 粒度最⼤ 的⼀种锁,对当前操作的整张表加锁,实现简单,资源消
耗也⽐较少,加锁快,不会出现死锁。其锁定粒度最⼤,触发锁冲突的概率最⾼,并发度最
低, MyISAM和 InnoDB引擎都⽀持表级锁。②⾏级锁: MySQL中锁定 粒度最⼩ 的⼀种锁,只针对当前操作的⾏进⾏加锁。 ⾏级锁能⼤
⼤减少数据库操作的冲突。其加锁粒度最⼩,并发度⾼,但加锁的开销也最⼤,加锁慢,会
出现死锁。InnoDB的行级锁是基于索引实现的,如果查询语句没命中任何索引,那么InnoDB会使用表级锁。
不同于MyISAM总是一次性获得所需的全部锁,InnoDB的锁是逐步获得的,当两个事务都需要获得对方持有的锁,导致双方都在等待,这就产生了死锁。
避免死锁:
- 通过表级锁来减少死锁产生的概率;
- 多个程序尽量约定以相同的顺序访问表(这也是解决并发理论中哲学家就餐问题的一种思路);
- 同一个事务尽可能做到一次锁定所需要的所有资源。
1.9 hash索引一般用在什么场景下呢?hash索引在硬盘上建立了索引了吗?hash索引的key是什么呢?
**底层数据结构:**哈希表
**应用的场景:**在绝大多数需求为单条记录查询的时候,可以选择hash索引,这样查询性能最快。
一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储在磁盘上。
**hash索引的key:**hash code
2.0 数据库中的日志你了解多少?
redo log(重做日志):
当有一条记录需要更新,InnoDB 引擎就会把记录写到 redo log 里面,并更新内存。这个时间更新就算完成了。同时,InnoDB 引擎会在适当的时候,将这个操作记录更新到磁盘里面。有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力成为 crash safe。redo log是指回放日志的时候把commit的事务重做一遍,对于没有commit的事务按照abort 处理,不进行任何操作。
bin log(归档日志):
bin log日志只能用于归档,没有 crash safe 能力。
这两种日志之间的区别:
① redo log 是InnoDB引擎持有的;bin log是MySQL的server 层实现的,所有的引擎都可以使用。
② redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;bin log是逻辑日志,记录的是这个语句的原始逻辑,比如“给ID = 2这一行的 c 字段加1”。
③ redo log 是循环写的,空间固定会用完;bin log是可以追加写的。“追加写”是指bin log 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。
undo log(回滚日志):
undo log 是把所有没有commit的事务回滚到事务开始前的状态,系统崩溃时,可能有些事务还没有commit,在系统恢复时,这些没有commit的事务就需要借助 undo log 来进行回滚。
2.1 char 和 varchar 的最长长度是多少?以及他们之间的区别?
varchar与char的区别:
char的特点:
- char表示定长字符串,长度是固定的;
- 如果插入数据的长度小于char的固定长度时,则用空格填充;
- 因为长度固定,所以存取速度要比varchar快很多,甚至能快50%,但正因为其长度固定,所以会占据多余的空间,是空间换时间的做法;
- 对于char来说,最多能存放的字符个数为255,和编码无关
varchar的特点:
- varchar表示可变长字符串,长度是可变的;
- 插入的数据是多长,就按照多长来存储;
- varchar在存取方面与char相反,它存取慢,因为长度不固定,但正因如此,不占据多余的空间,是时间换空间的做法;
- 对于varchar来说,最多能存放的字符个数为65532。
总之,结合性能角度(char更快) 和 节省磁盘空间角度(varchar更小),具体情况还需具体来设计数据库才是妥当的做法。
2.2 字符集你了解多少?
2.3 需要查看sql语句的执行计划用什么命令呢?
explain
2.4 你从工程的角度看,如果你要去开发数据库需要去注意哪些地方呢?
数据库基本设计规范:
① 没有特殊要求(即 InnoDB 无法满足的功能如:列存储,存储空间数据等)的情况下,所有表必须使用InnoDB存储引擎。MySQL 5.6 以后默认的为 InnoDB 支持事务,支持行级锁,更好的恢复性,高并发下性能更好。
② 数据库和表的字符集统一使用 UTF8MB4。兼容性更好,统一字符集可以避免由于字符集转换产生的乱码,不同的字符集进行比较前需要进行转换会造成索引失效。
③ 所有表和字段都需要添加注释。
④ 尽量控制单表数据量的大小,建议控制在 500 万以内。
⑤ 谨慎使用MySQL 分区表。
分区表在物理上表现为多个文件,在逻辑上表现为一个表,谨慎选择分区键,跨分区查询效率可能更低,建议采用物理分表的方式管理大数据。
⑥ 尽量做到冷热数据分离,减小表的宽度。
MySQL 限制每个表最多存储 4096 列,并且每一行数据的大小不能超过 65535 字节 减少磁盘 IO,保证热数据的内存缓存命中率
(表越宽,把表装载进内存缓冲池时所占用的内存也就越大,也会消耗更多的 IO)
更有效的利用缓存,避免读入无用的冷数据 经常一起使用的列放到一个表中(避免更多的关联操作)
⑦ 禁止在表中建立预留字段。
⑧ 禁止在数据库中存储图片,文件等大的二进制数据。
⑨ 禁止在线上做数据库压力测试。
⑩ 禁止从开发环境,测试环境直接连接生成环境数据库。
2.5 你设计表的时候,数据字段有什么需要注意的地方吗?
数据库字段设计规范:
① 优先选择符合存储需要的最小的数据类型。
列的字段越大,建立索引时所需要的空间也就越大,这样一页中所能存储的索引节点的数量也就越少也越少,在遍历时所需要的 IO 次数也就越多, 索引的性能也就越差。
② 避免使用 TEXT、BLOB 数据类型,最常见的 TEXT 类型可以存储 64k 的数据。
③ 避免使用 ENUM 类型。
④ 尽可能把所有列定义为 NOT NULL。
⑤ 使用 TIMESTAMP(4 个字节)或 DATETIME 类型(8 个字节)存储时间。
⑥ 同财务相关的金额类数据必须使用 decimal 类型。
2.6 幻读是什么,数据库是如何处理幻读的?以及幻读和不可重复读有什么区别?
幻读:
它发⽣在⼀个事务(T1)读取了⼏⾏数据,接着另⼀个并发事务(T2)插⼊了⼀些数据时。在随后的查询中,第⼀个事务(T1)就会发现多了⼀些原本不存在的记录,就好像发⽣了幻觉⼀样,所以称为幻读。
如何处理幻读:
产生幻读的原因是,行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。
幻读和不可重复读的区别:
不可重复读的重点是修改⽐如多次读取⼀条记录发现其中某些列的值被修改,幻读的重点在于新
增或者删除⽐如多次读取⼀条记录发现记录增多或减少了。
2.7 索引是不是建的越多越好,索引的性能开销主要体现在什么时候?
索引并不是建的越多越好!
**性能开销主要体现在:**因为索引需要空间来存储的,也需要定期维护的。每当有记录在表中增减或索引列被修改时,索引本身也会被修改。这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。
2.8 什么是联合索引?为什么要使用联合索引?使用时应该注意什么?
参考:【数据库基础】联合索引是什么?为什么要用它?用它时要注意什么?
1)联合索引是:
对多个字段同时建立的索引(有顺序,ABC,ACB是完全不同的两种联合索引。)
2)为什么要使用联合索引:
以联合索引(a,b,c)为例
① 建立这样的索引相当于建立了索引a、ab、abc三个索引。一个索引顶三个索引当然是好事,毕竟每多一个索引,都会增加写操作的开销和磁盘空间的开销。
② 覆盖(动词)索引。同样的有联合索引(a,b,c),如果有如下的sql: select a,b,c from table where a=xxx and b = xxx。那么MySQL可以直接通过遍历索引取得数据,而无需读表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。
**③ 索引列越多,通过索引筛选出的数据越少。**有1000W条数据的表,有如下sql:select * from table where a = 1 and b =2 and c = 3,假设每个条件可以筛选出10%的数据,如果只有单值索引,那么通过该索引能筛选出1000W*10%=100w 条数据,然后再回表从100w条数据中找到符合b=2 and c= 3的数据,然后再排序,再分页;如果是复合索引,通过索引筛选出1000w *10% *10% *10%=1w,然后再排序、分页,哪个更高效,一眼便知。
3)使用时应该注意什么:
**① 单个索引需要注意的事项,组合索引全部通用。**比如索引列不要参与计算啊、or的两侧要么都索引列,要么都不是索引列啊、模糊匹配的时候%不要在头部啦等等
② 最左匹配原则。(A,B,C) 这样3列,mysql会首先匹配A,然后再B,C.如果用(B,C)这样的数据来检索的话,就会找不到A使得索引失效。如果使用(A,C)这样的数据来检索的话,就会先找到所有A的值然后匹配C,此时联合索引是失效的。
③ 把最常用的,筛选数据最多的字段放在左侧。
2.9 数据库的优化方法有什么?或者说数据库调优的方法有哪些?
面试中,如果被问到如何去优化数据库,就可以从软优化和硬优化的两个方面去作答。软优化可以简单地从查询sql语句的优化、使用索引、增加中间表三个方面来说;硬优化可以从硬件三件套(cpu、内存、磁盘)、参数设置、以及分库分表 + 读写分离来说。
软优化:
① sql查询语句的优化:
**1、覆盖索引:**如果在我们建立的索引上就已经有我们需要的字段,就不需要回表了。可以直接通过遍历索引取得数据,而无需读表,这减少了很多的随机io操作。
**2、联合索引:以联合索引(a,b,c)为例,建立这样的索引相当于建立了索引a、ab、abc三个索引。一个索引顶三个索引当然是好事,毕竟每多一个索引,都会增加写操作的开销和磁盘空间的开销。
**3、最左匹配原则:**建立索引的时候应该遵循最左匹配原则。
**4、索引下推:**可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。
**5、使用短索引:**短索引不仅可以提高查询速度而且可以节省磁盘空间和I/O操作。
**6、不要在列上进行运算:**将在每个行上进行运算,这将导致索引失效而进行全表扫描。
**7、不使用NOT IN和<>操作:**NOT IN和<>操作都不会使用索引将进行全表扫描。
**8、索引不会包含有NULL值的列:**只要列中包含有NULL值都将不会被包含在索引中,复合索引中只要有一列含有NULL值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为NULL。
**② 增加冗余字段:**类似于创建中间表,增加冗余也是为了减少连接查询。
**③ 增加中间表:**对于将大量连接查询的表可以创建中间表,从而减少在查询时造成的连接耗时。
④ 设计数据库结构的时候,尽可能遵守三范式。
硬件优化:
① 硬件三件套(cpu、内存、磁盘):
- 配置多核心和频率高的cpu,多核心可以执行多个线程;
- 配置大内存,提高内存,即可提高缓存区容量,因此能减少磁盘I/O时间,从而提高响应速度;
- 配置高速磁盘或合理分布磁盘:高速磁盘提高I/O,分布磁盘能提高并行操作的能力.
② 参数设置:
优化数据库参数可以提高资源利用率,从而提高MySQL服务器性能。比如key_buffer_size(索引缓冲区大小)。
**③ 分库分表 + 读写分离: **
因为数据库压力过大,首先一个问题就是高峰期系统性能可能会降低,因为数据库负载过高对性能会有影响。另外一个,压力过大把你的数据库给搞挂了怎么办?所以此时你必须得对系统做分库分表 + 读写分离,也就是把一个库拆分为多个库,部署在多个数据库服务上,这时作为主库承载写入请求。然后每个主库都挂载至少一个从库,由从库来承载读请求。
3.0 数据库的三范式
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。
3.1 MySQL 如何查看sql语句的执行时间的
1、 show profiles;
2、 show variables;查看**profiling **是否是on状态;
3 、如果是off,则 set profiling = 1;
4 、执行自己的sql语句;
5 、show profiles;就可以查到sql语句的执行时间;
3.2 什么是MySQL 中的存储过程
**什么是存储过程呢:**简单的说,就是一组SQL语句集,功能强大,可以实现一些比较复杂的逻辑功能,类似于JAVA语言中的方法;
有哪些特性呢:
① 有输入输出参数,可以声明变量,有if/else, case,while等控制语句,通过编写存储过程,可以实现复杂的逻辑功能;
② 函数的普遍特性:模块化,封装,代码复用;
③ 速度快,只有首次执行需经过编译和优化步骤,后续被调用可以直接执行,省去以上步骤;
3.3 sql慢查询问题
1)开启mysql慢查询:
**方式一:**修改配置文件 在 my.ini 增加几行: 主要是慢查询的定义时间(超过2秒就是慢查询),以及慢查询log日志记录( slow_query_log)。
**方法二:**通过MySQL数据库开启慢查询:
2)分析慢查询日志:
直接分析mysql慢查询日志 ,利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句。
3)慢查询的优化:
(1)索引没起作用的情况:
① 使用LIKE关键字的查询语句
在使用LIKE关键字进行查询的查询语句中,如果匹配字符串的第一个字符为“%”,索引不会起作用。只有“%”不在第一个位置索引才会起作用。
② 使用多列索引的查询语句
MySQL可以为多个字段创建索引。一个索引最多可以包括16个字段。对于多列索引,只有查询条件使用了这些字段中的第一个字段时,索引才会被使用。
③ where子句中字段对null值进行了判断,或者使用了!= 或者<>操作符,或者使用了or来连结条件,或者是子句中使用了参数,或者子句中进行了函数的操作
④ 对in 或者 not in 的不慎使用
(2)优化数据库结构:
合理的数据库结构不仅可以使数据库占用更小的磁盘空间,而且能够使查询速度更快。数据库结构的设计,需要考虑数据冗余、查询和更新的速度、字段的数据类型是否合理等多方面的内容。
1. 将字段很多的表分解成多个表
对于字段比较多的表,如果有些字段的使用频率很低,可以将这些字段分离出来形成新表。因为当一个表的数据量很大时,会由于使用频率低的字段的存在而变慢。
2. 增加中间表
对于需要经常联合查询的表,可以建立中间表以提高查询效率。通过建立中间表,把需要经常联合查询的数据插入到中间表中,然后将原来的联合查询改为对中间表的查询,以此来提高查询效率。
(3)分解关联查询:
将一个大的查询分解为多个小查询是很有必要的。
很多高性能的应用都会对关联查询进行分解,就是可以对每一个表进行一次单表查询,然后将查询结果在应用程序中进行关联,很多场景下这样会更高效
3.4 MySQL是如何保证事务的ACID特性呢?即如何保证原子性?如何保证持久性呢?如何保证隔离性?如何保证一致性?(即ACID是怎么实现的呢?)
其实,这是个好问题,我们有时候去看书的时候,并没有看的这么细的,这个是在面试官的引导下答出来的!
参考博客:数据库事务原子性、一致性是怎样实现的?
数据库的一致性依赖于其他三种特性:原子性,隔离性,持久性
原子性如何保证?
innodb使用的是undo log 回滚日志,这样就可以撤回之前的操作。
持久性如何保证?
使用redo log 重做日志来进行保证的。
MySQL 是先把磁盘上的数据加载到内存当中,在内存当中对数据进行修改,再刷回磁盘上。如果此时宕机,内存中的数据就会丢失。此时,就要去借助redo log来解决持久性的问题和读写IO消耗严重问题。当做数据修改的时候,不仅在内存中操作,还会在redo log中记录此次的操作。当事务提交的时候,会将redo log日志进行刷盘。当数据库宕机重启时会将redo log中的内容恢复到数据库,再根据undo log 和 bin log内容决定回滚数据还是提交数据。
隔离性如何保证?
使用锁机制,保证每个事务能够看到的数据总是一致的,就好像其他事务不存在一样,多个事务并发执行后的状态和它们串行执行后的状态是等价的
两种锁:
- 悲观锁
当前事务设计操作的对象加锁,操作完成后释放给其它对象使用。
提高性能->{各种粒度:数据库/表/行/各种性质:共享/排他/共享意向/排他意向/共享排他意向}
解决死锁->{两阶段协议/死锁检测}
- 乐观锁
不同事物可同时可能到同一对象的不同历史版本。使用MVCC(多版本并发控制,Multi-Version Concurrency Control),可在事务提交前查看修改事务的版本,如果与读取版本不一致可放弃提交
如果有两个事务同时修改了同一数据行,那么在较晚的事务提交时进行冲突检测。实现:通过undo log来获取历史版本,另一种简单地在内存中保存同一数据行的多个历史版本,通过时间戳来区分。