笔记:MySQL相关知识

孤街醉人 提交于 2020-03-12 09:03:48
  1. MySQL背景介绍
  2. MySQL发展史
  3. MySQL分支介绍
  4. MySQL的优点
  5. MySQL数据库组成
  6. MySQL的原理
  7. MySQL执行流程
  8. MySQL日志
  9. MySQL事务、锁、索引与SQL优化

MySQL背景介绍

随着互联网企业的兴起,例如阿里巴巴、网易、Yahoo、Facebook等大型互联网公司的推动,目前Mysql已经成为世界上最流行的数据库,并一步步开始占领了原有商业数据库的市场,目前Mysql已经成为互联网公司甚至部分传统公司的首选数据库。
在这里插入图片描述

MySQL发展史

最初为AB公司的产品,Mysql5.0及之前的版本均由AB公司管理,在2008年的时候AB公司被Sun公司收购,Mysql也就归属到了Sun公司下,但在Sun还没待多久,Sun公司就被Oracle公司收购了,此期间诞生了一个Sun向Oracle的过渡版本:Mysql5.5(5.1-5.5之间没有发布过版本),直至5.6Mysql就彻底归属于Oracle公司旗下了,从Oracle收购Mysql以来,Mysql发展的速度及新的功能越来越强大,逐步向Oracle靠拢,如:表空间、redo、undo分离、隐藏索引,至2019年,Mysql8.0.15已经GA(官方发布稳定版本),整体发展过程如下(AB版本不做加入):

5.1 → 5.5 → 5.6 → 5.7 → 8.0

MySQL分支介绍

MySQL 是一个开源数据库,有开源那么就会出现分支(二次开发),MySQL 有两家很牛X的分支公司percona、mariadb,虽然两家做的很优秀,但目前主流使用的版本仍为Oracle的MySQL (社区版),主要由MySQL 社区进行维护,主要的Mysql版本如下:

Mysql(社区版)开源
Mysql(商业版)收费
percona
mariadb

MySQL的优点

1.大家都知道现在Oracle的薪资已经大不如前,初级岗位基本已经饱和,而MysqlDBA的薪资却一直保持在一个很高的水平。
2.去IOE的推进,腾讯、阿里、网易、新浪等大型互联网均在使用Mysql,并且小型互联网公司也基本全部在使用Mysql,一些传统企业也在进行由Oracle向MySQL 的转型

“IOE它是阿里巴巴造出的概念。其本意是,在阿里巴巴的IT架构中,去掉IBM的小型机、Oracle数据库、EMC存储设备,代之以自己在开源软件基础上开发的系统。”

3.MySQL 是一个开源的数据库,如果你懂C,你有问题或者原理不懂的地方,可以直接去扒代码,并且MySQL 社区很活跃,交流讨论很方便
4.轻量级数据库,MySQL 有很好的扩展性,结合NOSQL,主从复制扩展,安装简易
5.强大的复制级别,MySQL 现在支持多种复制级别,可以根据业务需求进行灵活选择

Mysql支持3种方式:基于语句的复制、基于行的复制、混合复制。对应的binlog的格式也有三种:STATEMENT,ROW,MIXED
(1)基于语句的复制(SBR)

每一条修改数据的sql语句会记录到binlog中。优点是不需要记录每一条sql语句和每一行的数据变化,减少了binlog日志量,节约IO,提高性能。缺点是在某些情况下会导致master-slave中的数据不一致(如RAND(),UUID(),存储过程,触发器等)

(2)基于行的复制(RBR)

不记录每条sql语句的上下文信息,转而需记录哪条数据被修改了,修改成什么样了。并且不会出现某些特定情况下的存储过程、触发器等在基于语句复制的模式下导致无法被正确复制的问题。缺点是会产生大量的日志,尤其是alter table的时候会让日志暴涨,无法准确的判断执行了那些sql,此外在备库上改表的schema时会出现复制失败,比如没有在最后加列或删除列

(3)混合复制(MRB)

以上两种模式的混合使用,MySQL会根据执行的SQL语句选择日志保存方式,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog。

MySQL数据库组成

MySQL是由实例与数据库组成(与Oracle相似,不同的是Mysql有强大的存储引擎)
MySQL实例:内存数据、进程任务
MySQL数据库:参数文件、数据文件、日志文件

MySQL的原理

MySQL 的逻辑架构图
在这里插入图片描述
MySQL可以分为 Server 层和存储引擎层两部分。

Server 层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。

存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持 InnoDB、MyISAM、Memory 等多个存储引擎。现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始成为了默认存储引擎。

