暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

熬夜肝了两万字 MySQL 面霸教程!这些问题你能答对多少?

数据与人 2022-12-21
322

点击上方"数据与人"右上角选择“设为星标”

分享干货,共同成长!



今天给大家分享MySQL常考的面试题,看看你们能答对多少。

目录大纲

事务的四大特性?

事务特性ACID原子性Atomicity
)、一致性Consistency
)、隔离性Isolation
)、持久性Durability
)。
具体含义:
  • 原子性(atomicity
    )
    一个事务必须被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作。

  • 一致性(consistency
    )
    数据库总是从一个一致性的状态转换到另外一个一致性的状态。如:拿转账来说,假设用户A和用户B两者的钱加起来一共是1000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是1000,这就是事务的一致性。

  • 隔离性(isolation
    )
    隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
    即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

  • 持久性(durability
    )
    一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必。而且「不可能有能做到100%的持久性保证的策略」否则还需要备份做什么。

事务隔离级别有哪些?

MySQL四种隔离级别是什么,分别解决了什么问题,一张图说清楚:
脏读、不可重复读、幻读具体含义:

脏读:在事务A修改数据之后提交数据之前,这时另一个事务B来读取数据,如果不加控制,事务B读取到A修改过数据,之后A又对数据做了修改再提交,则B读到的数据是脏数据,此过程称为脏读Dirty Read

不可重复读:一个事务内在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了变更、或者某些记录已经被删除了。

