MySQL

积累知识,分享智慧,让成长之路不再孤单

基础

MySQL 语言功能分类

  • DDL:数据定义语言(create、drop、alter)
  • DML:数据操作语言(select、update、insert、delete)
  • DCL:数据控制语言(grant、revoke、rollback、commit)

MySQL 字段类型

  • 数值类型
    • 整型(tinyint、int)
    • 浮点型(float、double)
    • 定点型(decimal)
  • 字符串类型
    • char
    • varchar
  • 日期类型
    • year
    • time
    • date
    • timestamp

char 和 varchar 的区别

  • 相同:都是存储字符串的数据类型
  • 不同:在存储空间上
    • char 是定长的数据类型,长度固定,如果实际存储长度小于指定的长度,会在末尾添加空格补齐。长度范围为 0~255。不会造成页分裂,不会有内存碎片。
    • varchar 是可变长的数据类型,长度不固定,存储实际的字符串内容,范围是 0~65535(小于65535),需要额外 1-2 字节存储长度信息,可能导致页分裂。

null 和 ‘’ 的区别

  • null 代表一个不确定的值,需要占用空间
  • ‘’ 长度为 0,不占用空间

MySQL 结构

  • server 层
    • 负责 MySQL 客户端与服务端建立连接、分析和执行 SQL 语句
    • 包括连接器、查询器、解析器、预处理器、优化器、执行器等
  • 存储引擎层
    • 负责数据的存储和提取,服务器通过 API 与存储引擎进行通信

MySQL 的 SQL 执行流程

以查询为例:
客户端与服务建立连接后,会先查询缓存(若使用此功能),如果找不到,就进入解析器阶段,对 SQL 语句进行词法分析、语法分析,生成一棵语法树,再由预处理器检查对应表或字段是否存在,之后进入优化器,确定执行计划(确定使用哪种索引),最后由执行器执行,即调用存储引擎查询结果。

MySQL 数据的存储

  • 存储引擎
    MySQL 存储引擎采用的是 插件式架构 ,支持多种存储引擎。存储引擎是基于表的,而不是数据库

    • InnoDB 存储引擎:具备外键、事务功能
      • 数据文件结构: 表名.frm 存储表结构, 表名.ibd 存储数据和索引 (8.0之后,合并为 表名.ibd)
      • 不仅缓存索引,也缓存真实数据
    • MyISAM 非事务处理存储引擎,不支持行级锁,外键,访问效率高
      • 数据文件结构:表名.frm 存储表结构, 表名.MYD 存储数据, 表名.MYI 存储索引
      • 只缓存索引,不缓存真实数据
      • B+ 树的叶子结点并不存储完整的用户记录,而是 主键值+数据记录的地址,因此其索引方式是非聚簇的;MyISAM 的回表速度是非常快的,是直接获取地址偏移量到文件中取数据
    • MyISAM 和 InnoDB 对比,二者均是默认采用 B+ 树索引,InnoDB 的数据文件本身就是索引文 件,而 MyISAM 索引和数据文件是分离的,InnoDB 最小的锁粒度是行锁,MyISAM 是表锁
    • Memory 引擎:置于内存的表
    • NDB引擎:MySQL集群专用存储引擎
  • 表空间
    MySQL 的每张表的数据都是保存在 .ibd 文件中的,该文件也称为独占表空间。
    表空间的结构:由段、区、页、行组成。
    在这里插入图片描述

