当前位置: 首页 > news >正文

【Mysql】事务隔离级别、索引原理、/redolog/undolog/binlog区别、主从复制原理

【Mysql】事务隔离级别、索引原理、/redolog/undolog/binlog区别、主从复制原理

  • 1、存储引擎
    • 1.1 InnoDB和MyISAM有什么区别?
  • 2、数据库范式
    • 2.1 什么是数据库范式,为什么要反范式?
  • 3、Mysql事务隔离级别
    • 3.1 MySQL中的事务隔离级别?
    • 3.2 如何理解MVCC?
    • 3.3 快照读与当前读的区别?
    • 3.4 InnoDB如何解决脏读、不可重复读和幻读的?
    • 3.5 undolog会一直存在吗?什么时候删除?
  • 4、介绍下InnoDB的锁机制?
    • 4.1 读锁/写锁
    • 4.2 记录锁、间隙锁、临键锁、插入意向锁
      • 4.2.1 行锁/记录锁
      • 4.2.2 间隙锁
      • 4.2.3 临键锁(Next Locks)
    • 4.3 表级别的S锁、X锁
    • 4.4 表级别的意向锁(intention lock)
  • 5、介绍下InnoDB的索引?
    • 5.1 索引的常见概念
    • 5.2 什么是回表,怎么减少回表的次数?
      • 5.2.1 覆盖索引
      • 5.2.2 索引下推
      • 5.2.3 MySQL只操作同一条记录,也会发生死锁吗?
  • 6、binlog、redolog和undolog区别?
    • 6.1 redolog日志
    • 6.2 undolog日志
    • 6.3 binlog日志介绍
    • 6.4 binlog与redo log对比
    • 6.5 什么是事务的2阶段提交?
    • 6.6 一次insert操作,MySQL的几种log的写入顺序?
  • 7、sql调优
    • 7.1 可能导致索引失效的情况?
    • 7.2 索引失效的问题如何排查?
    • 7.3 如何进行SQL调优?
  • 8、主从复制
    • 8. 1 MySQL主从复制的过程
  • 9、驱动表
    • 9.1 MySQL的驱动表是什么?MySQL怎么选的?
    • 9.2 如何判断哪张表是驱动表
    • 9.3 left join 一定是左表作为驱动表吗?
    • 9.4 MySQL 为什么是小表驱动大表,为什么能提高查询性能?

1、存储引擎

1.1 InnoDB和MyISAM有什么区别?

InnoDB和MyISAM是MySQL中比较常用的两个执行引擎,MySQL 在 5.5 之前版本默认存储引擎是 MyISAM,5.5 之后版本默认存储引擎是 InnoDB,MyISAM适合查询以及插入为主的应用,InnoDB适合频繁修改以及涉及到安全性较高的应用。

他们主要有以下区别:

1、InnoDB支持事务,MyISAM不支持
2、InnoDB 是聚集索引,MyISAM 是非聚集索引。MyISAM是采用了一种索引和数据分离的存储方式,Innodb的聚簇索引中索引和数据在一起。
3、InnoDB支持外键,MyISAM不支持
4、InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。
5、InnoDB不支持FULLTEXT类型的索引(5.6之前不支持全文索引)
6、InnoDB中不保存表的行数,但是MyISAM只要简单的读出保存好的行数即可
7、对于自增长的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中可以和其他字段一起建立联合索引

2、数据库范式

2.1 什么是数据库范式,为什么要反范式?

参考文章:【数据库三大范式和反范式】

范式(Normal Form)是数据库设计时遵循的一种规范,不同的规范遵循不同的范式。范式可以避免数据冗余,减少数据库的空间,减轻维护数据完整性的麻烦

  1. 第一范式(1NF):属性不可分割。即每个属性都是不可分割的原子项。
  2. 第二范式(2NF):联合主键不能存在部分依赖。满足第一范式,并且不存在部分依赖,即非主属性必须完全依赖于主属性。(主属性即主键,完全依赖是针对于联合主键的情况,非主键列不能只依赖于联合主键的一部分)。
  3. 第三范式(3NF):非主键字段不能存在传递依赖。满足第二范式,并且不能存在传递依赖。即非主属性不能与非主属性之间有依赖关系,非主属性必须直接依赖于主属性。

