[MySQL]基础

数据模型

数据模型与现实世界中的模型一样,是对现实世界数据特征的一种抽象。比如一个学生的特征暴扣姓名、年轻、学号等,这些特征被称为属性,属性具有以下特点:

  • 属性不可再分。
  • 一个实体的属性可以有很多歌。
  • 用于唯一区分不同实体的属性,称为key,比如每个学生的学号都是不一样的。
  • 属性取值可以有一定的约束,比如性别只能是男或女。

  • 属性追安可以具有一定的联系:

    • 一个老师教很多学生就是一对多(1:n)
    • 学生与学好唯一对应(1:1)
    • 一个老师可以教多个学生,一个学生也可以有多个老师,多对多。(n:m)

数据库的规范化

第一范式(1NF)

第一范式是指数据库的每一列都是不可分割的基本数据项,而下面这样的就存在可分割的情况:

  • 学生(姓名,电话号码)

电话号码实际上包括了家用座机号码和移动电话,因此它可以拆分为:

  • 学生(姓名,座机号码,手机号码)

满足第一范式是关系数据库最基本的要求。

第二范式(2NF)

第二范式要求表中必须存在逐渐,且其他属性必须完全依赖于主键,比如:

  • 学生(学号,姓名,性别)

学号是每个学生的唯一标识,每个学生都有着不同的学号,因此次表中存在一个主键,并且每个学生的所有属性都依赖于学号,学号发生改变表示学生发生改变,姓名和性别都会因此改变,所以此表满足第二范式。

第三范式(3NF)

再满足第二范式的情况下,所有的属性都不传递依赖于主键,满足第三范式。

  • 学生借书情况(借阅编号,学生编号,书籍编号,书籍名称,书籍作者)

实际上书籍编号依赖于借阅编号,而书籍名称和书籍作者依赖于书籍编号,因此存在传递依赖的情况,我们可以将书籍信息进行单独拆分为另一张表:

  • 学生借书情况(借阅编号,学生编号,书籍编号)
  • 书籍(书籍编号,书籍名称,书籍作者)

这样就消除了传递依赖。从而满足第三范式

BCNF

BCNF作为第三范式的补充。

  • 假设仓库管理关系表为StorehouseManage(仓库D,存储物品ID,管理员ID,数量)。
  • 且有一个管理员只在一个仓库工作;
  • 一个仓库可以存储多种物品。这个数据库表中存在如下决定关系:
    • (仓库ID,存储物品ID) -> (管理员ID,数量)
    • (管理员ID,存储物品ID) -> (仓库ID,数量)
      所以,(仓库ID,存储物品ID)和(管理员ID,存储物品ID)都是StorehouseManage的候选关键字,表中的唯一非关键字段为数量,它是符合第三范式的。但是由于存在如下决定关系:
      (仓库ID) -> (管理员ID)
      (管理员ID) -> (仓库ID)

即粗在关键字段决定关键字短的情况,如果修改管理员ID,那么就必须逐一进行修改,所以其不符合BCNF范式。

SQL语句

主要分为四类:

  • 数据查询语言(Data Query Language,DQL) 基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块
  • 数据操纵语言(Data Manipulation Language,DML)是SQL语言中,负责对数据库对象运行数据访问工作的指令集,以INSERT、UPDATE、DELETE三种指令为核心,分别代表插入、更新与删除,是开发以数据为中心的应用程席必定会使用到的指令。
  • 数据库定义语言DDL(Data Definition Language),是用于描述数据库中要存储的现实世界实体的语言。
  • DCL(Data ControlLanguage) 是数据库控制语言。是用来设置或更改数据库用户或角色权限的语句,包括(grant,deny,revoke等)语句。在默认状态下,只有sysadmin,dbcreatordb owner或db securityadmin等人员才有权力执行DCL.

我们平时所说的CRUD其实就是增删改查 (Create/Retrieve/Update/Delete)