页: InnoDB 是以页为单位进行读写的,默认每个页的大小为 16 KB。
区:在数据量大的时候,为索引分配空间时以区为单位进行分配,每个区的大小为 1MB,64个页划分为一个区,这样使得 B+ 数中每层链表中相连的页的物理位置相邻,能使用顺序 I/O。
段:表空间由各个段组成,一般分为数据段,索引段和回滚段等。
● 索引段:存放 B+ 树非叶子节点的区的集合
● 数据段:存放 B+ 树叶子节点的区的集合
● 回滚段:存放回滚数据的区的集合

  • 行格式
    InnoDB 行格式:
    表中的数据存储是以行为单位进行数据插入,这些记录在磁盘上的存放方式称为行格式
    InnoDB 提供的行格式:
    • Redudant
      MySQL5.0 版本之前 InnoDB 的行记录存储方式,会把记录中所有的列包括隐藏列的长度信息都存储到字段长度偏移表中

    • Compact
      在这里插入图片描述MySQL5.0 之后默认格式,除了保存字段值外,还会保存额外信息,是一种紧凑的行格式。
      (包括变长字段长度列表、null值列表、记录头信息、真实数据)适合处理大量可变长字段的数据
      记录的真实数据中包含三个隐藏字段:row_id: 隐藏字段 id,trx_id: 事务 id,roll_ptr: 回滚指针
      记录头: 存储下一条记录信

    • Dynamic

    • Compressed

Dynamic 和 Compressed 都是紧凑的行格式,与 Compact 结构类似,在其基础上进行的改进。
MySQL 中除了 TEXT,BLOB 外,其他所有列占用的字节长度不超过 65535 个字节,对于 varchar,其还有变长字段和 NULL需要占用空间,所以总长度小于 65535 字节。(需要看字符集编码)
行溢出:一个页存不了一条记录,这种现象称为行溢出。溢出后的数据会存储到溢出页中。

  • 数据页
    InnoDB 的数据是以 页 为单位进行读取的,记录是按行存储的;多个页使用双向链表关联,每页记录按主键值从小到大组成一个单向链表,每页会生成一个页目录
    在这里插入图片描述在这里插入图片描述

Count(*) 和 Count(1) 的区别

以 InnoDB 存储引擎为例:
从性能上分析:Count(*) = Count(1) >= Count(主键) > Count(字段)
count() 是一个聚合函数,用于统计不为 null 的记录个数,count(常数)相当于为表再加一列,值为常数
count( * ) 相当于 count(0) , 因此与 count(1) 一样,在执行过程中,它们会走索引,如果有二级索引,则走二级索引(key_len 最少的),没有二级索引走聚簇索引,因此其性能是比 count(主键) 快,而count(字段) 则是全表扫描,性能最差。
MyISAM 是 o(1)

非关系型数据库

  • 键值型
  • 文档型
    一般不保证 ACID

大数据的 NoSQL(HBase)一般采用 LSM,日志结构合并树,利用磁盘顺序读写能力,实现一个多层读写的 存储结构,是分层有序的面向磁盘的结构, LSM是日志结构(Log-Structured)的,打印日志是一行行往下写,不需要更改,只需要在后边追加就好了。其次会进行合并树(Merge-tree),合并就是把多个合成一个,自上而下。基于以上特点,LSM-tree其实就是一个多层结构,像一个喷泉树一样,上小下大。

SQL 语句

SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5
FROM player JOIN team ON player.team_id = team.team_id # 顺序 1
WHERE height > 1.80 # 顺序 2
GROUP BY player.team_id # 顺序 3
– 若有聚合函数,在 group by 之后执行
HAVING num > 2 # 顺序 4
ORDER BY num DESC # 顺序 6
LIMIT 2 # 顺序 7

  • 计算 TOP N
-- 查询各部门的最高工资
-- 方式一:连接查询
select *
from emp e
         join (select deptno, max(sal) max_sal
               from emp
               group by deptno) t
              on e.deptno = t.deptno
where e.sal = t.max_sal;
-- 方式二: 相关子查询
select *
from emp e
where e.sal = (select max(sal)
               from emp es
               where es.deptno = e.deptno);
-- 以下方式无法取多个最高工资相同的,只能取一个最高的
-- 方式三:窗口函数
select *
from (select *, row_number() over ( partition by deptno order by sal desc ) rn
      from emp) t_rn