幻读:事务A在按查询条件读取某个范围的记录时,事务B又在该范围内插入了新的满足条件的记录,当事务A再次按条件查询记录时,会产生新的满足条件的记录(幻行 Phantom Row

不可重复读与幻读有什么区别?
  • 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的「数据不一样」。(因为中间有其他事务提交了修改);
  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,第一次和第二次读出来的「记录数不一样」。(因为中间有其他事务提交了插入/删除)。

索引

索引的本质?

MySQL官方对索引的定义为:索引(Index
)是帮助MySQL高效获取数据的数据结构。提取句子主干,就可以得到索引的本质:索引是数据结构

索引分类?

主键索引:名为primary
的唯一非空索引,不允许有空值。
唯一索引:索引列中的值必须是唯一的,但是允许为空值。唯一索引和主键索引的区别是:唯一约束的列可以为null
且可以存在多个null
值。唯一索引的用途:唯一标识数据库表中的每条记录,主要是用来防止数据重复插入。
组合索引:在表中的多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使用,使用组合索引时需遵循最左前缀原则。
全文索引:只有在MyISAM
引擎上才能使用,只能在CHAR
VARCHAR
TEXT
类型字段上使用全文索引。

索引的优缺点?

优点:
  • 提高数据检索的效率,降低数据库的IO
    成本;
  • 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU
    的消耗;
所以记住,索引功能是:搜索+排序
缺点:
  • 索引提升查询效率的同时也会降低更新的效率,更新表时,MySQL不仅要保存数据,还要保存一下索引文件,每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息;
  • 实际上索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录,所以索引列也是要占用空间的。

索引的作用?

数据是存储在磁盘上的,查询数据时,如果没有索引,会加载所有的数据到内存,依次进行检索,读取磁盘次数较多。有了索引,就不需要加载所有数据,因为B+树的高度一般在2-4层,最多只需要读取2-4次磁盘,查询速度大大提升。

索引的使用场景?

  1. 主键自动建立唯一索引;
  2. 频繁作为查询条件的字段应该创建索引;
  3. 查询中与其它表关联的字段,外键关系建立索引;
  4. 单键/组合索引的选择问题, 组合索引性价比更高;
  5. 查询中排序的字段,如order by
    create_time
    ,排序字段若通过索引去访问将大大提高排序速度;
  6. 查询中统计或者分组字段;

索引的失效场景?

  1. 以%开头的LIKE
    查询不能够利用B+树索引

  2. 数据类型中出现隐式转换时不会用到索引

  3. 复合索引的情况下,查询条件不满足最左原则不会用到索引

  4. 用or分隔的条件,如果or前条件中的列有索引,而后面的列没有索引,那么涉及的索引都不会被用到(前面的索引也不会用到)。

  5. 如果Mysql估计使用索引比全表扫描更慢,则不使用索引。

索引的数据结构

MySQL 索引一般是哈希表或 B+ 树,常用的 InnoDB
引擎默认使用的是 B+ 树来作为索引的数据结构。
B+树索引
B+ 树是基于B 树和叶子节点顺序访问指针进行实现,它具有B树的平衡性,并且通过顺序访问指针来提高区间查询的性能。
在 B+ 树中,节点中的 key
 从左到右递增排列,如果某个指针的左右相邻 key
 分别是 keyi 和 keyi+1,则该指针指向节点的所有 key
 大于等于 keyi 且小于等于 keyi+1
进行查找操作时,首先在根节点进行二分查找,找到key
所在的指针,然后递归地在指针所指向的节点进行查找。直到查找到叶子节点,然后在叶子节点上进行二分查找,找出key
所对应的数据项。
MySQL 数据库使用最多的索引类型是BTREE
索引,底层基于B+树数据结构来实现。
mysql> show index from blog\G;
*************************** 1. row ***************************
Table: blog
Non_unique: 0
Key_name: PRIMARY
Seq_in_index: 1
Column_name: blog_id
Collation: A
Cardinality: 4
Sub_part: NULL
Packed: NULL
Null:
Index_type: BTREE
Comment:
Index_comment:
Visible: YES
Expression: NULL
哈希索引
哈希索引是基于哈希表实现的,对于每一行数据,存储引擎会对索引列进行哈希计算得到哈希码,并且哈希算法要尽量保证不同的列值计算出的哈希码值是不同的,将哈希码的值作为哈希表的key值,将指向数据行的指针作为哈希表的value
值。这样查找一个数据的时间复杂度就是O(1),一般多用于精确查找。

Hash索引和B+树索引的区别?

  • 哈希索引不支持排序,因为哈希表是无序的。
  • 哈希索引不支持范围查找
  • 哈希索引不支持模糊查询及多列索引的最左前缀匹配。
  • 因为哈希表中会存在哈希冲突,所以哈希索引的性能是不稳定的,而B+树索引的性能是相对稳定的,每次查询都是从根节点到叶子节点。

为什么B+树比B树更适合实现数据库索引?

B+树是B树的变种,是基于B树来改进的。为什么B+树会比B树更加优秀呢?

B树:有序数组+平衡多叉树; 

B+树:有序数组链表+平衡多叉树;

B+树的关键字全部存放在叶子节点中,非叶子节点用来做索引,而叶子节点中有一个指针指向一下个叶子节点。

B+ 树查找过程:


磁盘块 1 中存储 17 和 35 数据项,还有 P1、P2、P3 指针,P1 表示数据项小于 17 的磁盘块,P2 表示数据项在 17 和 35 之间的数据项,P3 表示数据项大于 35 的数据项。非叶子节点不储存数据,只储存指引搜索方向的数据项。

我们知道每次 IO 读取一个数据页的大小,也就是一个磁盘块。

假设我们要查找 29 这个数据项,首先进行第一次 IO 将磁盘块 1 读进内存,发现17 < 29 < 35,然后选用 P2 指针进行第二次 IO 将磁盘块 3 读进内存,发现26 < 29 < 30,然后选用 P2 指针将磁盘块 8 读进内存,在内存中做二分查找,找到 29,结束查询。

通过分析查询过程,我们可以知道 IO 次数和 B+ 树的高度成正比。H 为树的高度,M 为每个磁盘块的数据项个数,N 为数据项总数。

从下面的公式可以看出如果数据量N一定,M越大相应的H越小。


M 等于磁盘块的大小除以数据项大小,由于磁盘块大小一般是固定的,所以减小数据项大小才能使得 M 更大从而让树更矮胖。这也是为什么 B+ 树把真实数据放在叶子节点而不是非叶子节点的原因。

如果真实数据放在非叶子结点,磁盘块存储的数据项会大幅度减少,树就会增高相应查询数据时的 IO 次数就会变多。

什么是最左匹配原则?

如果 SQL 语句中用到了组合索引中的最左边的索引,那么这条 SQL 语句就可以利用这个组合索引去进行匹配。当遇到范围查询(>
<
between
like
)就会停止匹配,后面的字段不会用到索引。
(a,b,c)
建立索引,查询条件使用 a/ab/abc 会走索引,使用 bc 不会走索引。如果查询条件为a = 1 and b > 2 and c = 3
,那么a、b个字两段能用到索引,而c无法使用索引,因为b字段是范围查询,导致后面的字段无法使用索引。
如下图,对(a, b) 建立索引,a 在索引树中是全局有序的,而 b 是全局无序,局部有序(当a相等时,会根据b进行排序)
当a的值确定的时候,b是有序的。例如a = 1
时,b值为1,2是有序的状态。当执行a = 1 and b = 2
时a和b字段能用到索引。而对于查询条件a < 4 and b = 2
时,a字段能用到索引,b字段则用不到索引。因为a的值此时是一个范围,不是固定的,在这个范围内b的值不是有序的,因此b字段无法使用索引。

什么是聚集索引?

InnoDB使用表的主键构造主键索引树,同时叶子节点中存放的即为整张表的记录数据。聚集索引叶子节点的存储是逻辑上连续的,使用双向链表连接,叶子节点按照主键的顺序排序,因此对于主键的排序查找和范围查找速度比较快。
聚集索引的叶子节点就是整张表的行记录。InnoDB
主键使用的是聚簇索引。聚集索引要比非聚集索引查询效率高很多。
对于InnoDB
来说,聚集索引一般是表中的主键索引,如果表中没有显示指定主键,则会选择表中的第一个不允许为NULL
的唯一索引。如果没有主键也没有合适的唯一索引,那么InnoDB
内部会生成一个隐藏的主键作为聚集索引,这个隐藏的主键长度为6个字节,它的值会随着数据的插入自增。

什么是覆盖索引?

select
的数据列只用从索引中就能够取得,不需要回表进行二次查询,也就是说查询列要被所使用的索引覆盖。对于innodb
表的二级索引,如果索引能覆盖到查询的列,那么就可以避免对主键索引的二次查询。
不是所有类型的索引都可以成为覆盖索引。覆盖索引要存储索引列的值,而哈希索引、全文索引不存储索引列的值,所以MySQL使用b+树索引做覆盖索引。
对于使用了覆盖索引的查询,在查询前面使用explain
,输出的extra列会显示为using index

什么是前缀索引?

有时需要在很长的字符列上创建索引,这会造成索引特别大且慢。使用前缀索引可以避免这个问题。
前缀索引是指对文本或者字符串的前几个字符建立索引,这样索引的长度更短,查询速度更快。
创建前缀索引的关键在于选择足够长的前缀以保证较高的索引选择性。索引选择性越高查询效率就越高,因为选择性高的索引可以让MySQL
在查找时过滤掉更多的数据行。
建立前缀索引的方式:
// email列创建前缀索引
ALTER TABLE table_name ADD KEY(column_name(prefix_length));

索引的设计原则?

  • 索引列的区分度越高,索引的效果越好。比如使用性别这种区分度很低的列作为索引,效果就会很差。
  • 尽量使用短索引,对于较长的字符串进行索引时应该指定一个较短的前缀长度,因为较小的索引涉及到的磁盘I/O
    较少,查询速度更快。
  • 索引不是越多越好,每个索引都需要额外的物理空间,维护也需要花费时间。
  • 利用最左前缀原则

MySQL架构?


整体架构图:


MySQL的架构不同于其他数据库,它的插件式的存储引擎架构可以在多种不同场景中应用并发挥良好作用。这种架构可以根据业务的需求和实际需要选择合适的存储引擎。

各层介绍:

1.1 连接层

最上层是客户端,包含本地sock
通信和大多数基于客户端/服务端工具实现的类似于tcp/ip
的通信。

1.2 服务层


1.3.引擎层

存储引擎负责MySQL中数据的存储和提取,服务器通过API
与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以根据自己的实际需要进行选取。

show engines
:查看所有的数据库引擎

show variables like '%engine%' 
查看默认的数据库引擎

1.4.存储层

数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与存储引擎的交互。

2、查询流程

MySQL的查询流程大致是:

客户端通过协议与DB服务器建连接,发送查询语句,先检查查询缓存,如果命中,直接返回结果,否则进行语句解析。

语法解析器和预处理:首先MySQL通过关键字将SQL语句进行解析,并生成一颗对应的“解析树”。解析器将使用语法规则验证和解析查询;预处理器则根据一些规则进一步检查解析数是否合法。

查询优化器当解析树被认为是合法的了,并且由优化器将其转化成执行计划。一条查询可以有很多种执行方式,最后都返回相同的结果。优化器的作用就是找到这其中最好的执行计划。

流程图:

3、SQL的执行顺序
一般SQL语法:

SQL解析:

真正执行的顺序:

常见的存储引擎有哪些?

MySQL中常用的四种存储引擎分别是:MyISAM
InnoDB
MEMORY
ARCHIVE
。MySQL 5.5版本后默认的存储引擎为InnoDB
InnoDB存储引擎
InnoDB是MySQL默认的事务型存储引擎,使用最广泛,基于聚簇索引建立的。InnoDB内部做了很多优化,如能够自动在内存中创建自适应hash索引,以加速读操作。
优点:支持事务和崩溃修复能力;引入了行级锁和外键约束。
缺点:占用的数据空间相对较大。
适用场景:需要事务支持,并且有较高的并发读写频率。
MyISAM存储引擎
数据以紧密格式存储。对于只读数据,或者表比较小、可以容忍修复操作,可以使用MyISAM引擎。MyISAM会将表存储在两个文件中,数据文件.MYD
和索引文件.MYI
优点:访问速度快。
缺点:MyISAM不支持事务和行级锁,不支持崩溃后的安全恢复,也不支持外键。
适用场景:对事务完整性没有要求;表的数据都会只读的。
MEMORY存储引擎
MEMORY引擎将数据全部放在内存中,访问速度较快,但是一旦系统奔溃的话,数据都会丢失。
MEMORY引擎默认使用哈希索引,将键的哈希值和指向数据行的指针保存在哈希索引中。
优点:访问速度较快。
缺点
  1. 哈希索引数据不是按照索引值顺序存储,无法用于排序。
  2. 不支持部分索引匹配查找,因为哈希索引是使用索引列的全部内容来计算哈希值的。
  3. 只支持等值比较,不支持范围查询。
  4. 当出现哈希冲突时,存储引擎需要遍历链表中所有的行指针,逐行进行比较,直到找到符合条件的行。
ARCHIVE存储引擎
ARCHIVE存储引擎非常适合存储大量独立的、作为历史记录的数据。ARCHIVE提供了压缩功能,拥有高效的插入速度,但是这种引擎不支持索引,所以查询性能较差。

MyISAM和InnoDB的区别?

MVCC 实现原理?

MVCC(Multiversion concurrency control
) 就是同一份数据保留多版本的一种方式,进而实现并发控制。在查询的时候,通过read view
和版本链找到对应版本的数据。
作用:提升并发性能。对于高并发场景,MVCC比行级锁开销更小。
MVCC 实现原理如下:
MVCC 的实现依赖于版本链,版本链是通过表的三个隐藏字段实现。
  • DB_TRX_ID
    :当前事务id,通过事务id的大小判断事务的时间顺序。
  • DB_ROLL_PRT
    :回滚指针,指向当前行记录的上一个版本,通过这个指针将数据的多个版本连接在一起构成undo log
    版本链。
  • DB_ROLL_ID
    :主键,如果数据表没有主键,InnoDB会自动生成主键。

举例说明:

    create table mvcctest( id int primary key auto_increment, name varchar(20));
    transaction 1
    :
      start transaction;insert into mvcctest values(NULL,'mi');insert into mvcctest values(NULL,'kong');commit;
      假设系统初始事务ID为1;
      transaction 2
      :
        start transaction;select * from mvcctest; (1)select * from mvcctest; (2)commit

        SELECT:

        假设当执行事务2的过程中,准备执行语句(2)时,开始执行事务3:
        transaction 3
        :
          start transaction;insert into mvcctest values(NULL,'qu');commit;
          事务3执行完毕,开始执行事务2 语句2,由于事务2只能查询创建时间小于等于2的,所以事务3新增的记录在事务2中是查不出来的,这就通过乐观锁的方式避免了幻读的产生。

          UPDATE:

          假设当执行事务2的过程中,准备执行语句(2)时,开始执行事务4:
          transaction 4
          :
            start transaction;update mvcctest set name = 'fan' where id = 2;commit;
            InnoDB执行UPDATE,实际上是新插入了一行记录,并保存其创建时间为当前事务的ID,同时保存当前事务ID到要UPDATE的行的删除时间。
            事务4执行完毕,开始执行事务2 语句2,由于事务2只能查询创建时间小于等于2的,所以事务修改的记录在事务2中是查不出来的,这样就保证了事务在两次读取时读取到的数据的状态是一致的。

            DELETE:

            假设当执行事务2的过程中,准备执行语句(2)时,开始执行事务5:
            transaction 5:
              start transaction;delete from mvcctest where id = 2;commit;
              事务5执行完毕,开始执行事务2 语句2,由于事务2只能查询创建时间小于等于2、并且过期时间大于等于2,所以id=2的记录在事务2 语句2中,也是可以查出来的,这样就保证了事务在两次读取时读取到的数据的状态是一致的。

              快照读和当前读

              表记录有两种读取方式。
              • 快照读:读取的是快照版本。普通的SELECT
                就是快照读。通过mvcc来进行并发控制的,不用加锁。
              • 当前读:读取的是最新版本。UPDATE、DELETE、INSERT、SELECT … LOCK IN SHARE MODE、SELECT … FOR UPDATE
                是当前读。
              快照读情况下,InnoDB通过mvcc
              机制避免了幻读现象。而mvcc
              机制无法避免当前读情况下出现的幻读现象。因为当前读每次读取的都是最新数据,这时如果两次查询中间有其它事务插入数据,就会产生幻读。
              那么MySQL是如何避免幻读?
              • 在快照读情况下,MySQL通过mvcc
                来避免幻读。
              • 在当前读情况下,MySQL通过next-key
                来避免幻读(加行锁和间隙锁来实现的)。
              next-key包括两部分:行锁和间隙锁。行锁是加在索引上的锁,间隙锁是加在索引之间的。
              Serializable
              隔离级别也可以避免幻读,会锁住整张表,并发性极低,一般不会使用。

              共享锁和排他锁

              SELECT 的读取锁定主要分为两种方式:共享锁和排他锁。
              select * from table where id<6 lock in share mode;--共享锁
              select * from table where id<6 for update;--排他锁
              这两种方式主要的不同在于LOCK IN SHARE MODE
              多个事务同时更新同一个表单时很容易造成死锁。
              申请排他锁的前提是,没有线程对该结果集的任何行数据使用排它锁或者共享锁,否则申请会受到阻塞。在进行事务操作时,MySQL会对查询结果集的每行数据添加排它锁,其他线程对这些数据的更改或删除操作会被阻塞(只能读操作),直到该语句的事务被commit
              语句或rollback
              语句结束为止。
              SELECT... FOR UPDATE
               使用注意事项:
              1. for update
                 仅适用于innodb,且必须在事务范围内才能生效。
              2. 根据主键进行查询,查询条件为like
                或者不等于,主键字段产生表锁
              3. 根据非索引字段进行查询,会产生表锁

              bin log/redo log/undo log

              MySQL日志主要包括查询日志、慢查询日志、事务日志、错误日志、二进制日志等。其中比较重要的是 bin log
              (二进制日志)和 redo log
              (重做日志)和 undo log
              (回滚日志)。
              bin log
              bin log
              是MySQL数据库级别的文件,记录对MySQL数据库执行修改的所有操作,不会记录select和show语句,主要用于恢复数据库和同步数据库。
              redo log
              redo log
              属于 MySQL 存储引擎 InnoDB 的事务日志。
              MySQL 的数据是存放在磁盘中的,每次读写数据都需做磁盘 IO 操作,如果并发场景下性能就会很差。为此 MySQL 提供了一个优化手段,引入缓存 Buffer Pool
              。这个缓存中包含了磁盘中部分数据页(page
              )的映射,以此来缓解数据库的磁盘压力。
              当从数据库读数据时,首先从缓存中读取。如果缓存中没有,则从磁盘读取后放入缓存;当向数据库写入数据时,先向缓存写入,此时缓存中的数据页数据变更,这个数据页称为脏页,Buffer Pool
              中修改完数据后会按照设定的更新策略,定期刷到磁盘中,这个过程称为刷脏页。
              如果刷脏页还未完成,可 MySQL 由于某些原因宕机重启。此时 Buffer Pool
              中修改的数据还没有及时的刷到磁盘中,就会导致数据丢失,无法保证事务的持久性。
              为了解决这个问题,引入了redo log
              redo Log
              如其名侧重于重做!它记录的是数据库中每个页的修改,而不是某一行或某几行修改成怎样,可以用来恢复提交后的物理数据页,且只能恢复到最后一次提交的位置。
              redo log
              用到了 WAL(Write-Ahead Logging
              )技术。这个技术的核心就在于修改记录前,一定要先写日志,并保证日志先落盘,才能算事务提交完成。
              有了 redo log 再修改数据时,InnoDB 引擎会把更新记录先写在 redo log
              中,在修改 Buffer Pool
              中的数据。当提交事务时,调用 fsync
              redo log
              刷入磁盘。至于缓存中更新的数据文件何时刷入磁盘,则由后台线程异步处理。
              注意:此时 redo log
              的事务状态是 prepare
              ,还未真正提交成功,要等 bin log 日志写入磁盘完成才会变更为 commit
              ,事务才算真正提交完成。
              这样一来,即使刷脏页之前 MySQL 意外宕机也没关系。只要在重启时解析 redo log 中的更改记录进行重放,重新刷入磁盘即可。
              undo log
              undo log
              也是属于 MySQL 存储引擎 InnoDB 的事务日志。
              undo log 属于逻辑日志,如其名主要起到回滚的作用,它是保证事务原子性的关键。记录的是数据修改前的状态,在数据修改的流程中,同时会记录一条与当前操作相反的逻辑日志到 undo log
              中。
              这样当某些原因导致服务异常事务失败,就可以借助 undo log
              将数据回滚到事务执行前的状态,保证事务的完整性。
              那可能有人会问:同一个事务内的一条记录被多次修改,那是不是每次都要把数据修改前的状态都写入 undo log
              呢?
              答案是不会的!
              undo log 只负责记录事务开始前要修改数据的原始版本,当我们再次对这行数据进行修改,所产生的修改记录会写入到 redo log
              undo log
              负责完成回滚,redo log 负责完成前滚。
              未提交的事务,即事务未执行 commit
              。但该事务内修改的脏页中,可能有一部分脏块已经刷盘。如果此时数据库实例宕机重启,就需要用回滚来将先前那部分已经刷盘的脏块从磁盘上撤销。
              未完全提交的事务,即事务已经执行 commit
              ,但该事务内修改的脏页中只有一部分数据被刷盘,另外一部分还在 buffer pool
              缓存上,如果此时数据库实例宕机重启,就需要用前滚来完成未完全提交的事务。将先前那部分由于宕机在内存上的未来得及刷盘数据,从 redo log 中恢复出来并刷入磁盘。
              数据库实例恢复时,先做前滚,后做回滚。
              undo log、redo log、bin log
              三种日志都是在刷脏页之前就已经刷到磁盘了,相互协作最大限度保证了用户提交的数据不丢失。

              bin log和redo log有什么区别?

              • bin log会记录所有日志记录,包括InnoDB、MyISAM
                等存储引擎的日志;redo log只记录innoDB自身的事务日志。
              • bin log只在事务提交前写入到磁盘,一个事务只写一次;而在事务进行过程,会有redo log不断写入磁盘。
              • bin log是逻辑日志,记录的是SQL语句的原始逻辑;redo log是物理日志,记录的是在某个数据页上做了什么修改。

              MySQL主从同步


              在生产环境中,为了满足安全性,高可用性以及高并发等方面的需求,基本上采用的MySQL数据库架构都是MHA、MGR等,最低也得是一主一从的架构,搭配自动切换脚本,实现故障自动切换。上述架构都是通过集群主从复制(Master-Slave)的方式来同步数据。
              MySQL集群简单架构图:

              主从复制原理?

              mysql主从复制需要三个线程:master(binlog dump thread)、slave(I/O thread 、SQL thread)
              复制基本过程(面试常考):
              1. 主库写入数据并且生成binlog文件。该过程中MySQL将事务串行的写入二进制日志,依赖binlog dump线程。
              2. 在事件写入二进制日志完成后,master通知存储引擎提交事务。
              3. 从库服务器上的IO线程连接Master服务器,请求从执行binlog日志文件中的指定位置开始读取binlog至从库。
              4. 主库接收到从库的IO线程请求后,其上复制的IO线程会根据Slave的请求信息分批读取binlog文件然后返回给从库的IO线程。
              5. Slave服务器的IO线程获取到Master服务器上IO线程发送的日志内容、日志文件及位置点后,会将binlog日志内容依次写到Slave端自身的Relay Log(即中继日志)文件的最末端,并将新的binlog文件名和位置记录到master-info文件中,以便下一次读取master端新binlog日志时能告诉Master服务器从新binlog日志的指定文件及位置开始读取新的binlog日志内容。
              6. 从库服务器的SQL线程会实时监测到本地Relay Log中新增了日志内容,然后把RelayLog中的日志翻译成SQL并且按照顺序执行SQL来更新从库的数据。
              7. 从库在relay-log.info中记录当前应用中继日志的文件名和位置点以便下一次数据复制。

              主从延迟?

              主从延迟是怎么回事?
              根据前面主从复制的原理可以看出,两者之间是存在一定时间的数据不一致,也就是所谓的主从延迟。
              我们来看下导致主从延迟的时间点:
              • 主库 A 执行完成一个事务,写入 binlog,该时刻记为T1.
              • 传给从库B,从库接受完这个binlog的时刻记为T2.
              • 从库B执行完这个事务,该时刻记为T3.
              那么所谓主从延迟,就是同一个事务,从库执行完成的时间和主库执行完成的时间之间的差值,即T3-T1。
              我们也可以通过在从库执行show slave status
              ,返回结果会显示seconds_behind_master
              ,表示当前从库延迟了多少秒。
              seconds_behind_master如何计算的?
              • 每一个事务的binlog都有一个时间字段,用于记录主库上写入的时间
              • 从库取出当前正在执行的事务的时间字段,跟当前系统的时间进行相减,得到的就是seconds_behind_master,也就是前面所描述的T3-T1。
              为什么会主从延迟?
              正常情况下,如果网络不延迟,那么日志从主库传给从库的时间是相当短,所以T2-T1可以基本忽略。
              最直接的影响就是从库消费中转日志(relaylog
              )的时间段,而造成原因一般是以下几种:
              1、从库的机器性能比主库要差
              比如将20台主库放在4台机器,把从库放在一台机器。这个时候进行更新操作,由于更新时会触发大量读操作,导致从库机器上的多个从库争夺资源,导致主从延迟。
              不过,目前大部分部署都是采取主从使用相同规格的机器部署。
              2、从库的压力大
              按照正常的策略,读写分离,主库提供写能力,从库提供读能力。将进行大量查询放在从库上,结果导致从库上耗费了大量的CPU资源,进而影响了同步速度,造成主从延迟。
              对于这种情况,可以通过一主多从,分担读压力;也可以采取binlog输出到外部系统,比如Hadoop
              ,让外部系统提供查询能力。
              3、大事务的执行
              一旦执行大事务,那么主库必须要等到事务完成之后才会写入binlog。
              比如主库执行了一条insert … select
              非常大的插入操作,该操作产生了近几百G的binlog文件传输到只读节点,进而导致了只读节点出现应用binlog延迟。
              因此,DBA经常会提醒开发,不要一次性地试用delete语句删除大量数据,尽可能控制数量,分批进行。
              4、主库的DDL(alter、drop、create
              )
              1、只读节点与主库的DDL同步是串行进行,如果DDL操作在主库执行时间很长,那么从库也会消耗同样的时间,比如在主库对一张500W的表添加一个字段耗费了10分钟,那么从节点上也会耗费10分钟。
              2、从节点上有一个执行时间非常长的的查询正在执行,那么这个查询会堵塞来自主库的DDL,表被锁,直到查询结束为止,进而导致了从节点的数据延迟。
              5、锁冲突
              锁冲突问题也可能导致从节点的SQL线程执行慢,比如从机上有一些select .... for update的SQL,或者使用了MyISAM引擎等。
              6、从库的复制能力
              一般场景中,因偶然情况导致从库延迟了几分钟,都会在从库恢复之后追上主库。但若是从库执行速度低于主库,且主库持续具有压力,就会导致长时间主从延迟,很有可能就是从库复制能力的问题。
              从库上的执行,即sql_thread更新逻辑,在5.6版本之前,是只支持单线程,那么在主库并发高、TPS高时,就会出现较大的主从延迟。
              因此,MySQL自5.7版本后就已经支持并行复制了。可以在从服务上设置 slave_parallel_workers为一个大于0的数,然后把slave_parallel_type
              参数设置为LOGICAL_CLOCK
              ,这就可以了。

              怎么减少主从延迟

              主从同步问题永远都是一致性和性能的权衡,得看实际的应用场景,若想要减少主从延迟的时间,可以采取下面的办法:
              • 降低多线程大事务并发的概率,优化业务逻辑
              • 优化SQL,避免慢SQL,减少批量操作,建议写脚本以update-sleep
                这样的形式完成。
              • 提高从库机器的配置,减少主库写binlog和从库读binlog的效率差。
              • 尽量采用短的链路,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。
              • 实时性要求的业务读强制走主库,从库只做灾备,备份。

              大表优化?

              当 MySQL 单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化:

              单表优化?

              除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑、部署、运维的各种复杂度,一般以整型值为主的表在千万级以下,字符串为主的表在五百万以下是没有太大问题的。而事实上很多时候 MySQL 单表的性能依然有不少优化空间,甚至能正常支撑千万级以上的数据量:

              字段

              • 尽量使用INT而非BIGINT,如果非负则加上UNSIGNED
                (这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT
                更好。
              • VARCHAR的长度只分配真正需要的空间
              • 使用枚举或整数代替字符串类型
              • 尽量使用TIMESTAMP
                而非DATETIME
              • 单表不要有太多字段,建议在 20 以内
              • 表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。
              • 用整型来存 IP


              索引

              • 索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE
                ORDER BY
                命令涉及的列建立索引,可根据EXPLAIN
                来查看是否用了索引还是全表扫描
              • 应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描
              • 值分布很稀少的字段不适合建索引,例如 "性别" 这种只有两三个值的字段
              • 字符字段只建前缀索引
              • 字符字段最好不要做主键
              • 不用外键,由程序保证约束
              • 尽量不用UNIQUE
                ,由程序保证约束
              • 使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引

              简言之就是使用合适的数据类型,选择合适的索引

              选择合适的数据类型
              (1)使用可存下数据的最小的数据类型,整型 < date,time < char,varchar < blob

              (2)使用简单的数据类型,整型比字符处理开销更小,因为字符串的比较更复杂。如,int类型存储时间类型,bigint类型转ip函数
              (3)使用合理的字段属性长度,固定长度的表会更快。使用
              enum、char
              而不是varchar
              (4)尽可能使用
              not null
              定义字段
              (5)尽量少用text,非用不可最好分表

              # 选择合适的索引列
              (1)查询频繁的列,在
              where,group by,order by,on
              从句中出现的列
              (2)where条件中<,<=,=,>,>=,between,in,以及like 字符串+通配符(%)出现的列
              (3)长度小的列,索引字段越小越好,因为数据库的存储单位是页,一页中能存下的数据越多越好
              (4)离散度大(不同的值多)的列,放在联合索引前面。查看离散度,通过统计不同的列值来实现,count越大,离散程度越高:


              查询 SQL

              • 可通过开启慢查询日志来找出较慢的 SQL
              • 不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边
              • sql 语句尽可能简单:一条 sql 只能在一个 cpu 运算;大语句拆小语句,减少锁时间;一条大 sql 可以堵死整个库
              • 不用SELECT *
              • OR改写成IN:OR的效率是 n 级别,IN的效率是 log(n) 级别,in 的个数建议控制在 200 以内
              • 不用函数和触发器,在应用程序实现
              • 避免%xxx式查询
              • 少用JOIN
              • 使用同类型进行比较,比如用'123'和'123'比,123和123比
              • 尽量避免在WHERE子句中使用!= 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描
              • 对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
              • 列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大


              引擎

              目前广泛使用的是 MyISAM 和 InnoDB 两种引擎:
               MyISAM 
              MyISAM 引擎是 MySQL 5.1 及之前版本的默认引擎,它的特点是:
              • 不支持行锁,读取时对需要读到的所有表加锁,写入时则对表加排它锁
              • 不支持事务
              • 不支持外键
              • 不支持崩溃后的安全恢复
              • 在表有读取查询的同时,支持往表中插入新纪录
              • 支持BLOB和TEXT的前 500 个字符索引,支持全文索引
              • 支持延迟更新索引,极大提升写入性能
              • 对于不会进行修改的表,支持压缩表,极大减少磁盘空间占用
               InnoDB 
              InnoDB 在 MySQL 5.5 后成为默认索引,它的特点是:
              • 支持行锁,采用 MVCC 来支持高并发
              • 支持事务
              • 支持外键
              • 支持崩溃后的安全恢复
              • 不支持全文索引

              总体来讲,MyISAM 适合SELECT密集型的表,而 InnoDB 适合INSERT和UPDATE密集型的表

              系统调优参数

              可以使用下面几个工具来做基准测试:
              • sysbench
                :一个模块化,跨平台以及多线程的性能测试工具
              • iibench-mysql:基于 Java 的 MySQL/Percona/MariaDB 索引进行插入性能测试工具
              • tpcc-mysql:Percona 开发的 TPC-C 测试工具
              具体的调优参数内容较多,具体可参考官方文档,这里介绍一些比较重要的参数:
              • back_log:back_log 值指出在 MySQL 暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。也就是说,如果 MySql 的连接数据达到 max_connections 时,新来的请求将会被存在堆栈中,以等待某一连接释放资源,该堆栈的数量即 back_log,如果等待连接的数量超过 back_log,将不被授予连接资源。可以从默认的 50 升至 500
              • wait_timeout:数据库连接闲置时间,闲置连接会占用内存资源。可以从默认的 8 小时减到半小时
              • max_user_connection: 最大连接数,默认为 0 无上限,最好设一个合理上限
              • thread_concurrency:并发线程数,设为 CPU 核数的两倍
              • skip_name_resolve:禁止对外部连接进行 DNS 解析,消除 DNS 解析时间,但需要所有远程主机用 IP 访问
              • key_buffer_size:索引块的缓存大小,增加会提升索引处理速度,对 MyISAM 表性能影响最大。对于内存 4G 左右,可设为 256M 或 384M,通过查询show status like 'key_read%',保证key_reads key_read_requests在 0.1% 以下最好
              • innodb_buffer_pool_size:缓存数据块和索引块,对 InnoDB 表性能影响最大。通过查询show status like 'Innodb_buffer_pool_read%',保证 (Innodb_buffer_pool_read_requests – Innodb_buffer_pool_reads) Innodb_buffer_pool_read_requests越高越好
              • innodb_additional_mem_pool_size:InnoDB 存储引擎用来存放数据字典信息以及一些内部数据结构的内存空间大小,当数据库对象非常多的时候,适当调整该参数的大小以确保所有数据都能存放在内存中提高访问效率,当过小的时候,MySQL 会记录 Warning 信息到数据库的错误日志中,这时就需要该调整这个参数大小
              • innodb_log_buffer_size:InnoDB 存储引擎的事务日志所使用的缓冲区,一般来说不建议超过 32MB
              • query_cache_size:缓存 MySQL 中的 ResultSet,也就是一条 SQL 语句执行的结果集,所以仅仅只能针对 select 语句。当某个表的数据有任何任何变化,都会导致所有引用了该表的 select 语句在 Query Cache 中的缓存数据失效。所以,当我们的数据变化非常频繁的情况下,使用 Query Cache 可能会得不偿失。根据命中率(Qcache_hits/(Qcache_hits+Qcache_inserts)*100))进行调整,一般不建议太大,256MB 可能已经差不多了,大型的配置型静态数据可适当调大.
                可以通过命令show status like 'Qcache_%'查看目前系统 Query catch 使用大小
              • read_buffer_size:MySql 读入缓冲区大小。对表进行顺序扫描的请求将分配一个读入缓冲区,MySql 会为它分配一段内存缓冲区。如果对表的顺序扫描请求非常频繁,可以通过增加该变量值以及内存缓冲区大小提高其性能
              • sort_buffer_size:MySql 执行排序使用的缓冲大小。如果想要增加ORDER BY的速度,首先看是否可以让 MySQL 使用索引而不是额外的排序阶段。如果不能,可以尝试增加 sort_buffer_size 变量的大小
              • read_rnd_buffer_size:MySql 的随机读缓冲区大小。当按任意顺序读取行时 (例如,按照排序顺序),将分配一个随机读缓存区。进行排序查询时,MySql 会首先扫描一遍该缓冲,以避免磁盘搜索,提高查询速度,如果需要排序大量数据,可适当调高该值。但 MySql 会为每个客户连接发放该缓冲空间,所以应尽量适当设置该值,以避免内存开销过大。
              • record_buffer:每个进行一个顺序扫描的线程为其扫描的每张表分配这个大小的一个缓冲区。如果你做很多顺序扫描,可能想要增加该值
              • thread_cache_size:保存当前没有与连接关联但是准备为后面新的连接服务的线程,可以快速响应连接的线程请求而无需创建新的
              • table_cache:类似于 thread_cache_size,但用来缓存表文件,对 InnoDB 效果不大,主要用于 MyISAM


              升级硬件

              Scale up,这个不多说了,根据 MySQL 是 CPU 密集型还是 I/O 密集型,通过提升 CPU 和内存、使用 SSD,都能显著提升 MySQL 性能

              读写分离?

              也是目前常用的优化,从库读主库写,一般不要采用双主或多主引入很多复杂性,尽量采用文中的其他方案来提高性能。同时目前很多拆分的解决方案同时也兼顾考虑了读写分离

              缓存?

              缓存可以发生在这些层次:

              • MySQL 内部:在系统调优参数介绍了相关设置
              • 数据访问层:比如 MyBatis 针对 SQL 语句做缓存,而 Hibernate 可以精确到单个记录,这里缓存的对象主要是持久化对象Persistence Object
              • 应用服务层:这里可以通过编程手段对缓存做到更精准的控制和更多的实现策略,这里缓存的对象是数据传输对象Data Transfer Object
              • Web 层:针对 web 页面做缓存
              • 浏览器客户端:用户端的缓存

              可以根据实际情况在一个层次或多个层次结合加入缓存。这里重点介绍下服务层的缓存实现,目前主要有两种方式:

              • 直写式(Write Through):在数据写入数据库后,同时更新缓存,维持数据库与缓存的一致性。这也是当前大多数应用缓存框架如 Spring Cache 的工作方式。这种实现非常简单,同步好,但效率一般。
              • 回写式(Write Back):当有数据要写入数据库时,只会更新缓存,然后异步批量的将缓存数据同步到数据库上。这种实现比较复杂,需要较多的应用逻辑,同时可能会产生数据库与缓存的不同步,但效率非常高。

              表分区?

              MySQL 在 5.1 版引入的分区是一种简单的水平拆分,用户需要在建表的时候加上分区参数,对应用是透明的无需修改代码

              对用户来说,分区表是一个独立的逻辑表,但是底层由多个物理子表组成,实现分区的代码实际上是通过对一组底层表的对象封装,但对 SQL 层来说是一个完全封装底层的黑盒子。MySQL 实现分区的方式也意味着索引也是按照分区的子表定义,没有全局索引

              用户的 SQL 语句是需要针对分区表做优化,SQL 条件中要带上分区条件的列,从而使查询定位到少量的分区上,否则就会扫描全部分区,可以通过EXPLAIN PARTITIONS
              来查看某条 SQL 语句会落在那些分区上,从而进行 SQL 优化。

              分区的好处是:

              • 可以让单表存储更多的数据
              • 分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作
              • 部分查询能够从查询条件确定只落在少数分区上,速度会很快
              • 分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备
              • 可以使用分区表赖避免某些特殊瓶颈,例如 InnoDB 单个索引的互斥访问、ext3 文件系统的 inode 锁竞争
              • 可以备份和恢复单个分区

              分区的限制和缺点:

              • 一个表最多只能有 1024 个分区
              • 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来
              • 分区表无法使用外键约束
              • NULL 值会使分区过滤无效
              • 所有分区必须使用相同的存储引擎

              分区的类型:

              • RANGE 分区:基于属于一个给定连续区间的列值,把多行分配给分区
              • LIST 分区:类似于按 RANGE 分区,区别在于 LIST 分区是基于列值匹配一个离散值集合中的某个值来进行选择
              • HASH 分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含 MySQL 中有效的、产生非负整数值的任何表达式
              • KEY 分区:类似于按 HASH 分区,区别在于 KEY 分区只支持计算一列或多列,且 MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值

              分区适合的场景有:

              • 最适合的场景数据的时间序列性比较强,则可以按时间来分区,如下所示:

              CREATE TABLE members (
                 firstname VARCHAR(25NOT NULL,
                 lastname VARCHAR(25NOT NULL,
                 username VARCHAR(16NOT NULL,
                 email VARCHAR(35),
                 joined DATE NOT NULL
              )
              PARTITION BY RANGEYEAR(joined) ) (
                 PARTITION p0 VALUES LESS THAN (1960),
                 PARTITION p1 VALUES LESS THAN (1970),
                 PARTITION p2 VALUES LESS THAN (1980),
                 PARTITION p3 VALUES LESS THAN (1990),
                 PARTITION p4 VALUES LESS THAN MAXVALUE
              );

              • 查询时加上时间范围条件效率会非常高,同时对于不需要的历史数据能很容的批量删除。

              如果数据有明显的热点,而且除了这部分数据,其他数据很少被访问到,那么可以将热点数据单独放在一个分区,让这个分区的数据能够有机会都缓存在内存中,查询时只访问一个很小的分区表,能够有效使用索引和缓存

              另外 MySQL 有一种早期的简单的分区实现 - 合并表(merge table),限制较多且缺乏优化,不建议使用,应该用新的分区机制来替代

              垂直拆分?

              垂直分库是根据数据库里面的数据表的相关性进行拆分,比如:一个数据库里面既存在用户数据,又存在订单数据,那么垂直拆分可以把用户数据放到用户库、把订单数据放到订单库。垂直分表是对数据表进行垂直拆分的一种方式,常见的是把一个多字段的大表按常用字段和非常用字段进行拆分,每个表里面的数据记录数一般情况下是相同的,只是字段不一样,使用主键关联

              比如原始的用户表是:
              垂直拆分后是:
              垂直拆分的优点是:
              • 可以使得行数据变小,一个数据块 (Block) 就能存放更多的数据,在查询时就会减少 I/O 次数 (每次查询时读取的 Block 就少)
              • 可以达到最大化利用 Cache 的目的,具体在垂直拆分的时候可以将不常变的字段放一起,将经常改变的放一起
              • 数据维护简单
              缺点是:
              • 主键出现冗余,需要管理冗余列
              • 会引起表连接 JOIN 操作(增加 CPU 开销)可以通过在业务服务器上进行 join 来减少数据库压力
              • 依然存在单表数据量过大的问题(需要水平拆分)
              • 事务处理复杂

              水平拆分?

              概述


              水平拆分是通过某种策略将数据分片来存储,分库内分表和分库两部分,每片数据会分散到不同的 MySQL 表或库,达到分布式的效果,能够支持非常大的数据量。前面的表分区本质上也是一种特殊的库内分表

              库内分表,仅仅是单纯的解决了单一表数据过大的问题,由于没有把表的数据分布到不同的机器上,因此对于减轻 MySQL 服务器的压力来说,并没有太大的作用,大家还是竞争同一个物理机上的 IO、CPU、网络,这个就要通过分库来解决

              前面垂直拆分的用户表如果进行水平拆分,结果是:


              实际情况中往往会是垂直拆分和水平拆分的结合,即将Users_A_M和Users_N_Z再拆成Users和UserExtras
              ,这样一共四张表

              水平拆分的优点是:
              • 不存在单库大数据和高并发的性能瓶颈
              • 应用端改造较少
              • 提高了系统的稳定性和负载能力
              缺点是:
              • 分片事务一致性难以解决
              • 跨节点 Join 性能差,逻辑复杂
              • 数据多次扩展难度跟维护量极大


              分片原则

              • 能不分就不分,参考单表优化
              • 分片数量尽量少,分片尽量均匀分布在多个数据结点上,因为一个查询 SQL 跨分片越多,则总体性能越差,虽然要好于所有数据在一个分片的结果,只在必要的时候进行扩容,增加分片数量
              • 分片规则需要慎重选择做好提前规划,分片规则的选择,需要考虑数据的增长模式,数据的访问模式,分片关联性问题,以及分片扩容问题,最近的分片策略为范围分片,枚举分片,一致性 Hash 分片,这几种分片都有利于扩容
              • 尽量不要在一个事务中的 SQL 跨越多个分片,分布式事务一直是个不好处理的问题
              • 查询条件尽量优化,尽量避免 Select *
                的方式,大量数据结果集下,会消耗大量带宽和 CPU 资源,查询尽量避免返回大量结果集,并且尽量为频繁使用的查询语句建立索引。
              • 通过数据冗余和表分区赖降低跨库 Join 的可能

              这里特别强调一下分片规则的选择问题,如果某个表的数据有明显的时间特征,比如订单、交易记录等,则他们通常比较合适用时间范围分片,因为具有时效性的数据,我们往往关注其近期的数据,查询条件中往往带有时间字段进行过滤,比较好的方案是,当前活跃的数据,采用跨度比较短的时间段进行分片,而历史性的数据,则采用比较长的跨度存储。

              总体上来说,分片的选择是取决于最频繁的查询 SQL 的条件,因为不带任何 Where 语句的查询 SQL,会遍历所有的分片,性能相对最差,因此这种 SQL 越多,对系统的影响越大,所以我们要尽量避免这种 SQL 的产生。

              查询语句执行流程?

              查询语句的执行流程如下:权限校验、查询缓存、分析器、优化器、权限校验、执行器、引擎。
              执行下面这条查询语句:
                select * from test where id = 1000;
                返回结果为:
                  +------+-------+---------+---------------------+
                  | id  | name  | Stu_ID  |  create_time  |
                  +------+-------+---------+---------------------+
                  |1000 | 刘皇叔 | 2022001 | 2022-08-10 22:29:08 |
                  +------+-------+---------+---------------------+
                  来看一下 SQL 语句在 MySQL 的各个模块中的执行过程。

                  更新语句执行过程?

                  更新语句执行流程如下:分析器、权限校验、执行器、引擎、redo log
                  prepare
                  状态)、binlog
                  redo log
                  commit
                  状态)
                  以此SQL为例,执行流程图如下
                    update test set i=i+1 where ID=1;

                    exist和in的区别?

                    exists
                    用于对外表记录做筛选。exists
                    会遍历外表,将外查询表的每一行,代入内查询进行判断。当exists
                    里的条件语句能够返回记录行时,条件就为真,返回外表当前记录。反之如果exists
                    里的条件语句不能返回记录行,条件为假,则外表当前记录被丢弃。
                    select a.* from A awhere exists(select 1 from B b where a.id=b.id)
                    in
                    是先把后边的语句查出来放到临时表中,然后遍历临时表,将临时表的每一行,代入外查询去查找。
                    select * from Awhere id in(select id from B)
                    子查询的表比较大的时候,使用exists
                    可以有效减少总的循环次数来提升速度;当外查询的表比较大的时候,使用in
                    可以有效减少对外查询表循环遍历来提升速度。

                    truncate、delete与drop区别?

                    相同点:
                    1. truncate
                      和不带where
                      子句的delete
                      、以及drop
                      都会删除表内的数据。
                    2. drop
                      truncate
                      都是DDL
                      语句(数据定义语言),执行后会自动提交。
                    不同点:
                    1. truncate 和 delete 只删除数据不删除表的结构;drop 语句将删除表的结构被依赖的约束、触发器、索引;
                    2. 一般来说,执行速度: drop > truncate > delete。

                    having和where的区别?

                    • 二者作用的对象不同,where
                      子句作用于表和视图,having
                      作用于组。
                    • where
                      在数据分组前进行过滤,having
                      在数据分组后进行过滤。

                    show processlist详解?

                    show processlist
                     或 show full processlist
                     可以查看当前 MySQL 是否有压力,正在运行的SQL
                    ,有没有慢SQL
                    正在执行。返回参数如下:
                    1. id:线程ID,可以用kill id
                      杀死某个线程
                    2. db:数据库名称
                    3. user:数据库用户
                    4. host:数据库实例的IP
                    5. command:当前执行的命令,比如Sleep
                      Query
                      Connect
                    6. time:消耗时间,单位秒
                    7. state:执行状态,主要有以下状态:
                      • Sleep
                        ,线程正在等待客户端发送新的请求
                      • Locked
                        ,线程正在等待锁
                      • Sending data
                        ,正在处理SELECT
                        查询的记录,同时把结果发送给客户端
                      • Kill
                        ,正在执行kill
                        语句,杀死指定线程
                      • Connect
                        ,一个从节点连上了主节点
                      • Quit
                        ,线程正在退出
                      • Sorting for group
                        ,正在为GROUP BY
                        做排序
                      • Sorting for order
                        ,正在为ORDER BY
                        做排序
                    8. info:正在执行的SQL
                      语句

                    更多精彩内容,关注我们▼▼












                    文章转载自数据与人,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                    评论