数据库定义语言(DDL)

  • 通过create database创建一个数据库:
    1
    create database 数据库名
  • 创建时设置编码格式来支持中文:
    1
    CREATE DATABASE IF NOT EXISTS 数据库名 DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
  • 通过drop database 删除数据库
    1
    drop database 数据库名

    列级约束条件

  • NOT NULL:指定列不能为空
  • UNIQUE :指定列中的值必须是唯一的,不能重复
  • 主键:指定列表为主键,可以唯一标识每一行数据。不能为NULL,且唯一
  • 外健:可以关联到另一张表中的主键列。外健列的值必须存在于关联表的主键列中,否则会出现出发关联失败的错误。
  • CHECK检查: 指定列中的值必须满足指定条件。常见的条件包括大小、范围、正则表达式。(MySQL不支持)
  • 默认:指定列的默认值。

表的约束条件

主键、外键、唯一、检查

数据库查询语言(DQL)

聚集函数

一般用于作统计,包括:

  • count([distinct]*)统计所有的行数(distinct表示去重再统计,下同)。
  • count([distinct]列名)统计某列的值的种类的总和.
  • sum([distinct]列名)求一列的和。(注意必须是数字类型的)
  • avg([distinct]列名)求一列的平均值。(注意必须是数字类型的)
  • max([distinct]列名)求一列的最小值。
  • min([distinct]列名)求一列的最大值。
1
2
SELECT count(distinct 列名) FROM 表名 WHERE 条件 

分组和分页查询

通过group by来对查询结果进行分组,它需要结合聚合函数一起使用:

1
2
SELECT sum(*) FROM 表名 WHERE 条件 GROUP BY 列名
SELECT COUNT(*), sex FROM student GROUP BY sex

  • 通过having来限制条件

    1
    2
    SELECT sum(*) FROM 表名 WHERE 条件 GROUP BY 列名 HAVING 约束条件
    SELECT COUNT(*), sex FROM student GROUP BY sex HAVING sex = '男'
  • 通过limit来限制查询数量

  • 只查询前三条
    1
    2
    SELECT * FROM 表名 LIMIT 数量
    SELECT *FROM student LIMIT 3
  • 分页
    1
    SELECT * FROM 表名 LIMIT 起始位置,数量
    一页显示2个,查第一页
    1
    SELECT *FROM student LIMIT 0,2
    第二页
    1
    SELECT *FROM student LIMIT 1,2

多表查询

  • 多表查询是同时查询的两个或两个以上的表,多表查询会提通过连接转换为单表查询。

    1
    2
    SELECT * FROM1, 表2
    SELECT *FROM student, teacher

  • 直接这样查询会得到两张表的笛卡尔积,也就是每一项数据和另一张表的每一项数据都结合一次,会产生庞大的数据

    1
    2
    SELECT * FROM1, 表2 WHERE 条件
    SELECT *FROM student, teacher WHERE sid = 1

这样,只会从笛卡尔积的结果中得到满足条件的数据。

自身连接查询

自身连接,就是将表本身和表进行笛卡尔积计算,得到结果,但由于表名相同,因此需要先起一个别名:

1
2
SELECT * FROM 表名 别名1, 表名 别名2
SELECT *FROM student s1, student s2

  • 其实自身连接查询和前面的是一样的,只是连接对象变成自己和自己了。

外链接查询

  • 专门用于联合查询情景的。
  • 比如现在有一个存储所有用户的表,还有一张用户详细信息的表,我希望将这两张表结合到一起来查看完整的数据,我们就可以通过使用外连接来进行查询。
  • 外连接有三种方式:
    • 通过使用inner join进行内连接,只会返回两个表满足条件的交集部分:
      1
      SELECT * FROM student INNER JOIN teach on student.sid = teach.sid

    • left join进行左连接,不仅会返回两个表满足条件的交集部分,也会返回左边表中的全部数据,而在右表中缺失的数据会使用null来代替(右连接同理):
      1
      SELECT *FROM student LEFT JOIN teach on student.sid = teach.sid

      连续左连接
      1
      2
      SELECT *FROM student LEFT JOIN teach on student.sid = teach.sid
      LEFT JOIN teacher on teach.tid = teacher.tid
      1
      2
      SELECT *FROM student LEFT JOIN teach on student.sid = teach.sid
      LEFT JOIN teacher on teach.tid = teacher.tid WHERE teacher.name = '张三'