where t_rn.rn < 2;
-- 方式四:order by
select *, max(sal) ms
from emp
group by deptno
order by ms;
  • 深度分页
    limit 0,100 和 limit 1000000,100 一样吗?(深度分页问题)
    MySQL 中 limit m, n 是先读取前面 m + n 条记录,然后再抛弃前面 m 条记录,返回 n 条数据,m 越大,偏移量越大,性能越低。
    • 优化
      • 使用子查询和 join 优化
      -- 例
      select id,name,age from t1 where name = '张三' limit 1000000, 10-- 可以通过子查询以及 join 内连接,先查询出 1000000,10 的 id,然后再查对应的字段;也就是先得到目标结果的 id ,再进行回表
      select id,name,age 
      from t1 
      inner join (
        select id
        from t1
        where name = '张三'
        order by id
        limit 1000000,10
      ) as t2 on t1.id = t2.id
      

数据库范式

● 1 NF(第一范式):数据表中字段不可再分
● 2 NF(第二范式):非主属性完全依赖于主键
● 3 NF(第三范式):非主属性不能依赖于其他非主键
● BCNF(巴斯-科德范式):消除了主属性对候选键的部分依赖或者传递依赖关系

索引

索引的数据结构:

索引:快速查找数据记录的数据结构,相当于书的目录。
  • 优点:提高查找数据的效率
  • 缺点:创建索引和维护索引需要时间,也需要一定空间。(增删改时效率低)
    索引是在存储引擎中实现的,不同存储引擎支持不同的索引类型。

B+ 树是一种多路平衡查找树,在叶子节点上存放数据和索引,非叶子节点只存放索引,B+ 树存储千万级别的数据只需要 3~4 层高度即可,也就意味着从千万级别的数据表中查询数据最多需要 3~4 次 I/O
设计数据库的索引结构:既要考虑增删改查的时间复杂度,更重要的是考虑磁盘的 I/O 次数。

索引的分类

按数据结构分类

  • B 树:多路平衡查找树,相对于 B+ 树,B+ 树只在叶子节点上存储数据,而 B 树在非叶子节点上也存储数据,在相同的磁盘 I/O 次数下,B+ 树能查询到更多的节点。并且,B+ 树叶子节点采用双向链表,适合范围查询,而 B 树 无法做到这点。
  • Hash 表:在等值查询时时间复杂度为 O(1) ,但不适合范围查找。
  • 二叉树:查询复杂度是 O(log2 n),树深度很深,I/O 次数多。B+树是 O(logd N)
  • AVL:需要频繁的旋转,性能低
  • 红黑树:自平衡二叉查找树,并不追求严格的平衡,而是大致的平衡,效率低。
  • Full-Text

按物理存储分类

  • 聚簇索引:所有的用户记录都存储在叶子结点(所有列包括隐藏列)( 索引即数据,数据即索引)
    聚簇索引的选取规则:主键 —> 唯一索引 —> rowid 隐藏字段,无需创建,默认就有
  • 非聚簇索引:(二级索引) 叶子节点存放的是索引和主键值

回表:从二级索引中的 B+ 树中先找到主键值,再去聚簇索引中的 B+ 树找到完整的用户记录,此过程即为回表
索引覆盖:如果查询的数据在二级索引中都能找到,无需进行回表的过程就是索引覆盖
联合索引:同时对多个列建立索引,为选定多个列的索引建立一棵 B+ 树。
联合索引的最左匹配原则,在遇到范围查询时就会停止匹配,范围查询的字段可以用到联合索引,范围后的字段无法使用到联合索引
索引下推:在联合索引遍历的过程中,对联合索引中的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。(执行计划中 extra 为 Using index condition 即为使用了索引下推的优化)

按字段分类

  • 主键索引
  • 唯一索引
  • 普通索引
  • 前缀索引

索引的适用场景

  • 字段具有唯一性
  • 经常用于 where 查询条件的字段
  • 经常用于 group by 和 order by 的字段
    不适合:
  • 不经常出现在 where、group by 和 order by 中的字段
  • 区分度不高的,比如性别字段,存在大量重复的数据
  • 数据量少
  • 频繁更新的字段

索引优化