你执行 create table 建表的时候,如果不指定引擎类型,默认使用的就是
InnoDB。不过,你也可以通过指定存储引擎的类型来选择别的引擎,比如在 create table 语句中使用 engine=memory,来指定使用内存引擎创建表。不同存储引擎的表数据存取方式不同,支持的功能也不同。

MySQL执行流程

1. 连接器
第一步,你会先连接到这个数据库上,这时候接待你的就是连接器。连接器负责跟客户端建立连接、获取权限、维持和管理连接。连接命令一般是这么写的:

mysql -h$ip -P$port -u$user -p

输完命令之后,你就需要在交互对话里面输入密码。虽然密码也可以直接跟在 -p 后面写在命令行中,但这样可能会导致你的密码泄露。如果你连的是生产服务器,强烈建议你不要这么做。

连接命令中的 mysql 是客户端工具,用来跟服务端建立连接。在完成经典的 TCP 握手后,连接器就要开始认证你的身份,这个时候用的就是你输入的用户名和密码。

  • 如果用户名或密码不对,你就会收到一个"Access denied for user"的错误,然后客户端程序结束执行。
  • 如果用户名密码认证通过,连接器会到权限表里面查出你拥有的权限。之后,这个连接里面的权限判断逻辑,都将依赖于此时读到的权限。

这就意味着,一个用户成功建立连接后,即使你用管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限。修改完成后,只有再新建的连接才会使用新的权限设置。

连接完成后,如果你没有后续的动作,这个连接就处于空闲状态,你可以在 show processlist 命令中看到它。下图是 show processlist 的结果,其中的 Command 列显示为“Sleep”的这一行,就表示现在系统里面有一个空闲连接。

在这里插入图片描述
客户端如果太长时间没动静,连接器就会自动将它断开。这个时间是由参数 wait_timeout 控制的,默认值是 8 小时。

如果在连接被断开之后,客户端再次发送请求的话,就会收到一个错误提醒: Lost connection to MySQL server during query。这时候如果你要继续,就需要重连,然后再执行请求了。

数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。

建立连接的过程通常是比较复杂的,所以我建议你在使用中要尽量减少建立连接的动作,也就是尽量使用长连接。

但是全部使用长连接后,你可能会发现,有些时候 MySQL 占用内存涨得特别快,这是因为 MySQL 在执行过程中临时使用的内存是管理在连接对象里面的。这些资源会在连接断开的时候才释放。所以如果长连接累积下来,可能导致内存占用太大,被系统强行杀掉(OOM),从现象看就是 MySQL 异常重启了。

怎么解决这个问题呢?可以考虑以下两种方案:

  1. 定期断开长连接。使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连。
  2. 如果你用的是 MySQL 5.7 或更新版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源。这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态。

2. 查询缓存
连接建立完成后,你就可以执行 select 语句了。执行逻辑就会来到第二步:查询缓存。

MySQL 拿到一个查询请求后,会先到查询缓存看看,之前是不是执行过这条语句。之前执行过的语句及其结果可能会以 key-value 对的形式,被直接缓存在内存中。key 是查询的语句,value 是查询的结果。如果你的查询能够直接在这个缓存中找到 key,那么这个 value 就会被直接返回给客户端。

如果语句不在查询缓存中,就会继续后面的执行阶段。执行完成后,执行结果会被存入查询缓存中。你可以看到,如果查询命中缓存,MySQL 不需要执行后面的复杂操作,就可以直接返回结果,这个效率会很高。

但是大多数情况下我会建议你不要使用查询缓存,为什么呢?因为查询缓存往往弊大于利。

查询缓存的失效非常频繁,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能你费劲地把结果存起来,还没使用呢,就被一个更新全清空了。对于更新压力大的数据库来说,查询缓存的命中率会非常低。除非你的业务就是有一张静态表,很长时间才会更新一次。比如,一个系统配置表,那这张表上的查询才适合使用查询缓存。

好在 MySQL 也提供了这种“按需使用”的方式。你可以将参数 query_cache_type 设置成 DEMAND,这样对于默认的 SQL 语句都不使用查询缓存。而对于你确定要使用查询缓存的语句,可以用 SQL_CACHE 显式指定,像下面这个语句一样:

mysql> select SQL_CACHE * from T where ID=10;

需要注意的是,MySQL 8.0 版本直接将查询缓存的整块功能删掉了,也就是说 8.0 开始彻底没有这个功能了。

3. 分析器
如果没有命中查询缓存,就要开始真正执行语句了。首先,MySQL 需要知道你要做什么,因此需要对 SQL 语句做解析。

分析器先会做“词法分析”。你输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么,代表什么。

MySQL 从你输入的"select"这个关键字识别出来,这是一个查询语句。它也要把字符串“T”识别成“表名 T”,把字符串“ID”识别成“列 ID”。