等级越高的范式设计出来的表越多,查询存在多表连接时较为耗时。这时可以考虑使用反范式。即用空间来换时间,把数据冗余在多个表中,查询时可以减少表之间的关联。

3、Mysql事务隔离级别

3.1 MySQL中的事务隔离级别?

参考文章:【数据库事务的ACID特性和四种隔离级别】

对于数据库中相同部分的数据,如果2个事务同时进行操作的话,可能会有数据并发问题。在不保证串行执行的基础上,主要有以下4种数据并发问题:1写3读

  1. 脏写(Dirty Write)即一个事务修改数据过程中(未提交/回滚),有另一个事务也来进行修改并提交了,这样会导致脏写(因为另一个事务发现数据和自己修改的不一致)。
  2. 脏读(Dirty Read):一个事务修改数据中,另一个事务读取了该数据,之后该事务进行了回滚,那么另一个事务读到的数据就属于脏读。
  3. 不可重复读(Non-Repeatable Read):其他事务在修改某条数据,另一个事务每次读取的数据都不一样(即每次都读到了最新的数据),这种情况属于不可重复读。
  4. 幻读(Phantom Read):一个事物读取了数据,然后另一个事务插入了新的数据,原来的事务再读的时候会多出几行,这种情况属于幻读。

针对上面的数据并发问题:1写3读,数据库设立了4种隔离级别。隔离级别越高,数据库的并发性能就越差。由于脏写问题是一个严重的问题,因此这4种隔离级别都解决了脏写问题。

  1. 读未提交(Read Uncommited):可以看到其他未提交事务执行的结果。解决了脏写问题
  2. 读已提交(Read Commited):一个事务只能看见已提交事务执行的结果。解决了脏写、脏读问题
  3. 可重复读(Repeatable read)可重复读是mysql默认的隔离级别。一个事务读取前后的数据保证一致,不受其他并发事务修改的影响。解决了脏写、脏读、不可重复读的问题,但还是会有幻读的问题。
  4. 串行化(Serializable):一个事务读取数据期间,禁止其他事务插入、更新、删除操作。解决了脏写、脏读、不可重复读、幻读的问题

3.2 如何理解MVCC?

参考文章:多版本并发控制:MVCC的作用和基本原理

数据库的读写问题,除了采用加锁的方式解决,还可以通过MVCC的方式解决。

MVCC(Multiversion Concurrency Control)多版本并发控制,通过数据行的多个版本管理实现数据库的并发控制。即查询一些正在被另一个事务更新的数据行时,可以看到他们被更新之前的值,不用等待另一个事务释放锁。

所谓的MVCC,就是生成一个ReadView,通过ReadView找到符合条件的记录版本(历史版本由undo日志构建)。查询语句只能读到在生成ReadView之前已提交事务做的修改,不影响其他事务进行写操作,因此可以解决读写问题。相比于加锁的方式解决读写问题,可以大幅提高并发性能。

3.3 快照读与当前读的区别?

快照读不加锁的简单的select都属于快照读,即不加锁的非阻塞读。比如:

SELECT * FROM students WHERE ...

之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于MVCC。他在很多情况下,避免了加锁操作,降低了开销。

当前读:当前读读取的是记录最新的数据。加锁的SELECT,或者对数据进行增删改查都会进行当前读。比如:

SELECT * FROM student LOCK IN SHARE MODE;  # 共享锁
SELECT * FROM student FOR UPDATE;  # 排他锁
INSERT INTO student VALUES ...;  # 排他锁
DELETE FROM student WHERE ...;  # 排他锁
UPDATE student SET ...;  # 排他锁

3.4 InnoDB如何解决脏读、不可重复读和幻读的?

在Innodb中,通过MVCC解决脏读和不可重复读,通过MVCC+间隙锁解决幻读的

脏读的解决。脏读指一个事务可以读取另一个事务未提交的数据,导致数据不一致。在读已提交(Read Committed)隔离级别下,事务只能读取到其他事务已经提交的数据版本。因此,如果一个事务在读取数据时,另一个事务已经修改了这些数据但尚未提交,那么读取事务将不会看到这些未提交的更改。

脏读的解决依赖了Read View,Read View会来告诉我们本次事务应该看到哪个快照,不应该看到哪个快照。简单点说就是:当事务在“读已提交”隔离级别下执行读取操作时, InnoDB获取当前最新的全局事务ID,这个ID表示在当前时刻所有已提交事务的最新状态。InnoDB会检查每个数据行的版本,如果该版本是由一个小于或等于当前事务ID的事务修改的,并且该事务已提交,则这个版本是可见的。这保证了事务只能看到在它开始之前已经提交的数据版本。