嵌套查询

  • 我们可以将查询的结果作为另一个查询的条件,比如:
    查找张三教的学生
    1
    2
    SELECT * FROM 表名 WHERE 列名 = (SELECT 列名 FROM 表名 WHERE 条件)
    SELECT *FROM student WHERE sid in (SELECT sid FROM teach WHERE tid = (SELECT tid from teacher WHERE name = '张三'))

联查

1
SELECT FROM student s LEFT JOIN teach t on s.sid = t.sid WHERE tid = (SELECT tid FROM teacher WHERE name = '张三')

视图

  • 视图本质就是一个查询结果,不过我们每次都可以通过打开视图来按照我们想要的样子查看数据。
  • 既然视图的本质就是一个查询结果,那么它本身就是一个虚表,并不是真实存在的,数据实际上还是存放在原来的表中。
  • 可以通过create view来创建视图
    1
    2
    CREATE VIEW 视图名称(列名) as 子查询语句 [WITH CHECK OPTION];
    CREATE VIEW test as SELECT * FROM student WHERE sex = '男'
  • 创建后,我们就可以使用select语句来直接查询视图上的数据了,因此,还能在视图的基础上,导出其他的视图。
    1
    SELECT *FROM test
    1
    UPDATE test set name = '小蓝' WHERE sid = 1

1
CREATE VIEW test as SELECT * FROM student WHERE sex = ‘男’ WITH CHECK OPTION
  • WITH CHECK OPTION是指当创建后,如果更新视图中的数据,是否要满足子查询中的条件表达式,不满足将无法插入.
    1
    UPDATE test SET sex = '女' WHERE sid = 1
  • 无法更改,报错:1369 - CHECK OPTION failed ‘learn.test’。即限定条件是sex=男,不能更改这个限定数据变成其他的,改成男的可以。
  • 若视图是由两个及以上基本表导出的,则此试图不允许更新。

    • 创建两个表的视图:
      1
      CREATE VIEW test as SELECT s.sid sid, name ,sex ,tid FROM student s LEFT JOIN teach t on s.sid = t.sid 
    • 不能使用 *,因为两张表都有sid
  • 若视图的字段来自字段表达式或常数,则不允许对此视图执行INSERT和UPDATE操作,但允许执行DELETE操作。

  • 若视图的字段来自集函数,则此视图不允许更新。
  • 若视图定义中含有GROUP BY子句,则此视图不允许更新。
  • 若视图定义中含有DISTINCT短语,则此视图不允许更新。
  • 若视图定义中有嵌套查询,并且内层查询的FROM子句中涉及的表也是导出该视图的基本表,则此视图不允许更新。例如将成绩在平均成绩之上的元组定义成一个视图GOOD_SC: CREATE VIEW GOOD_SC AS SELECT Sno, Cno, Grade FROM SC WHERE Grade > (SELECT AVG(Grade) FROM SC);   导出视图GOOD_SC的基本表是SC,内层查询中涉及的表也是SC,所以视图GOOD_SC是不允许更新的。
  • 一个不允许更新的视图上定义的视图也不允许更新。

索引

  • 在数据量非常庞大时,通过创建索引,来提高我们的查询效率。
  • 就像Hash一样,他能快速地定位到元素存放的位置。
    1
    2
    3
    4
    5
    -- 创建索引
    CREATE INDEX 索引名称 ON 表名 (列名)
    CREATE INDEX i ON student(name)
    -- 查看表中的索引
    show INDEX FROM student

单列索引