● 查询优化
○ 使用 join 代替子查询
○ 采用左外连接时,对右边建立索引,避免全表扫描
○ 小表驱动大表
○ 采用内连接
● 使用前缀索引
● 使用覆盖索引
● 主键自增,避免了页分裂
● 索引设置为 not null,
○ 对于索引的统计、值计算(count(name) 不计算 null)变得复杂,
○ 占用存储空间
● 避免索引失效
○ 涉及到计算、函数、类型转换时会导致索引失效
○ 违反最左匹配原则
○ 使用模糊查询时可能导致索引失效
○ 使用 or 时,若前后存在非索引的列也会导致索引失效
○ 使用 != ,is null 可以使用索引,is not null 无法使用索引

执行计划

在这里插入图片描述在这里插入图片描述

extra:
● Using where:使用了 where 过滤
● Using filesort:无法通过索引完成排序,需要通过文件进行排序,性能差(使用 group by 可能出现)
○ (一般在内存中进行排序)
● Using index:使用了覆盖索引,无需回表
● Using temporary:使用了临时表,一般在 group by 或 order by 出现,应避免此种情况
EXPLAIN 的输出格式:普通表格、json、tree、可视化 workbench

事务

表示数据库的一组数据操作,要么都执行,要么都不执行。
事务的 ACID 特性: Atomicity、Consistency、Isolation、Durability
原子性、一致性、隔离性、持久性

原子性:一个事务中的操作要么都成功,要么都失败(由 undo log 实现)
一致性:事务执行前后数据库的状态保持一致,满足完整性(由原子性、隔离性和持久性实现)
隔离性:多个事务之间是相互隔离的(由 MVCC 或锁机制实现)
持久性:事务提交后对数据的修改是永久性的(由 redo log 实现)

并发事务可能出现的问题

● 脏写:事务 A 修改了事务 B 未提交的数据,若事务 A 提交后,事务 B 回滚,事务 A 发现数据还是原来的。
● 脏读:事务 A 读到事务 B 尚未提交的数据。
● 不可重复读:事务 A 读了两次某个字段值,事务 B 在期间进行了修改,导致事务 A 两次读到的数据不同。
● 幻读:A在读的过程中突然多了几条数据。(若少了几条数据仍属于不可重复读)

事务的隔离级别

● 读未提交:可读到其它事务未提交的数据
● 读已提交:一个事务只能看见已提交事务的数据
● 可重复读:在一个事务中进行了两次或多次读,即使被其它事务修改过,仍是原来的数据。
● 串行化:对记录进行加锁,一个事务执行期间,禁止其它事务对该表执行插入、更新和删除的操作

在可重复读情况下:不能避免幻读现象
● 快照读:通过 MVCC 解决了幻读
● 当前读:通过 记录锁 + 间隙锁 解决了幻读 (解决大部分情况)

存在幻读情况:
情况一:

-- 事务 A
begin;
-- step 1
select * from user where id = 5;
-- 结果为 empty

-- step 3
update user set name = 'ls' where id = 5;
select * from user where id = 5;
-- 查询到结果为  5, ls, ...

-- 事务 B
begin;
-- step 2
insert into user values(5, 'zs', '...');
commit;

在这里插入图片描述情况二:

-- 事务 A
begin;
-- step 1
select * from user where id > 5;
-- 得到 2 条记录

-- step 3
select * from user where id > 5 for update;
-- 得到 3 条记录

-- 事务 B
-- step 2
insert into user values(10, 'zs', '...');

在这里插入图片描述

上述这两种情况,在某个事务中,其他事务插入数据后,都进行了当前读的操作,导致之后的快照读出现幻读
当前读是不使用读视图,而是直接从最新版本的数据中读取,第一次生成的读视图在当前读后会发生变化。
如果在事务中进行了当前读,读视图会被刷新

MVCC

MVCC:多版本并发控制,是通过数据行的多个版本管理来实现数据库的并发控制。
是采用乐观锁的方式解决不可重复读和幻读