不可重读的解决。不可重复读指一个事务读取同一行数据两次,但是在两次读取之间另一个事务修改了该行数据,导致两次读取的结果不同。InnoDB 通过使用 MVCC 来解决不可重复读的问题。在RR这种隔离级别下,当我们使用快照读进行数据读取的时候,只会在第一次读取的时候生成一个Read View,后续的所有快照读都是用的同一个快照,所以就不会发生不可重复读的问题了

幻读的解决。InnoDB中的REPEATABLE READ这种隔离级别通过间隙锁+MVCC解决了大部分的幻读问题,但是并不是所有的幻读都能解读,想要彻底解决幻读,需要使用Serializable的隔离级别。

RR中,通过MVCC机制的,解决了快照读的幻读问题,RR中的快照读只有第一次会进行数据查询,后面都是直接读取快照,所以不会发生幻读

但是,如果两个事务,事务1先进行快照读,然后事务2插入了一条记录并提交,再在事务1中进行update新插入的这条记录是可以更新成功的,这就是发生了幻读。

还有一种场景,如果两个事务,事务1先进行快照读,然后事务2插入了一条记录并提交,在事务1中进行了当前读之后,再进行快照读也会发生幻读。

3.5 undolog会一直存在吗?什么时候删除?

InnoDB会在合适的时间进行统一清理,释放空间。这个过程称为purge。其实背后就是一个purge线程,purge线程在调度进行清理时,会做判断,对于insert undolog就直接清理了,但是对于update undolog,必须确保没有活跃事务需要这些日志用于一致性读。也就是说,即使事务已经提交,但只要还有其他事务在进行一致性读操作并依赖这些日志,update undolog就不能被清理。

那update undolog是如何判断有没有被其他事物依赖的呢?

其实很简单,就一句话:如果一个事务在所有当前活跃的读事务开始之前就已经完成并提交,那么这个事务的Undo Log就可以清理,因为这些读事务不需要它来查看历史数据

4、介绍下InnoDB的锁机制?

4.1 读锁/写锁

对于数据库中并发事务的读-读场景不会引起什么问题,对于写-写、读-写或写-读这些场景可能会一起一些问题。

常见的可以分为2种锁:共享锁(Shared Lock, s lock)和排他锁(Exclusive Lock, x lock)。也叫读锁(read lock)和写锁(write lock)。

  1. 对读取的记录加S锁:其他事务可以加读锁,要加写锁的话需要阻塞排队。
SELECT ... LOCK IN SHARE MODE;
# 或者
SELECT ... FOR SHARE.  #(8.0新特性)
  1. 对读取的记录加X锁:其他事务加读锁和写锁都需要阻塞排队。
SELECT ... FOR UPDATE 

4.2 记录锁、间隙锁、临键锁、插入意向锁

4.2.1 行锁/记录锁

mysql服务器层没有实现行锁机制,行级锁只在存储引擎层实现

优点:锁定粒度小,发生锁冲突概率低,可以实现的并发度高。
缺点:锁的开销比较大,加锁比较慢,容易出现死锁情况。

4.2.2 间隙锁

间隙锁只有在Repeatable Reads这种隔离级别中才会起作用。在Repeatable Reads这种隔离下,对于锁定的读操作(select … for update 、 lock in share mode)、update操作、delete操作时,会进行如下的加锁:

● 对于具有唯一搜索条件的唯一索引,InnoDB只锁定找到的索引记录,而不会锁定间隙。
● 对于其他搜索条件,InnoDB锁定扫描的索引范围,使用gap lock或next-key lock来阻塞其他事务插入范围覆盖的间隙。

4.2.3 临键锁(Next Locks)

临键锁的本质就是一个记录锁和和一个间隙锁的合体,既能保护这条记录,也能阻止别的事务将新数据插入到被保护记录的前面,因此临键锁是一个左开右闭的区间

4.3 表级别的S锁、X锁

在对表执行SELECT、INSERT、DELETE、UPDATE这些DML语句时,Innodb不会添加表级别的S锁或X锁。而在对表执行ALTER TABLE、DROP TABLE这些DDL语句时,其他事务对这个表执行DML语句时会发生阻塞。这个过程是通过在server层使用一种称之为元数据锁(Metadata Lock)结构来实现的。

