1MySQL 基础
1.1 数据库三大范式是什么
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。
在设计数据库结构的时候,要尽量遵守三范式,如果不遵守,必须有足够的理由。比如性能。事实上我们经常会为了性能而妥协数据库的设计。
1.2 MySQL存储引擎MyISAM与InnoDB区别
存储引擎Storage engine:MySQL中的数据、索引以及其他对象是如何存储的,是一套文件系统的实现。
常用的存储引擎有以下:
- Innodb引擎:Innodb引擎提供了对数据库ACID事务的支持。并且还提供了行级锁和外键的约束。它的设计的目标就是处理大数据容量的数据库系统。
- MyIASM引擎(原本Mysql的默认引擎):不提供事务的支持,也不支持行级锁和外键。
- MEMORY引擎:所有的数据都在内存中,数据的处理速度快,但是安全性不高。
MyISAM与InnoDB区别
MyISAM | InnoDB | |
---|---|---|
存储结构 | 每张表被存放在三个文件:frm-表格定义、MYD(MYData)-数据文件、MYI(MYIndex)-索引文件 | 所有的表都保存在同一个数据文件中(也可能是多个文件,或者是独立的表空间文件),InnoDB表的大小只受限于操作系统文件的大小,一般为2GB |
存储空间 | MyISAM可被压缩,存储空间较小 | InnoDB的表需要更多的内存和存储,它会在主内存中建立其专用的缓冲池用于高速缓冲数据和索引 |
可移植性、备份及恢复 | 由于MyISAM的数据是以文件的形式存储,所以在跨平台的数据转移中会很方便。在备份和恢复时可单独针对某个表进行操作 | 免费的方案可以是拷贝数据文件、备份 binlog,或者用 mysqldump,在数据量达到几十G的时候就相对痛苦了 |
文件格式 | 数据和索引是分别存储的,数据.MYD ,索引.MYI
|
数据和索引是集中存储的,.ibd
|
记录存储顺序 | 按记录插入顺序保存 | 按主键大小有序插入 |
外键 | 不支持 | 支持 |
事务 | 不支持 | 支持 |
锁支持(锁是避免资源争用的一个机制,MySQL锁对用户几乎是透明的) | 表级锁定 | 行级锁定、表级锁定,锁定力度小并发能力高 |
SELECT | MyISAM更优 | |
INSERT、UPDATE、DELETE | InnoDB更优 | |
select count(*) | myisam更快,因为myisam内部维护了一个计数器,可以直接调取。 | |
索引的实现方式 | B+树索引,myisam 是堆表 | B+树索引,Innodb 是索引组织表 |
哈希索引 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
1.3 MyISAM索引与InnoDB索引的区别?
- InnoDB索引是聚簇索引,MyISAM索引是非聚簇索引。
- InnoDB的主键索引的叶子节点存储着行数据,因此主键索引非常高效。
- MyISAM索引的叶子节点存储的是行数据地址,需要再寻址一次才能得到数据。
- InnoDB非主键索引的叶子节点存储的是主键和其他带索引的列数据,因此查询时做到覆盖索引会非常高效。
2 索引
2.1 什么是索引?
索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
索引是一种数据结构。数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询、更新数据库表中数据。索引的实现通常使用B树及其变种B+树。
更通俗的说,索引就相当于目录。为了方便查找书中的内容,通过对内容建立索引形成目录。索引是一个文件,它是要占据物理空间的。
2.2 索引有哪些优缺点?
索引的优点
-
可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
-
通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
索引的缺点
- 时间方面:创建索引和维护索引要耗费时间,具体地,当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,会降低增/改/删的执行效率;
- 空间方面:索引需要占物理空间。
2.3 索引有哪些种类
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对于表中主键创建的索引 | 默认自动创建, 只能有一个 | PRIMARY |
唯一索引 | 避免同一个表中某数据列中的值重复 | 可以有多个 | UNIQUE |
常规索引 | 快速定位特定数据 | 可以有多个 | |
全文索引 | 全文索引查找的是文本中的关键词,而不是比较索引中的值 | 可以有多个 | FULLTEXT |
聚集索引(Clustered Index) | 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 | (从底层看) |
二级索引(Secondary Index) | 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 | (从底层看) |
2.4 索引使用场景(重点)
在使用索引之前,判断是否适合使用索引。索引是有在多查询、少增删的场景下。因为增删数据会让MySQL调整B+树,从而增加开销。
判断是否索引如下:
- 先使用
show global status like 'Com_'
可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次; - 再使用慢日志查询,开启慢日志
slow_query_log=1;
并设置多少秒之后算慢查询long_query_time=2;
查看慢日志文件 - 然后
show profiles
能够在做SQL优化时帮助我们了解时间都耗费到哪里去了 - 使用
explain
分析SQL语句
2.5 索引为什么不使用B树和hash方式,而使用B+树
(1)对于hash数据结构的优点是可以快速查询到数据,但是缺点是不能进行范围查询。
- hash索引不支持模糊查询以及多列索引的最左前缀匹配。原理也是因为hash函数的不可预测。AAAA和AAAAB的索引没有相关性。
- hash索引不支持使用索引进行排序,原理同上
- hash索引任何时候都避免不了回表查询数据,而B+树在符合某些条件(聚簇索引,覆盖索引等)的时候可以只通过索引完成查询。
- hash索引虽然在等值查询上较快,但是不稳定。性能不可预测,当某个键值存在大量重复的时候,发生hash碰撞,此时效率可能极差。而B+树的查询效率比较稳定,对于所有的查询都是从根节点到叶子节点,且树的高度较低。
(2)对于B树,将数据是存放在每一个结点的,所以每个结点比较大。那么对于有固定大小的页来说,一页存不下多少数据,就要花资源去记录下一页数据,分页查询较多,查询效率不高
(3)对于B+树来说,所有的数据都会出现在叶子节点。叶子节点形成一个单向链表。非叶子节点仅仅起到索引数据作用,具体的数据都是在叶子节点存放的。带有顺序指针的B+Tree,提高区间访问的性能,利于排序。同时每个结点只是索引,一页可以存放更多的索引,分页查询更少,效率更高。
假设:
一行数据大小为1k,一页中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键即使为bigint,占用字节数为8。
高度为2:
n * 8 + (n + 1) * 6 = 16*1024 , 算出n约为 1170
1171* 16 = 18736
也就是说,如果树的高度为2,则可以存储 18000 多条记录。
高度为3:
1171 * 1171 * 16 = 21939856
也就是说,如果树的高度为3,则可以存储 2200w 左右的记录。
2.6 索引设计的原则?
- 针对于数据量较大,且查询比较频繁的表建立索引。
- 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长。所以只保持需要的索引有利于查询即可。
2.7 创建索引时需要注意什么?
- 非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值;
- 取值离散大的字段:(变量各个取值之间的差异程度)的列放到联合索引的前面,可以通过count()函数查看字段的差异值,返回值越大说明字段的唯一值越多字段的离散程度高;
- 索引字段越小越好:数据库的数据存储以页为单位一页存储的数据越多一次IO操作获取的数据越大效率越高(比如可以使用前置索引)。
2.8 使用索引查询一定能提高查询的性能吗?为什么
通常,通过索引查询数据比全表扫描要快。但是我们也必须注意到它的代价。
- 索引需要空间来存储,也需要定期维护, 每当有记录在表中增减或索引列被修改时,索引本身也会被修改。 这意味着每条记录的INSERT,DELETE,UPDATE将为此多付出4,5 次的磁盘I/O。 因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢。使用索引查询不一定能提高查询性能,索引范围查询(INDEX RANGE SCAN)适用于两种情况:
- 基于一个范围的检索,一般查询返回结果集小于表中记录数的30%
- 基于非唯一性索引的检索
2.9 百万级别或以上的数据如何加载和删除
加载:
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n'
删除:在我们删除数据库百万级别数据的时候,查询MySQL官方手册得知删除数据的速度和创建的索引数量是成正比的。
- 所以我们想要删除百万数据的时候可以先删除索引(此时大概耗时三分多钟)
- 然后删除其中无用数据(此过程需要不到两分钟)
- 删除完成后重新创建索引(此时数据较少了)创建索引也非常快,约十分钟左右。
- 与之前的直接删除绝对是要快速很多,更别说万一删除中断,一切删除会回滚。那更是坑了。
2.10 什么是最左前缀原则?什么是最左匹配原则
在 user 表中,有一个联合索引,这个联合索引涉及到三个字段,顺序分别为:profession,age,status。
对于最左前缀法则指的是,查询时,最左变的列,也就是profession必须存在,否则索引全部失效。而且中间不能跳过某一列,否则该列后面的字段索引将失效。
最左前缀法则中指的最左边的列,是指在查询时,联合索引的最左边的字段(即是第一个字段)必须存在,与我们编写SQL时,条件编写的先后顺序无关。
2.11 索引失效的情况
- 不符合最左匹配原则,但是where语句中的顺序可以和所以不一致
- 使用范围查找,比如>,<不可以使用索引;但是 >= 和 <= 可以使用索引
- 索引列运算
- 字符串不加引号
- 模糊查询,但是这种情况是可以的
like '软件%'
- or连接条件
- 数据分布影响
2.12 B+树在满足聚簇索引和覆盖索引的时候不需要回表查询数据,
select * from user where name = 'Arm'; --回表查询
select id,name from user where name = 'Arm'; --不需要回表查询
2.13 什么是聚簇索引?何时使用聚簇索引与非聚簇索引
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,当需要访问数据时(通过索引访问数据),在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因
澄清一个概念:innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值
何时使用聚簇索引与非聚簇索引
2.14 非聚簇索引一定会回表查询吗?
不一定,这涉及到查询语句所要求的字段是否全部命中了索引,如果全部命中了索引,那么就不必再进行回表查询。
举个简单的例子,假设我们在员工表的年龄上建立了索引,那么当进行 select age from employee where age < 20
的查询时,在索引的叶子节点上,已经包含了age信息,不会再次进行回表查询。
2.15 如何分析SQL语句(解释使用explain)
explain 的属性
字段 | 含义 |
---|---|
id | select查询的序列号,表示查询中执行select子句或者是操作表的顺序 (id相同,执行顺序从上到下;id不同,值越大,越先执行)。 |
select_type | 表示 SELECT 的类型,常见的取值有 SIMPLE(简单表,即不使用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION 中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等 |
type | 表示连接类型,性能由好到差的连接类型为**NULL、system、const(唯一索引)、eq_ref(非唯一索引)、ref、range、 index、all** 。 |
possible_key | 显示可能应用在这张表上的索引,一个或多个。 |
key | 实际使用的索引,如果为NULL,则没有使用索引。 |
key_len | 表示索引中使用的字节数, 该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下, 长度越短越好(索引树的高度) 。 |
rows | MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估计值,可能并不总是准确的。 |
filtered | 表示返回结果的行数占需读取行数的百分比, filtered 的值越大越好。 |
Extra |
Extra
Extra | 含义 |
---|---|
Using where; Using Index | 查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据 |
Using index condition | 查找使用了索引,但是需要回表查询数据 |
Using index | 使用了索引 |
Using filesort | 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。 |
3 事务
3.1 事物的四大特性(ACID)介绍一下?
关系性数据库需要遵循ACID规则,具体内容如下:
- 原子性: 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
- 一致性: 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
- 隔离性: 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
- 持久性: 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
3.2 什么是脏读?幻读?不可重复读?
- 脏读(Drity Read):事务A已更新一份数据,事务B在此时读取了同一份数据,由于某些原因,事务A 进行了RollBack操作,则事务B所读取的数据就会是不正确的。(A一个晃身,骗过了B)
- 不可重复读(Non-repeatable read):事务A的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务B更新的原有的数据。(转个头,数据就了)
- 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有事务A查询了几行 (Row)数据,而事务B却在此时插入了新的几行数据,先前的事务在接下来的查询中,就会发现有几行数据是它先前所没有的。(原来没有的,后来有了)
3.3 什么是事务的隔离级别?MySQL的默认隔离级别是什么?
为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻影读 |
---|---|---|---|
READ-UNCOMMITTED | √ | √ | √ |
READ-COMMITTED | × | √ | √ |
REPEATABLE-READ | × | × | √ |
SERIALIZABLE | × | × | × |
SQL 标准定义了四个隔离级别(是个标准):
- READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。只能保证持久性
- READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。语句级别的。
- REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。事务级别。
- SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVCC(多版本并发控制),通过保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读) 并不会有任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到 SERIALIZABLE(可串行化) 隔离级别。
3.4 什么是MVCC机制?使用了什么思想?作用于那些地方?是如何实现?
MVCC的具体实现,还需要依赖于数据库记录中的三个隐式字段、undo log日志、readView
三个隐式字段
DB_TRX_ID:最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
DB_ROW_ID:隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。
undo log日志
版本链,用于回滚
readView
ReadView(读视图)是 快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务(未提交的)id。
ReadView中包含了四个核心字段:
字段 | 含义 |
---|---|
m_ids | 当前活跃的事务ID集合 |
min_trx_id | 最小活跃事务ID |
max_trx_id | 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的) |
creator_trx_id | ReadView创建者的事务ID |
而在readview中就规定了版本链数据的访问规则:trx_id 代表当前undolog版本链对应事务ID。
条件 | 是否可以访问 | 说明 |
---|---|---|
trx_id ==creator_trx_id | 可以访问该版本 | 成立,说明数据是当前这个事务更改的。 |
trx_id < min_trx_id | 可以访问该版本 | 成立,说明数据已经提交了。 |
trx_id > max_trx_id | 不可以访问该版本 | 成立,说明该事务是在ReadView生成后才开启。 |
min_trx_id <= trx_id<= max_trx_id | 如果trx_id不在m_ids中,是可以访问该版本的 | 成立,说明数据已经提交 |
3.5 四种隔离级别的实现
3.5.1 读未提交
使用当前读。在此隔离级别下,事务之间可以读取彼此未提交的数据。但注意在所有写操作执行时都会加排它锁,那还怎么读未提交呢?
该级别主要的特点是释放锁的时机与众不同:在执行完写操作后立即释放,而不像其他隔离级别在事务提交以后释放。因此极易出现脏读(不可重复读和幻读就更不用说了)
但该级别的并发性能也正因为锁释放得很早而变得很高,就连写写操作都很难产生锁竞争。
3.5.2 读提交
使用快照读。读未提交有很大的数据可靠性问题,所以再往前一步,我们就得到了读提交。
如何解决脏读:实现了快照读,每一次select都会产生一个新的数据快照,实现读提交的第一个要点就是将锁的释放时机延迟到事务提交之后,从而可以实现读提交,解决了脏读。
对并发有较大的影响:但是,锁的释放时机延迟了,不仅写与写操作之间会产生锁竞争,在锁释放之前,也无法执行读操作,这对并发性产生了很大的影响。为了提高并发性,MySQL采用了一种名为MVCC的解决方案,MVCC可以解决这样的问题:既然不想阻塞等待最新的数据,那就无视当前持有锁的事务,读取最新的历史版本数据。
因此,在读已提交的级别下,我们每次执行select操作时都会通过MVCC获取当前数据的最新快照,不加任何锁,也无视任何锁(因为历史数据是构造出来的,身上不可能有锁),完美解决读写之间的并发问题,和读未提交的并发性能只差在写写操作上。
而为了进一步提升写写操作上的并发性能,该级别下不会使用间隙锁,无论什么查询都只会加行锁,而且在执行完WHERE条件筛选之后,会立即释放掉不符合条件的行锁。
但是,正因为对并发性能的极致追求或者说贪婪,该级别下还是遗留了不可重复读和幻读问题:
为什么不能解决不可重复读:因为每次select时,这就意味着,如果我们在事务A中执行多次的select,在每次select之间有其他事务更新了我们读取的数据并提交了,那就 出现了不可重复读
锁的范围: 因为没有间隙锁,这就意味着,如果我们在事务A中多次执行 select * from user where age>18 and age<30 for update
时,其他事务是可以往 age为(18,30) 这个区间插入/删除数据的,那就 出现了幻读
3.5.3 可重复读
使用快照读**。且只有第一次select才形成快照读**。既然读提交依然有较大的数据可靠性能问题,那我们可以进一步进行限制,这样就得到了可重复读,该级别在读提交的基础上做了两点修改,从而避免了不可重复读和幻读:
如何解决脏读:一开始的修改其实是对快照的修改,即使sessionA回滚了,也只是快照回滚,跟数据库的数据没什么关系。所以,sessionB查询数据库中数据的时候不用担心脏读问题,解决了脏读。
如何解决不可重复读:一次事务中只在第一次select时生成版本,后续的查询都是在这个版本上进行,那么下次使用相同语句查询时,访问的快照仍然是第一次产生的数据快照。那么即使当session1提交(commit)事务之后,session2访问的仍然是事务提交之前的即数据处于prepare状态的数据快照。这就解决了不可重复读的问题。
为什么解决不了幻读: 锁的范围: 在行锁的基础上,加上Gap Lock(间隙锁),从而形成Next-Key Lock,在所有遍历过的(不管是否匹配条件)索引行上以及之间的区域上,都加上锁,阻塞其他事务在遍历范围内进行写操作,从而避免了幻读。尽管InnoDB在可重复读级别下已经将数据可靠性和并发性能两方面做得尽善尽美了,但前提是用户查询时能够主动使用Locking Reads,即select … lock in share mode(共享锁)和select … for update(排它锁)。如果只是使用普通的select,依然防不住幻读。这是因为MVCC的 快照只对读操作 有效,对写操作无效。
举例说明会更清晰一点: 事务A依次执行如下3条sql,事务B在语句1和2之间,插入10条age=20的记录,事务A就幻读了:
select count(1) from user where age=20;
-- return 0: 当前没有age=20的
update user set name=test where age=20;
-- Affects 10 rows: 因为事务B刚写入10条age=20的记录,而写操作是不受MVCC影响,能看到最新数据的,所以更新成功,而一旦操作成功,这些被操作的数据就会对当前事务可见
select count(1) from user where age=20;
-- return 10: 出现幻读
这种场景,需要用户主动使用Locking Read来防止其他事务在查询范围内进行写操作,因此,为了防患于未然,隔离级别又往前迈了一步
在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)。
3.5.4 串行化
使用当前读。该级别下,会自动将所有普通select转化为 select … lock in share mode
执行,即针对同一数据的所有读写都变成互斥的了,可靠性大大提高,并发性大大降低.
可重复读级别下使用Locking Read也可以变成读写互斥,那这两个有什么区别呢?可重复读我们可以自己选择是否使用Locking Read,更自由,所以在可重复读下我们可以选择使用普通的select读写实现并发。
3.5.5 在这里对介绍的四种隔离级别的实现做一个小结
- “读未提交”隔离级别下直接返回记录上的最新值,没有MVCC视图概念;
- 在“读提交”和“可重复度读“隔离级别下,数据库里面会创建一个MVCC视图,访问的时候以视图的逻辑结果为准
- 在”读提交“级别下,这个MVCC视图是在每个 SQL 语句开始执行的时候创建的,即快照读;
- 在可重复读的隔离级别下,InnoDB每次进行select查询语句,只有第一次执行select语句会产生数据快照(整张表的快照),之后执行相同的select语句,不再产生数据快照,那么下次使用相同语句查询时,访问的快照仍然是第一次产生的数据快照。那么即使当session1提交(commit)事务之后,session2访问的仍然是事务提交之前的即数据处于prepare状态的数据快照。这就解决了不可重复读的问题。
- 而“串行化”隔离级别下直接用加锁的方式来避免并行访问。
3.6 事务的七大传播行为
propagation_required
支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
propagation_supports
支持当前事务,如果当前没有事务,就以非事务方式执行。
propagation_mandatory
支持当前事务,如果当前没有事务,就抛出异常。
propagation_requires_new
新建事务,如果当前存在事务,把当前事务挂起。
propagation_not_supported
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
propagation_never
以非事务方式执行,如果当前存在事务,则抛出异常。
propagation_nested
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务
4 锁
4.1 MySQL中有哪些锁
行锁【共享锁(S)、排他锁(X)】、表锁【表共享读锁(read lock)、表独占写锁(write lock)】、意向锁、元数据锁、间隙锁、临键锁
表锁: 顾名思义是对数据库中表上锁,表锁有两种形式,表共享读锁(简称”读锁“),表独占写锁(简称”写锁“)
表共享读锁: 指定表加了读锁,不会影响其他线程的读,但是会阻塞其他线程的写
表独占写锁: 指定表加了写锁,会阻塞其他线程的读和写
页级锁: 页级锁是MyS QL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。
特点:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般
元数据锁MDL:MDL加锁过程是系统自动控制,无需显式使用,在访问一张表的时候会自动加上。MDL锁主要作用是维护表元数据的数据一致性(也就是表结构在此时不能发生改变),在表上有活动事务的时候,不可以对元数据进行写入操作。为了避免DML与DDL冲突,保证读写的正确性。增删改查,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)
意向锁:为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查。在执行DML操作时,会对涉及的行加行锁,同时也会对该表加上意向锁。而其他客户端,在对这张表加表锁的时候,会根据该表上所加的意向锁来判定是否可以成功加表锁,而不用逐行判断行锁情况了。
行锁: 对数据库表中的某一行加锁。分为共享锁和排他锁。增删改都是排他锁,查是共享锁
间隙锁和临键锁:
A.索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁 。(比如表里存在2,5两条数据,查3这条数据,2和5之间就会被锁,同时无法插入4)
B.索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-keylock 退化为间隙锁。 假如,我们要根据这个二级索引查询值为18的数据,并加上共享锁,我们是只锁定18这一行就可以了吗? 并不是,因为是非唯一索引,这个结构中可能有多个18的存在,所以,在加锁时会继续往后找,找到一个不满足条件的值(当前案例中也就是29)。此时会对18加临键锁,并对29之前的间隙加锁
C. 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止。查询的条件为id>=19,并添加共享锁。 此时我们可以根据数据库表中现有的数据,将数据分为三个部分:[19]、(19,25]、(25,+∞]所以数据库数据在加锁是,就是将19加了行锁,25的临键锁(包含25及25之前的间隙),正无穷的临键锁(正无穷及之前的间隙)
4.2 隔离级别与锁的关系
在读未提交(Read Uncommitted)级别下,读取数据不需要加共享锁,这样就不会跟被修改的数据上的排他锁冲突。
在Read Committed级别下,读操作需要加共享锁,但是在语句执行完以后释放共享锁;
在Repeatable Read级别下,读操作需要加行级共享锁,但是在事务提交之前并不释放共享锁,也就是必须等待事务执行完毕以后才释放共享锁。
SERIALIZABLE 是限制性最强的隔离级别,读操作需要加表级共享锁,因为该级别锁定整个范围的键,并一直持有锁,直到事务完成。
5 集群模式
5.1 为什么要使用relay log,slave不可以直接读取binlog嘛
中继日志(relay log)只在主从服务器架构的从服务器上存在。从服务器(slave)为了与主服务器(Master)保持一致,要从主服务器读取二进制日志的内容,并且把读取到的信息写入本地的日志文件中,这个从服务器本地的日志文件就叫中继日志。然后,从服务器读取中继日志,并根据中继日志的内容对从服务器的数据进行更新,完成主从服务器的数据同步。文章来源:https://www.toymoban.com/news/detail-472925.html
中继日志是连接mastert(主服务器)和slave(从服务器)的信息,它是复制的核心,I/O线程将来自master的binlog存储到中继日志中,中继日志充当缓冲,这样master不必等待slave执行完成就可以发送下一个binlog。这样master不必等待slave执行完成就可以发送下一个binlog文章来源地址https://www.toymoban.com/news/detail-472925.html
到了这里,关于Java 面试 | MySQL(2023版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!