MVC 的实现是依赖于:Read View 、Undo log 和隐藏字段
多个事务对同一记录进行更新会产生多个历史快照,这些历史快照保存在 undo log 日志中,如果有事务要查询这个行记录,就需要 Read View 来确定读取哪个版本的记录。
读未提交则直接读取最新版本即可

  • 读已提交在每次读取数据前都会生成一个 Read View
  • 可重复读只在第一次读取数据时生成一个 Read View

分类

从锁的粒度来分:
● 全局锁:整个数据库处于只读状态
● 表级锁:表锁,元数据锁(MDL),意向锁,自增锁(AUTO-INC)
● 行级锁

  • 全局锁
-- 加锁
flush tables with	read lock;
-- 释放锁
unlock tables;
-- 使用场景:进行数据库备份;在此期间是只读状态,若是需要想要执行修改操作,
--    在可重复读的隔离级别下,在备份数据库之前开启事务,会创建 Read View,
--    整个事务执行期间都会使用这个 Read View,业务仍能执行修改操作。
--    使用 mysqldump 时添加参数 --single-transaction 即可。 
  • 表级锁
    在这里插入图片描述

  • 行级锁
    在这里插入图片描述
    MySQL 加行级锁:
    加锁的对象是索引

加锁规则:
● 加锁的基本单位是 next-key lock,是记录锁和间隙锁的组合,左开右闭区间
● 查找过程中访问到的记录才加锁,任何二级索引上的锁,或者非索引列上的锁,最终都要回溯到主键上,在主键上也要加一把锁。
优化:在一些场景下 next-key lock 会退化为记录锁或间隙锁
● 索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。即 InnoDB 扫描的是一个主键或是一个唯一索引的话,那么 InnoDB 只会采用记录锁方式来加锁
● 索引上(不一定是唯一索引)的等值查询,向右遍历时且最后一个值不满足等值条件的时候, next-key lock 退化为间隙锁
唯一索引上的范围查询会访问到不满足条件的第一个值为止
间隙锁的获取是兼容的,也就是两个事务都能获得相同的间隙锁,但是无法进行写操作
对于 X 型, next-key lock 是互斥的,对于 S 型,如果右边区间是 +∞,则是兼容的
唯一索引等值查询时:
● 记录存在:添加的是记录锁
● 记录不存在:添加的间隙锁(左开右开区间)
如果是二级索引,除了在二级索引上加锁,也会对主键进行加锁
唯一索引范围查询时:
● 记录不存在:间隙锁
● 记录存在:由查询区间决定,若是开区间,则是间隙锁,否则不变
非唯一索引等值查询时:
在加锁时,会对非唯一索引和主键都加锁,但是对于主键索引加锁,只有满足查询条件的才会对主键进行加锁。
● 记录存在:直到扫描到第一个不符合条件的二级索引,满足条件的是加 next-key lock,第一个不满足条件的退化为间隙锁,在符合条件的记录的主键上加记录锁
● 记录不存在:不满足条件的,只会对二级索引加间隙锁,不会对主键加锁
非唯一索引范围查询时:
不会退化,都是加 next-key lock 锁,左开右闭区间
没有索引查询时:
对每条记录都加 next-key lock 锁,相当于锁全表,因此在对非索引列加独占锁时,会锁全表。
不是表锁,而是全表扫描对索引进行加锁。
避免这种情况,在 update 或 delete 时 where 条件中必须有索引或者有 limit
limit 会查询到满足条件后的记录就不再进行加锁了

死锁

create table `order` (
  id int not null auto_increment,
  order_no int default null,
  create_date datetime default null,
  primary key (id),
  key idx_order (order_no)
) ENGINE=InnoDB ;

begin;
-- step 1 :查询订单 1007 是否存在
select * from order where order_no = 1007 for update;
-- step 4:若没有,插入订单记录
insert into order(order_no, create_date) values(1007, now());
-- 阻塞 ...