一般情况下,不用手动使用到表级别的S锁和X锁

LOCK TABLES t READ;  # 手动加S锁
LOCK TABLES t WRITE;  # 手动加X锁

4.4 表级别的意向锁(intention lock)

已经有一个表级别的S锁和X锁了,为什么还需要意向锁呢?

比如事务A对表Table1中的一行加上了行级锁,这时候这行记录就不能写了。事务B申请对Table1增加了表级锁,想要判断事务B能否加锁成功,就需要让事务B在对Table1增加表级锁的时候,先判断一下是不是有事务增加过行级锁。但是,事务B总不能遍历表中数据逐条判断是否有加锁吧?

所以,为了解决这个问题,MySQL引入了意向锁机制。所以,意向锁是数据库管理系统中用于实现锁协议的一种锁机制,旨在处理不同锁粒度(如行锁和表锁)之间的并发性问题

当一个事务请求获取一个行级锁或表级锁时,MySQL会自动获取相应的表的意向锁。这样,其他事务请求获取表锁时,就可以先基于这个意向锁来发现是否有人加过锁,并根据该锁的类型(意向共享锁/意向排他锁)来判断自己是否可以获取锁。这样可以在不阻塞其他事务的情况下,为当前事务锁定资源。

意向锁有两种类型:意向共享锁和意向排他锁。

  1. 意向共享锁: 表示事务打算在资源上设置共享锁(读锁)。这通常用于表示事务计划读取资源,并不希望在读取时有其他事务设置排它锁
  2. 意向排它锁: 表示事务打算在资源上设置排它锁(写锁)。这表示事务计划修改资源,并不希望有其他事务同时设置共享或排它锁

5、介绍下InnoDB的索引?

5.1 索引的常见概念

索引是帮助mysql高效获取数据的数据结构,因此索引是数据结构

数据库中的B+树索引分为聚集索引和非聚集索引。
聚集索引就是按照每张表的主键构造一个B+树,B+树的叶子节点中记录着表中一行记录的所有值。只要找到这个叶子节点也就得到了这条记录的所有值。
非聚簇索引的叶节点中不包含行记录的所有值,只包含索引值和主键的值。要找到数据记录,还要进行一次回表操作。就是先查到主键ID,再通过ID查询所需数据。

5.2 什么是回表,怎么减少回表的次数?

在 InnoDB 里,索引B+ Tree的叶子节点存储了整行数据的是主键索引,也被称之为聚簇索引。而索引B+ Tree的叶子节点存储了主键的值的是非主键索引,也被称之为非聚簇索引。

在存储的数据方面,主键(聚簇)索引的B+树的叶子节点直接就是我们要查询的整行数据了。而非主键(非聚簇)索引的叶子节点是主键的值。

那么,当我们根据非聚簇索引查询的时候,会先通过非聚簇索引查到主键的值,之后,还需要再通过主键的值再进行一次查询才能得到我们要查询的数据。而这个过程就叫做回表。

所以,在InnoDB 中,使用主键查询的时候,是效率更高的, 因为这个过程不需要回表。另外,依赖覆盖索引、索引下推等技术,也可以通过优化索引结构以及SQL语句减少回表的次数。

5.2.1 覆盖索引

覆盖索引(covering index)指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。也可以称之为实现了索引覆盖。即查询的字段都在索引中。

当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O提高效率。

如,表covering_index_sample中有一个普通索引 idx_key1_key2(key1,key2)。

当我们通过SQL语句:

select key2 from covering_index_sample where key1 = ‘keytest’;

的时候,就可以通过覆盖索引查询,无需回表。

如果SQL中查询的信息不包含在联合索引中,那么就不会走索引覆盖。如:

select key2,key3 from covering_index_sample where key1 = ‘keytest’;

联合索引可以通过索引覆盖而避免回表查询,可以大大提升效率,对于频繁的查询,可以考虑将select后面的字段和where后面的条件放在一起创建联合索引

5.2.2 索引下推

索引下推是 MySQL 5.6引入了一种优化技术,默认开启,使用SET optimizer_switch = ‘index_condition_pushdown=off’;可以将其关闭。