做完了这些识别以后,就要做“语法分析”。根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。

如果你的语句不对,就会收到“You have an error in your SQL syntax”的错误提醒。

4. 优化器
经过了分析器,MySQL 就知道你要做什么了。在开始执行之前,还要先经过优化器的处理。

优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序。
优化器阶段完成后,这个语句的执行方案就确定下来了,然后进入执行器阶段。

5. 执行器
MySQL 通过分析器知道了你要做什么,通过优化器知道了该怎么做,于是就进入了执行器阶段,开始执行语句。

开始执行的时候,要先判断一下你对这个表 T 有没有执行查询的权限,如果没有,就会返回没有权限的错误。

如果有权限,就打开表继续执行。打开表的时候,执行器就会根据表的引擎定义,去使用这个引擎提供的接口。

mysql> select * from T where ID=10;

比如我们这个例子中的表 T 中,如果ID 字段没有索引,那么执行器的执行流程是这样的:

  1. 调用 InnoDB 引擎接口取这个表的第一行,判断 ID 值是不是 10,如果不是则跳过,如果是则将这行存在结果集中;
  2. 用引擎接口取“下一行”,重复相同的判断逻辑,直到取到这个表的最后一行。
  3. 执行器将上述遍历过程中所有满足条件的行组成的记录集作为结果集返回给客户端。

至此,这个语句就执行完成了。

你会在数据库的慢查询日志中看到一个 rows_examined 的字段,表示这个语句执行过程中扫描了多少行。这个值就是在执行器每次调用引擎获取数据行的时候累加的。

在有些场景下,执行器调用一次,在引擎内部则扫描了多行,因此引擎扫描行数跟 rows_examined 并不是完全相同的。

MySQL日志

MySQL两个重要的日志模块:redo log(重做日志)和 binlog(归档日志)

最开始 MySQL 里并没有 InnoDB 引擎。MySQL 自带的引擎是 MyISAM,但是 MyISAM 没有 crash-safe
的能力,binlog 日志只能用于归档。而 InnoDB 是另一个公司以插件形式引入 MySQL 的,既然只依靠 binlog 是没有
crash-safe 能力的,所以 InnoDB 使用另外一套日志系统——也就是 redo log 来实现 crash-safe 能力。

这两种日志有以下三点不同。

  1. redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
  2. redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如“给 ID=2 这一行的 c 字段加 1 ”。
  3. redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

update 语句执行流程
在这里插入图片描述

MySQL事务、锁、索引与SQL优化

1.数据库事务

  • 事务定义:

一个任务最小的不可再分的工作单元;正常情况下一个事务对应一个完整的业务流程

完整业务流程需包含批量的DML(insert、delete、update)语句;
有DML操作才有事务,这个增删改查的顺序与业务逻辑有关,业务逻辑不同,DML数量也有不同;

  • 事务特征(ACID):

原子性(Atomicity):最小单位不可再分
一致性(Consistency):所有DML要么都成功要么都失败
隔离性(Isolation):A事务与B事务之间访问同一数据时,相互不干扰
持久性(Durability):事务一旦提交,数据将永久性地修改(数据从内存中持久保存到了磁盘上)

生产中开启事务的几种方式
1.开启事务的标志 任意一条DML语句/start transction
2.结束事务的标志 提交:成功结束,DML记录日志和磁盘数据库进行一次同步 (commit) 回滚:失败结束,将所有的DML记录日志清理(rollback)

  • 事务隔离等级
    SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )
  • 读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。
  • 读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
  • 可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
  • 串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

提醒:Mysql默认的事务隔离级别为repeatable_read(可重复读)

2.数据库锁

数据库锁设计的初衷是处理并发问题。作为多用户共享的资源,当出现并发访问的时候,数据库需要合理地控制资源的访问规则。而锁就是用来实现这些访问规则的重要数据结构。