begin;
-- step 2 :查询订单 1008 是否存在
select * from order where order_no = 1008 for update;
-- step 3:若没有,插入订单记录
insert into order(order_no, create_date) values(1008, now());
-- 阻塞 ...

按照加锁原则,事务 A 会加间隙锁,同样事务 B 也会加间隙锁
在插入时会尝试先获取插入意向锁,获取失败阻塞等待
间隙锁无论是 S 型还是 X 型没什么区别,二者兼容,两个事务的间隙锁之间是相互兼容的
Insert 语句的加锁:
在其正常执行过程中是不会生成锁结构的,它是靠聚簇索引记录的 trx_id 隐藏列作为 隐式锁来保护记录的
当事务需要加锁时,如果不会发生冲突,就会跳过加锁环节,即隐式锁,有冲突才加锁。
隐式锁转为显示锁的场景:
有间隙锁
唯一键
避免死锁
设置事务等待锁的超时时间,默认是 50 s
开启主动死锁检测,检测到死锁后,回滚某个事务
使用 RC 替换 RR 避免因间隙锁等带来的死锁情况
MySQL 只操作同一条记录,也会发生死锁吗?
若一个事务更新操作时,先获取到的是普通索引,会尝试获取主键的索引,若有其他事务已经获取到主键索引,而又要获取普通索引的锁时就会发生死锁。

日志

事务的原子性、一致性和持久性由事务的 redo 日志和 undo 日志来保证
undo log(回滚):是 InnoDB 存储引擎层生成的日志,主要用于事务回滚以及 MVCC(逻辑日志)
redo log(重做):是 InnoDB 存储引擎层生成的日志,保证事务中的持久性,用于故障恢复等(物理日志)
binlog(归档):是 Serve 层生成的日志,用于数据备份和主从复制(记录 DDL、DML 语句)

undo log

事务在执行过程中,出现异常等现象,如何回滚到事务之前的数据
解决:在每次事务执行过程中,记录原来的数据到日志中用于回滚
发生回滚时:插入对应删除,删除对应插入,更新对应更新就值
● 每条记录的更新操作生成的日志都会记录在 undo log 里,这些日志记录通过 roll_pointer 形成一个版本链,而通过 trx_id 确定该记录是被哪个事务修改的。
● 在实现 MVCC 时是通过 Read View 确定 undo log 版本链中的记录。

redo log

Buffer Pool 是基于内存的,为了保证数据的可靠性,当数据进行修改后,会先更新内存,再记录到 redo log 中。采用 WAL 的思想,先写日志,再写磁盘。在事务提交时先持久化 redo log。
在内存中修改 undo 页后也会记录对应的 redo log
区别在于:redo log 记录事务执行完成后的数据状态,也就是更新之后的数据,而 undo log 记录事务开始前的数据,即更新之前的数据。
事务开始后,在执行事务操作时,会记录 undo log,也会记录 redo log,undo log 用于事务回滚,redo log 用于事务持久化。
redo log 要写入磁盘,数据也要写到磁盘
是因为数据写入磁盘时是属于随机写,即先找到位置再写入,效率低,而 redo log 是追加的方式,是顺序写,效率更高,需要 redo log 就是为了保证 MySQL 在任何时候崩溃而能保证数据不丢失。
redo log 也并不是直接写入磁盘
redo log 也有自己的缓冲池,也不是在事务提交时写入磁盘,如果 redo log 中记录的数据过大,等到事务提交时写入磁盘也是费时间的。因此它也有自己的刷盘时机,比如 redo log buffer 写入空间大于一半时、每隔 1s 以及每次事务提交时。
redo log 写满
它是有一个重做日志组,包含两个 redo log 文件,一个写完,再写另外一个,再覆盖第一个,依次循环,会记录要擦除的位置,如果追上擦除位置,就会阻塞,等脏页数据刷入磁盘之后继续写。

bin log