官方文档中给的例子和解释如下: people表中(zipcode,lastname,firstname)构成一个索引
SELECT * FROM people WHERE zipcode=’95054′ AND lastname LIKE ‘%etrunia%’ AND address LIKE ‘%Main Street%’;

如果没有使用索引下推技术,则MySQL会通过zipcode=’95054’从存储引擎中查询对应的数据,返回到MySQL服务端,然后MySQL服务端基于lastname LIKE ‘%etrunia%’和address LIKE ‘%Main Street%’来判断数据是否符合条件。

如果使用了索引下推技术,则MYSQL首先会返回符合zipcode=’95054’的索引,然后根据lastname LIKE ‘%etrunia%’来判断索引是否符合条件。

如果符合条件,则根据该索引来定位对应的数据,如果不符合,则直接reject掉。 有了索引下推优化,可以在有like条件查询的情况下,减少回表次数。

当一条SQL使用到索引下推时,explain的执行计划中的extra字段中内容为:Using index condition。

5.2.3 MySQL只操作同一条记录,也会发生死锁吗?

会。因为数据库的锁锁的是索引,并不是记录。

当我们在事务中,更新一条记录的时候,如果用到普通索引作为条件,那么会先获取普通索引的锁,然后再尝试获取主键索引的锁。

那么这个时候,如果刚好有一个线程,已经拿到了这条记录的主键索引的锁后,同时尝试在该事务中去拿该记录的普通索引的锁。这时候就会发生死锁。

6、binlog、redolog和undolog区别?

详细内容可以参考之前的文章:
主从复制简介
Mysql数据库Redo日志和Undo日志的理解

6.1 redolog日志

InnoDb存储引擎在更新数据时,并不会直接刷新到磁盘上,这样效率和速度都很低。而是会顺序IO写入到redolog日志中,即数据从缓冲池(buffer pool)刷入到redo日志中,该事务就算提交成功了。即使此时数据库系统崩溃了,数据库重启的时候,系统也能从redo日志中将刚刚那次事务的变更刷入到磁盘中。同时由于redo日志时顺序写入,因此速度要比随机IO快很多。

因此,redolog日志保证了事务的持久性,数据库崩溃恢复时,可读取redolog日志,保证事务的原子性和一致性。

6.2 undolog日志

undolog日志主要有2个作用:
作用1:回滚数据
回滚数据时逻辑上地将数据回滚到事务开始前,实际磁盘页上的数据是进行了变更的
比如执行insert之前,会写一条delete的语句到undo日志中,一旦事务回滚,则会执行这条delete语句。

作用2:MVCC
InnoDb存储引擎中的MVCC是通过undo日志实现的。当用户读取一行记录时,若该记录已被其他未提交事务占用,当前事务可以通过undo日志读取改行记录之前版本的信息,从而实现了非锁定的读取。

所以,redo日志和undo日志不是正向和反向的关系,他们都属于数据库事务的一种恢复操作,只是解决的问题不同而已。redo日志针对的是事务写过程中,在保证数据持久性的基础上,提升了事务提交的速度。undo日志针对的是事务需要回滚时,能保证数据一致性

6.3 binlog日志介绍

binlog即binary log,二进制日志文件,记录了数据库所有的DDL和DML等数据库更新事件的语句。它以事件形式记录并保存在二进制文件中,通过这些信息,可以再现数据更新操作的全过程。

binlog主要应用场景:

  1. 数据恢复:如果mysql数据库意外停止,可以通过二进制日志文件来查看用户执行了哪些操作,可以根据二进制日志文件中的记录来恢复数据库服务器。
  2. 数据复制:由于日志的延续性和时效性,master把它的二进制日志传递给slaves来达到master-slave数据一致的目的。

因此mysql数据库的数据备份、主备、主主、主从都离不开binlog,需要依靠binlog来同步数据,保证数据一致性。

6.4 binlog与redo log对比

  • redo log 是物理日志,记录内容是“在某个数据页上做了什么修改”,属于innodb存储引擎层产生的。
  • binlog 是逻辑日志,记录内容是语句的原始逻辑,属于mysql server层。
  • 他们都属于持久化的保证,但是侧重点不同
    redo log让innodb存储引擎拥有崩溃恢复能力,持久化保障的的是单台机器
    binlog保证了mysql集群架构的数据一致性,持久化保障的是多台机器

6.5 什么是事务的2阶段提交?