根据加锁的范围,MySQL 里面的锁大致可以分成全局锁、表级锁和行锁三类。

  • 全局锁

    顾名思义,全局锁就是对整个数据库实例加锁。MySQL 提供了一个加全局读锁的方法,命令是 Flush tables with read
    lock
    (FTWRL)。当你需要让整个库处于只读状态的时候,可以使用这个命令,之后其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结构等)和更新类事务的提交语句。

    全局锁的典型使用场景是,做全库逻辑备份。也就是把整库每个表都 select 出来存成文本。

  • 表级锁

    MySQL 里面表级别的锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)。

    a) 表锁的语法是 lock tables … read/write。与 FTWRL 类似,可以用 unlock tables 主动释放锁,也可以在客户端断开的时候自动释放。需要注意,lock tables 语法除了会限制别的线程的读写外,也限定了本线程接下来的操作对象。

    举个例子, 如果在某个线程 A 中执行 lock tables t1 read, t2 write; 这个语句,则其他线程写 t1、读写
    t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1
    都不允许,自然也不能访问其他表。

    在还没有出现更细粒度的锁的时候,表锁是最常用的处理并发的方式。而对于 InnoDB 这种支持行锁的引擎,一般不使用 lock tables 命令来控制并发,毕竟锁住整个表的影响面还是太大。

    b)另一类表级的锁是 MDL(metadata lock)。MDL 不需要显式使用,在访问一个表的时候会被自动加上。MDL 的作用是,保证读写的正确性。你可以想象一下,如果一个查询正在遍历一个表中的数据,而执行期间另一个线程对这个表结构做变更,删了一列,那么查询线程拿到的结果跟表结构对不上,肯定是不行的。

    因此,在 MySQL 5.5 版本中引入了 MDL,当对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁。

    读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
    读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

  • 行锁
    顾名思义,行锁就是针对数据表中行记录的锁。这很好理解,比如事务 A 更新了一行,而这时候事务 B 也要更新同一行,则必须等事务 A 的操作完成后才能进行更新。

    在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。

3.索引

一句话简单来说,索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。

在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。由于 InnoDB 存储引擎在 MySQL 数据库中使用最为广泛,所以我主要说一下InnoDB 的索引模型。

InnoDB 的索引模型
在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。又因为InnoDB 使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的。每一个索引在 InnoDB 里面对应一棵 B+ 树。

索引类型分为主键索引非主键索引

主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。
非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。

建立索引的方式:主键索引,唯一索引,普通索引,组合索引和全文索引

建立索引的方式 定义
主键索引 键索引很明显就是主键约束的实现方式,这种索引要求建立索引的内容不能相同而且不能为空。在Mysql中主键索引默认是聚集索引,因此查询速度更快。
唯一索引 唯一索引是唯一性约束的实现方式,这种索引要求建立索引的内容不能相同但可以为空,在Mysql中,建立唯一索引和添加唯一性约束效果是一致的。唯一索引可以是聚集索引(InnoDB中),也可以是非聚集索引。
普通索引 普通索引就是我们平常建立在数据上的索引,一般是非聚集索引,在InnoDB中普通索引中含有主键索引的内容和建立索引的数据内容。
组合索引 组合索引是多列共同建立的索引,要建立符合最左前缀原则的组合索引
全文索引 全文索引是针对文本内容建立的索引,要指定索引的长度,通过LIKE条件查询时可以使用,但是要满足不能有前导模糊查询的情况

查询时会使用索引的情况

当字段上建有索引时,通常以下情况会使用索引: 
index_column = ? 
index_column > ? 
index_column >= ? 
index_column < ? 
index_column <= ? 
index_column between ? and ? 
index_column in (?,?,,?) 
index_column like ?||%’(后导模糊查询,这个是针对全文索引) 
T1.index_column=T2.column1(两个表通过索引字段关联)

查询时不会使用索引的情况
在这里插入图片描述

推荐建立索引索引的列
在这里插入图片描述

4.SQL优化

a. 定位执行速度较低的sql(log-slow-queries,show processlist)
b.分析低效 SQL(EXPLAIN 分析低效 SQL)
c.优化 SQL 语句(优化 where 条件,优化关联查询和子查询,避免耗费
时间和资源的操作,合理的建立索引)

explain 用法:查看sql的执行计划,帮助我们分析mysql是如何解析sql语句的

1.查看表的加载顺序
2.查看sql的查询类型
3.哪些索引可能被使用,哪些索引又被实际使用了
4.表之间的引用关系
5.一个表中有多少行被优化器查询
6.其他额外的辅助信息

最后附上执行计划各字段含义:

字段 含义
id id相同,执行顺序由上至下 ;id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行;id相同不同,同时存在
select_type 分别用来表示查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询;SIMPLE 简单的select查询,查询中不包含子查询或者UNION;PRIMARY 查询中若包含任何复杂的子部分,最外层查询则被标记为PRIMARY;SUBQUERY 在SELECT或WHERE列表中包含了子查询;DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中;UNION 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED;UNION RESULT 从UNION表获取结果的SELECT
table 表名
type system > const > eq_ref > ref > range > index > all
possible_keys 显示可能应用在这张表中的索引,一个或多个 该索引将被列出,但不一定被查询实际使用
key_len 表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好
ref 显示索引的那一列被使用了 最好是一个常数
rows 根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数,也就是说,用的越少越好
Extra 包含不适合在其他列中显式但十分重要的额外信息
Using filesort 九死一生,说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为“文件排序”
Using temporary 十死无生,使用了用临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by
Using index 发财了 表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!