单列索引只针对某一列数据创建索引,单列索引有以下几种类型:

  • NORMAL:普通索引,完完全全相当于一本书的目录。
  • UNIQUE:唯一索引,一旦建立,那么整个列中将不允许出现重复数据。每个表的主键列,都有一个特殊的唯一索引,叫做Primary Key,它不仅仅要求不允许出现重复,还要求不能为NULL,它还可以自动递增。每张表可以有多个唯一索引,但是只能有一个Primary索引。
  • SPATIAL:空间索引,对空间数据类型的字段建立的索引,MYSQL中的空间索引有四种。
  • FULLTEXT:全文索引,它是模糊匹配的一种更好的解决方案,它的效率比使用like更高,并且还支持多种匹配方式,灵活性也更加强大,只有字段的数据类型为char、varchar、text及其系列才可以建立全文索引。

我们来看看如何使用全文索引,首先创建一张用于测试全文索引的表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CREATE TABLE articles (
id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
title VARCHAR(200),
body TEXT,
FULLTEXT (body));


INSERT INTO articles VALUES
(NULL,'MySQL Tutorial', 'DBMS stands for DataBase ...'),
(NULL,'How To Use MySQL Efficiently', 'After you went through a ...'),
(NULL,'Optimising MySQL','In this tutorial we will show ...'),
(NULL,'1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
(NULL,'MySQL vs. YourSQL', 'In the following database comparison ...'),
(NULL,'MySQL Security', 'When configured properly, MySQL ...');


  • 使用全文索引进行模糊匹配

    1
    SELECT *FROM articles WHERE MATCH(body) AGAINST ('database')

  • match中就是必须是哪些字段,against中定义需要模糊匹配的字符。

  • 我们用作查找的字符串实际上是被分词之后的结果。
  • 如果进行模糊匹配的不是一个词语,那么会查找失败,但是它的效率远高于以下这种写法:
    1
    SELECT * FROM articles WHERE body like '%database%';

聚簇索引和非聚簇索引

  • 聚簇索引:索引和数据存放在一起,叶子节点保留数据行。
  • 非聚簇索引:索引和数据分开存放,叶子节点存放的是指向数据行的地址。

组合索引

  • 组合索引实际上就是将多行捆绑在一起,作为一个索引,同样支持以上几种索引类型。
  • 注意组合索引进行匹配时,遵循最左原则。
    • 最左原则:只有从索引的最左侧列开始一次查询,索引才会被使用。
    • 例子:假设有一个联合索引(a,b,c)分别代表不同的列。如果查询条件时a=1,b=2,c=3,那么索引可以被利用。如果时b=2,c=3就无法被利用,因为查询条件中没有使用索引最左侧的列a。
  • 唯一索引时只要有一个数据不一样就可以插入。

索引失效

  • 1.遵守最左原则。
  • 2.不在索引列上做任何操作(列入计算、函数、类型转换)
  • 3.使用(!= 或 <>)的时候无法使用索引。
  • LIKE和%通配符也无法使用索引。
    • Like和%要扫描整个字符串列,并进行逐个比较,可能导致索引失效,从而强制MySQL执行全表扫描。

索引的底层原理

  • 索引是存储在硬盘上的,跟我们之前使用的HashMap之类的不同,它们都是在内存中的,但是硬盘的读取速度远小于内存的速度,每一次IO操作都会耗费大量的时间。
  • 我们也不可能把整个磁盘上的索引全部导入内存,因此我们需要考虑尽可能多的减少IO次数,索引的实现可以依靠两种数据结构,一种是我们在JavaSE阶段已经学习过的Hash表,还有一种就是B-Tree。

Hash

通过对Key进行散列值计算,我们可以直接得到对应数据的存放位置,它的查询效率能够达到O(1),但是它也存在一定的缺陷:

  • Hash索引仅仅能满足“=”,“in”查询条件,不能使用范围查询。
  • Hash碰撞问题。
  • 不同用部分索引健来搜索,因为组合索引在计算哈希值时时一起计算的。

B+

  • 有n棵子树的结点中含有n个健值。
  • 所有的键值信息只在叶子节点中包含,非叶子节点仅仅保存节点的最小值(或最大值),和指向叶子节点的指针。
  • 所有的叶子节点都有一个根据大小顺序只想下一个叶子节点的指针Q,本质上数据就是一个链表。

这样,读取IO的时间相比BTree就减少了很多,并且查询任何键值信息都需要完整地走到叶子节点,保证了查询的IO读取次数一致。因此MySQL默认选择B+Tree作为索引的存储数据结构。

  • 这是MyISAM存储引擎下的B+Tree实现:

  • 这是InnoDB存储引擎下的B+Tree实现:

  • 区别:

    • 主键:InnoDB直接存数据,MyISAM存的是地址。数据本身就是索引的一部分(所以这里建议主键使用自增)。
    • 不是主键:最后存的是主键ID。(因此InnoDB必须有主键,若没有也会自动查找替代)。

事务

  • 当我们要进行的操作非常多时,比如要依次删除很多个表的数据,我们就需要大量的SQL语句来完成。
  • 这些数据库操作语句就可以构成一个事务。
  • 只有Innodb引擎支持事务。

事务的特性

  • 原子性:
    • 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。
    • 事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性:
    • 在事务开始之前和结束之后,数据库的完整性没有被破坏。
    • 这表示写入的资料必须完全符合所有预设规则,包括资料的精确度,串联性以及后续数据库可以自发性地完成预定的工作。
  • 隔离性:
    • 数据库运去多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行由于交叉执行导致数据的不一致。
    • 事务隔离分为不同级别,包括未提交,读提交,可重复读和串行化。
  • 持久性:事务处理结束后,对数据的修改就是永久的,即使系统故障也不会丢失。

锁机制

在MySQL中,很容易出现多线程同时操作表中数据的情况,如果要避免潜在的并发问题,那么我们就可以使用事务隔离机制来处理,就用到了锁机制。
隔离级别包括:

  • 读未提交:能够读取到其他事务中未提交的内容,存在脏读问题。
    • 脏读:假设有两个事务T1和T2,T1在执行的过程中,读取到了T2未提交的数据,这时如果T2回滚了,那么T1读取到的数据是不一致的。
  • 读已提交:只能读取到其他事务已经提交的内容,避免了脏读的问题,但存在不可重复读的问题。
    • 不可重复读问题:在同一个事务中,多次读取同一行数据时,得到的结果不同。这时因为在两次读取之间,另一个事务修改了这一行的数据,导致了第一次读取和第二次读取的结果不同。
  • 可重复读:在读取某行后不允许其他事务操作此行,直到事务结束,但是依然存在幻读问题。
    • 幻读:在一个事务中多次执行同一个查询,但得到结果却不同。这是因为在两次查询之间,有另一个数据提交了一些新的数据,导致查询结果发生了变化。可重复读只限制了update,但未限制insert。
  • 串行读:一个事务的开始必须等待另一个事务的完成。

在RR级别下,MySQL在一定程度上解决了幻读问题:

  • 在快照读(不加锁)读情况下,mysql通过mvcc来避免幻读。
  • 在当前读(加锁)读情况下,mysql通过next-key来避免幻读。

MVCC,全称 Multi-Version Concurrency Control ,即多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。

读锁和写锁

从对数据的操作类型上来说,锁分为读锁和写锁:

  • 读锁:也叫共享锁,当一个事务添加了读锁后,其他的事务也可以添加读锁或是读取数据,但是不能进行写操作,只能等到所有的读锁全部释放。
  • 写锁:也叫排他锁,当一个书屋添加了写锁后,其他事务不能读也不能写也不能添加任何锁,只能等待当前事务释放锁。

全局锁、表锁和行锁

从锁的作用范围上划分,分为全局锁、表锁和行锁:

  • 全局锁:作用于全局,整个数据库的所有操作全部受到锁限制。
  • 表锁:作用于整个表,所有对表的操作都会受到锁的下肢。
  • 行锁:作用于表中的某一行,只会通过锁限制对某一行的操作。(仅InnoDB支持)

全局锁

我们首先来看全局锁,它作用于整个数据库,我们可以使用以下命令来开启读全局锁:

1
flush tables with read lock;

  • 开启后,整个数据库被上读锁,我们只能去读数据,但是不允许写数据。(包括更新、插入、删除等)一旦执行写操作,会被阻塞,直到锁被释放,我们可以使用以下命令来解锁:

    1
    unlock tables;
  • 除了手动释放锁之外,当我们的会话结束后,锁也会被自动释放。

表锁

  • 表锁作用于某一张表,也是MyISAM和InnoDB存储引擎支持的方式,我们可以使用以下命令来为表添加锁:
    1
    lock table 表名称 read/write;
  • 在我们为表添加写锁后,我们发现其他地方是无法访问此表的,一律都被阻塞。

行锁

  • 表锁的作用范围太广了,如果我们仅仅只是对某一行进行操作,那么大可不必对整个表进行加锁,因此InnoDB支持了行锁,我们可以使用以下命令来对某一行进行加锁:
    1
    2
    3
    4
    -- 添加读锁(共享锁)
    select * from ... lock in share mode;
    -- 添加写锁(排他锁)
    select * from ... for update;
  • 使用InnoDB的情况下,在执行更新、删除、插入操作时,数据库也会自动为所设计的行添加写锁(排他锁),直到事务提交时,才会释放锁。
  • 执行普通的查询操作时,不会添加任何锁。
  • 在使用MyISAM的情况下,在执行更新、删除、插入操作时,数据库会对设计的表添加写锁,在执行查询操作时,数据库会对设计的表添加读锁。

  • 如果不使用ID进行选择,行锁将发生变化(行锁升级):

    •   select * from student where name = '小明' lock in share mode;
      
      其他人的信息也无法进行修改。
    • 因为没有索引,他不知道SQL语句的是哪一行,锁会升级成类似于表锁。
    • 添加索引就可以了。
  • 行锁需要索引。

记录锁、间隙锁和临健锁

行锁可以继续分为多个类型

记录锁

(Record Locks)记录锁。

  • 仅仅锁住索引记录的一行。
  • Record Locks锁住的永远是索引,而非记录本身,即使该表上没有任何索引,那么innodb会在后台创建一个隐藏的聚集主键索引,那么锁住的就是这个隐藏的聚集主键索引。
  • 当一条sql没有走任何索引时,那么将会在每一条聚合索引后面加写锁,这个类似于表锁,但在原理上和表锁不同。

间隙锁

  • (Gap Locks)仅仅锁住一个索引区间(开区间,不包括双端端点)。
  • 在索引记录之间的间隙中加锁,或者是在某一条索引记录之前或者之后加锁,并不包括索引记录本身。
  • 比如在 1、2中,间隙锁的可能值有 (-∞, 1),(1, 2),(2, +∞),间隙锁可用于防止幻读,保证索引间的不会被插入数据。

临健锁

  • (Next-Key Locks)Record lock + Gap lock,左开右闭区间。
  • 默认情况下,InnoDB正是使用Next-key Locks来锁定记录(如select … for update语句)它还会根据场景进行灵活变换:
  • 假设数据库有10条数据,事务中修改第100条,查询不到,变成间隙锁,此时锁(10, +∞)数据,10和10之前的还可以修改,但不可插入新数据。

乐观锁与悲观锁

  • 乐观锁:是一种基于版本号(或时间戳)的并发控制策略。
    • 假设数据在一般情况下不会发生冲突,因此对数据的读取和修改操作并不加锁。
    • 而是在数据更新的时候,对数据版本进行比对,如果版本号不一致,则说明数据依据被其他事务修改过,此时会回滚事务,重新读取数据进行修改。
    • 适用于多读少写的场景,可以提高并发性能。
  • 悲观锁:是一种基于锁的并发控制策略:
    • 假设数据在一般情况下可能发生冲突,因此在进行数据读取和修改操作时,会先对数据进行加锁。
    • 其他事务此时无法对该数据进行操作,直到锁被释放。
    • 适用于多写少读的场景,可以保证数据的一致性。

MVCC

是一种多版本并发控制技术,用于实现数据库系统的并发控制和事务隔离。

  • 基于乐观锁的思想,通过在数据行中保存多个版本的数据,使得每个数据在读取数据的时候都可以读取到一个对应版本的数据,从而避免了数据的冲突和并发反问的问题。
  • 在MVCC中,每个事务可以读取到自己开始之前提交的数据版本,但是无法读取到其他书屋还未提交的数据版本。

在MySQL中,MVCC主要通过以下两种方式实现:

  • Undo日志:
    • 在更新操作时,会先记录原来的数据,称为Undo日志。
    • 当有事务需要读取数据时,MySQL会根据事务的隔离级别,在日志中找到对应的版本数据,并将其返回给事务进行操作。
  • Read View:
    • 事务在执行过程中维护的一个数据视图,包含了事务开始之前已经提交的所有数据版本。
    • 当事务需要读取数据时,会根据事务的隔离级别,生成一个ReadView,并将其作为参数传递给存储引擎
    • 读取数据时至返回符合ReadView条件的数据版本,从而实现了MVCC的效果。

日志系统

日志可以保证数据的一致性和可靠性,并提供数据恢复和复制功能。

InnoDB存储引擎提供了多种日志,包括重做日志(Redo Log)、回滚日志(Undo Log)、二进制日志等。

错误日志:

  • 记录MySQL服务器的所有错误和警告消息。
  • 包括启动、关闭、运行时等错误。

查询日志

  • 记录所有执行的查询语句。
  • 包括查询语句的文本、执行时间、返回行数等信息。

慢查询日志

  • 记录执行时间超过指定阈值的查询语句。
  • 通常用于找出需要优化和改进的查询。

二进制日志

  • 用于复制和恢复数据。
  • 在主从复制场景下,主服务器将数据修改操作写入二进制日志,从服务器读取二进制日志并执行相应操作以保证数据同步。
  • 在数据恢复场景下,通过重放二进制日志可以将数据恢复到特定的时间点货状态。

事务日志

  • 记录每个事务的更改操作,包括插入、更新、删除等操作。
  • 用于实现ACID事务的特性,确保数据库数据的一致性。
    • Redo Log(重做日志):记录了在事务提交之前对数据库的更改操作。当系统崩溃时可以用来恢复数据库的修改操作。
    • Undo Log(撤销日志):记录了事务进行的所有修改操作和逆操作,当一个事务需要回滚时,可以通次日志将数据恢复到修改前的状态。MVCC功能使用次日志实现。

慢启动日志:

- 记录MySQL服务器启动时的详细信息。
- 包括各个组件的启动过状态、配置参数等。

通用日志

- 记录MySQL服务器的运行状态信息。
- 包括连接信息、状态变化等。

存储引擎

  • MyISAM:MySQL5.5之前的默认存储引擎,在插入和查询的情况下性能很高,但是它不支持事务,只能添加表级锁。
  • InnoDB:MySQL5.5之后的默认存储引擎,它支持ACID事务、行级锁、外健,但是性能比不过MyISAM,更加消耗资源。
  • Memory:数据都存放在内存中,数据库重启或发生崩溃,表中的数据将全部丢失。

页偏移过大时怎么优化

如果页偏移过大,会导致查询性能变差,因为数据库需要扫描大量数据才能找到目标数据。
可以采取以下措施优化:

  • 设置每页最大查询数量。
  • 使用索引。
  • 使用缓存。