所谓的MySQL事务的2阶段提交,其实是在更新过程中,保证binlog和redolog一致性的一种手段。

过程是:

● Prepare 阶段
○ 这个阶段 SQL 已经成功执行并生成 redolog并写入磁盘,处于prepare阶段
● BinLog持久化
○ binlog 提交,将 binlog 内存日志写入磁盘
● Commit
○ 在执行引擎内部执行事务操作,写入redolog,处于Commit阶段

那么,为什么这个过程需要用2阶段提交的方式呢?
假设我们执行一条SQL语句,修改他的name为jack : update user set name = ‘jack’ where id = 10 。

假设先写入redo log 成功,但是没来得及写入bin log ,系统崩了。在MySQL重启后,可以根据redolog把记录更新成’jack’,但是,binlog由于没写成功,所以他是没有记录下来这次变更的,那么也就意味着,主备同步的时候,是缺了一条SQL的,导致主备库之间数据不一致。

那么,如果换个顺序,先写入binlog成功,但是没来及的写入redolog,系统崩了。在MySQL重启之后,崩溃恢复的时候由于redo log还没写,所以什么都不用做,数据库记录还是旧值。但是因为binlog已经写入成功了,所以在做主备同步的时候,就会把新值同步到备库,就导致了主备库之间数据不一致。

如上面的例子,如果不引入二阶段提交的话,在bin log 和redo log没办法保证一致性的情况下,就会导致主备库之间的数据不一致。

而为了解决这个问题,那就引入了2阶段提交,来整体的控制redo log 和 bin log的一致性写入。

6.6 一次insert操作,MySQL的几种log的写入顺序?

当执行一次insert操作时,MySQL InnoDB引擎的日志写入顺序如下:

1、写入undolog,先将事务修改前的数据记录到Undo Log中。

2、写入redolog,处于prepare阶段 (表示事务已修改但未提交)。

3、写入binlog,将binlog 内存日志数据写入文件缓冲区并刷新到磁盘中。

4、写入redolog,处于commit阶段。

首先,undolog要记录变更前的数据,所以一定要最先执行。

redolog和binlog的写入需要保证原子性,先写 redo log,后写 binlog,中间MySQL 崩溃,会导致主从数据不一致(因为binlog用于主从同步)。 而先写 binlog,后写 redo log,中间MySQL 崩溃,数据页没有更新,事务无法恢复。 所以需要用2阶段提交的方式写入。

7、sql调优

7.1 可能导致索引失效的情况?

  1. 索引列参与了计算。
  2. 对索引列进行函数操作。
  3. 隐式类型转换。原理同对索引列进行函数操作。
  4. 使用OR。如果使用OR的话,并且OR的两边存在<或者>的时候,就会索引失效;如果OR两边都是=判断,并且两个字段都有索引,那么也是可以走索引的。
  5. like操作。"jack%“和"jack%is"这两种可以走索引,但是如果是”%jack%“和”%jack"就没办法走索引了
  6. 不等于比较。
  7. is not null
  8. order by。当进行order by的时候,如果数据量很小,数据库可能会直接在内存中进行排序,而不使用索引。
  9. in。使用in的时候,有可能走索引,也有可能不走,一般在in中的值比较少的时候可能会走索引优化,但是如果选项比较多的时候,可能会不走索引。

7.2 索引失效的问题如何排查?

MySQL的索引失效是一个比较常见的问题,这种情况一般会在慢SQL发生时需要考虑,考虑是否存在索引失效的问题。

在排查索引失效的时候,第一步一定是找到要分析的SQL语句,然后通过explain查看他的执行计划。主要关注type、key和extra这几个字段。

一般来说,比较理想的走索引的话,应该是以下几种情况:

首先,key一定要有值,不能是NULL
其次,type应该是ref、eq_ref、range、const等这几个
还有,extra的话,如果是NULL,或者using index,using index condition都是可以的

如果通过执行计划之后,发现一条SQL没有走索引,比如 type = ALL, key = NULL , extra = Using where

那么就要进一步分析没有走索引的原因了。我们需要知道的是,到底要不要走索引,走哪个索引,是MySQL的优化器决定的,他会根据预估的成本来做一个决定

那么,有以下这么几种情况可能会导致没走索引:

1、没有正确创建索引:当查询语句中的where条件中的字段,没有创建索引,或者不符合最左前缀匹配的话,就是没有正确的创建索引。
2、索引区分度不高:如果索引的区分度不够高,那么可能会不走索引,因为这种情况下走索引的效率并不高。
3、表太小:当表中的数据很小,优化器认为扫全表的成本也不高的时候,也可能不走索引
4、查询语句中,索引字段因为用到了函数、类型不一致等导致了索引失效

7.3 如何进行SQL调优?

一般一个SQL慢,可能有以下几种原因:

1、索引失效
2、多表join
3、查询字段太多
4、表中数据量太大
5、索引区分度不高
6、数据库连接数不够
7、数据库的表结构不合理
8、数据库IO或者CPU比较高
9、数据库参数不合理
10、事务比较长
11、锁竞争导致的等待
12、深分页问题

所以,一次完整的SQL调优,一般需要考虑以上几个因素,一般会涉及到其中的一个或者多个问题。那么就逐个优化。

8、主从复制

8. 1 MySQL主从复制的过程

主从复制详细过程参考之前的一篇文章:主从复制简介

1、从服务器在开启主从复制后,会创建出两个线程:I/O线程和SQL线程
2、从服务器的I/O线程,会尝试和主服务器建立连接,相对应的,主服务中也有一个binlog dump线程, 是专门来和从服务器的I/O线程做交互的。
3、从服务器的I/O线程会告诉主服务的dump线程自己要从什么位置开始接收binlog
4、主服务器在更新过程中,将更改记录保存到自己的binlog中,根据不同的binlog格式,记录的内容可能不一样。
5、在dump线程检测到binlog变化时,会从指定位置开始读取内容,然后会被slave的I/O线程把他拉取过去。
6、从服务器的I/O线程接收到通知事件后,会把内容保存在relay log中。
7、从服务器还有一个SQL线程,他会不断地读取他自己的relay log中的内容,把他解析成具体的操作,然后写入到自己的数据表中。

在这里插入图片描述

9、驱动表

9.1 MySQL的驱动表是什么?MySQL怎么选的?

驱动表是表连接中的基础表,也就是通过驱动表的数据结果集作为循环基础数据,然后一条一条的通过这个结果集的数据作为过滤条件到被驱动表中查询数据,然后再做合并。那么,也就意味着:驱动表在SQL语句执行的过程中先读取。而被驱动表在SQL语句执行的过程中后读取

当我们知道MySQL的join的原理之后,其实就可以很容易的知道,驱动表的选择会决定着一条SQL的执行效率。所以,一条SQL中,该使用哪张表作为驱动表,其实是优化器决定的。

MySQL的优化器选择驱动表的原则是:更好的访问性能和筛选性能,所以,通常情况下, 会做以下几个方面的考量:

1、表大小
小表作为驱动表可以更快地被扫描和匹配。所以优化器倾向于选择较小的表作为驱动表

当然,如果两张表都没有索引,那么都需要全表扫描,那么在nested loop join下就是笛卡尔积, 那么小表驱动大表的提升就微乎其微。

但是如果我们考虑到有索引的情况、hash join的情况等的话,小表驱动大表还是优选的,所以总体来说,数据库还是会倾向于小表驱动大表。

2、索引
在MySQL中,索引能大大的影响SQL的查询效率,所以选择可以利用索引进行加速访问的表作为驱动表可以提升效率。

3、where条件:如果查询中包含过滤条件,优化器会选择能够使用过滤条件进行筛选的表作为驱动表,以减少后续的匹配操作。

4、连接类型:根据连接类型,INNER JOIN、LEFT JOIN、RIGHT JOIN等,优化器可能会做一些选择。比如left join会选择左表作为驱动表,主要是因为LEFT JOIN要返回左表中的所有记录,而右表中的匹配记录是可选的。通过以左表作为驱动表,可以确保返回左表中的所有记录。
○ left join: 左表是驱动表,右表是被驱动表
○ right join: 右表是驱动表,左表是被驱动表
○ inner join: 表数据量较小的表会由mysql自动选择作为驱动表

9.2 如何判断哪张表是驱动表

可以使用explain查看一下SQL的执行计划。在输出的执行计划中,排在第一行的表是驱动表,排在第二行的表是被驱动表。