MySQL 执行完一条更新操作后,在 Serve 层会生成一条 bin log,在事务提交时写入 binlog 文件
bin log 有三种格式:
● statement:记录每条语句
● row:记录数据最终的结果
● mixed:混合
binlog 主要是备份恢复和主从,误删数据库可以通过 binlog
redolog 主要是停电故障恢复
Buffer Pool
MySQL 中数据是存储在磁盘的,当访问这些数据时就需要从磁盘中加载数据到内存,因此 InnoDB 存储引擎通过缓冲池(Buffer Pool)来提高数据读写性能。
● 读取数据时先去 Buffer Pool 中寻找,找不到再从磁盘读取
● 修改数据时到 Buffer Pool 中修改,修改数据所在的页,并将该页设置为脏页,表示已经修改(内存和磁盘数据不一致),然后选择合适的时机将脏页写入磁盘。
InnoDB 是将数据划分了若干个页,以页为单位与内存交互。
对于 undo log 日志,会写入 Buffer Pool 中的 undo 页中。
主从复制
过程: 从节点会到主节点上读取 binlog 实现数据同步.
步骤: 主节点将数据写入到 binlog 中, 从节点读取 binlog 拷贝到它的中继日志, 再读取中继日志读取数据实现数据同步
数据一致性问题解决:
● 异步复制: 客户端 commit 后不需要从库返回结果, 但可能导致数据不一致。(主库宕机).
● 半同步复制: 客户端 commit 后需要等待至少一个从库接收到 binlog 并写入中继日志,再返回结果给客户端。
● 组复制: 客户端 commit 后需要等待半数以上的从库收到 binlog 并写入中继日志后, 再返回结果.
组复制 (MGR) 是基于 Paxos 协议实现的. Zookeeper 也是此协议.
一条更新语句的执行流程
● sql 语句的执行流程
● 涉及到日志的操作流程
在开启事务后,

  1. 首先会从 Buffer Pool 中读取数据,若不存在则从磁盘中加载到 Buffer Pool 中
  2. 在进行修改之前,会在 undo log 中记录修改前的数据,undo log 是回滚日志,用于失败回滚
  3. 执行 update 时会先更新 Buffer Pool中的数据,但不会直接写入磁盘,InnoDB 会将修改后的数据页状态设为脏页,表示已经修改
  4. 在修改 Buffer Pool 中数据同时也会将修改操作写入到 redo log buffer 中。
  5. 在事务提交时会将 redo log 写入磁盘,保证事务的持久性。
  6. 在事务提交后,InnoDB 会将 Buffer Pool 中的脏页写入磁盘,保证数据的持久性,但这个写入过程并不是立即执行的,是由一个后台线程异步执行的,所以可能会有延迟。
  7. 在提交过程中,InnoDB 会将事务的提交信息记录到 binlog 中,用于主从复制,同步数据。
    redo log在事务执行过程中可以不断写入,bin log 只有在事务提交时才会写入
    两阶段提交
    如果在写完 redo log 之后,binlog 日志写期间发生异常,则会导致从库数据不一致。
    为了解决两份日志之间的逻辑一致性问题,InnoDB 存储引擎使用两阶段提交方案。
    即 将 redo log 的写入拆成两个步骤 prepare 和 commit;
    MySQL 根据redo log日志恢复数据时,发现 redo log 还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务。
    若 redo log设置commit阶段发生异常,并不会回滚事务,虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。也就是在事务提交时 redo log 是prepare 阶段,当 bin log 写入后,再将 redo log改为 commit。

中继日志

在主从复制时使用。

慢查询日志

默认情况 MySQL 没有开启慢查询日志,需要手动开启,耗费性能。
默认的响应时间是 10s ,long_query_time 为 10s
● 开启慢查询日志
set global slow_query_log =‘ON’;
● 修改 long_query_time 阈值
show variables like ‘%long_query_time%’;
set global long_query_time = 1;
● 查询慢查询数目
SHOW GLOBAL STATUS LIKE ‘%Slow_queries%’;
● 慢查询日志分析工具
mysqldumpslow

错误日志

记录了 MySQL 服务器启动、停止等期间的错误信息

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值