+----+-------------+---------------+--------+------------------+---------+---------+--------------------------------+------+-------------+
| id | select_type | table         | type   | possible_keys    | key     | key_len | ref                            | rows | Extra       |
+----+-------------+---------------+--------+------------------+---------+---------+--------------------------------+------+-------------+
|  1 | SIMPLE      | orders        | ALL    | NULL             | NULL    | NULL    | NULL                           | 100  | Using where |
|  1 | SIMPLE      | order_details | ref    | order_id         | order_id| 4       | db_name.orders.order_id       | 2    | NULL        |
+----+-------------+---------------+--------+------------------+---------+---------+--------------------------------+------+-------------+

如上图,orders表为驱动表,order_details表为非驱动表。

上面的执行计划中,有一个id字段,这个是执行计划中每个操作的唯一标识符。对于一条查询语句,每个操作都有一个唯一的id。但是在多表join的时候,一次explain中的多条记录的id是相同的。而排在前面的表会作为驱动表先执行。

9.3 left join 一定是左表作为驱动表吗?

不一定,因为这是MySQL优化器决定的。当右表的数据量远远小于左表时,通过以右表作为驱动表可以更快地完成匹配操作。当右表上存在适合的索引或过滤条件,也可以通过右表作为驱动表来利用这些优化。

9.4 MySQL 为什么是小表驱动大表,为什么能提高查询性能?

假设我们有两个表:employees(1000 条记录)和 departments(10 条记录),并且要进行以下查询:

SELECT e.name, d.department_name
FROM employees e
JOIN departments d ON e.department_id = d.id

在不考虑hash join等其他链接方式,只考虑nested loop join的情况下,其实执行的次数是笛卡尔积,即:

for(1000){for(10)
}for(10){for(1000)
}

但是,假设employees.department_id和departments.id 都有索引的情况下,就不一样了,因为索引的查询是比较快的,他的复杂度是log(n)。那么:

大表驱动小表,复杂度为:O(1000) * O(log 10)
小表驱动大表,复杂度为:O(10) * O(log 1000)

这样一算的话,就非常清楚了,肯定是小表驱动大表的整体的复杂度更低!

参考链接:
1、https://blog.csdn.net/qq_46769552/article/details/149599675
2、https://www.yuque.com/hollis666/wk6won/gux80i
3、https://www.yuque.com/hollis666/wk6won/ctpkpgi7gxkgklk8

面试题:
1、MySQL中的事务隔离级别?
2、什么是数据库范式,为什么要反范式?
3、binlog、redolog和undolog区别?
4、可能导致索引失效的情况?
5、MySQL主从复制的过程?

http://www.dtcms.com/a/393120.html

相关文章:

  • AWS 全景速查手册
  • 小米Openvela城市沙龙
  • Python数据分析:求矩阵的秩。啥是矩阵秩?听故事学线代并用Python实现,娘来太容易学会了!
  • UI Toolkit自定义元素
  • redis未授权访问-漏洞复现
  • PR调节器与PI调节器的区别
  • Unity核心概念⑫:碰撞检测
  • 【读论文】面向工业的ASR语音大模型
  • 重谈IO——五种IO模型及其分类
  • 数据库造神计划第十七天---索引(2)
  • 【开题答辩实录分享】以《车联网位置信息管理软件》为例进行答辩实录分享
  • (3)机器学习-模型介绍
  • 如何在 Ubuntu 20.04 LTS 上安装 MySQL 8
  • MuMu模拟器使用入门实践指南:从ADB连接到Frida动态分析
  • 条款5:优先选用auto, 而非显示类型声明
  • 强化学习原理(一)
  • 解读43页PPT经营分析与决策支持系统建设方案交流及解决经验
  • ubuntu24设置证书登录及问题排查
  • MySQL 备份与恢复完全指南:从理论到实战
  • 2011/12 JLPT听力原文 问题四
  • 实战free_s:在高并发缓存系统中落地“内存释放更安全——free_s函数深度解析与free全方位对比”
  • 异步通知实验
  • 用 C 语言模拟面向对象编程
  • 联邦学习论文分享:FedKTL
  • 智能体分类:从反应式到混合式的架构演进与实践
  • 【面板数据】上市公司企业ZF连接度数据集(1991-2024年)
  • 让codex像 cladue code一样 自动牛马
  • NeurIPS 2025 spotlight Autonomous Driving VLA World Model FSDrive
  • 多线程JUC
  • Qwen3技术之模型后训练