MySQL设计与优化

前言

  • 怎么设计优雅的表结构?指导原则是什么?
  • 索引为何那么快?底层为何要用B+树?
  • 怎么设计好的索引? 怎么优化索引?
  • 经常使用系统参数表明什么意思?怎么优化参数?
  • mysql优化手段有哪些?

目录

基本概念

mysql概述

innodb引擎架构

mysql设计

mysql优化

总结

一. 基本概念

1. 关系模型

  • 一对一
  • 一对多
  • 多对多

2. 关系型数据库

依赖关系模型建立的数据库,用一个二维表格及其关系组成的数据组织,最大的特色是事务的一致性php

3. 非关系型数据库

基于非关系模型的数据库,非关系模型包括node

  • 列模型:Hbase
  • 键值对模型:redis
  • 文档型模型:mongodb(聚合型数据库)

4. 关系VS非关系

比较项 SQL NoSQL
事务一致性
扩展性
高并发读写效率
实时性
数据一致性
冗余

5. 冗余

同一信息的重复储存,叫作冗余mysql

  • 低级冗余:字段的重复
  • 高级冗余:字段的派生:好比总额=单价*数量

造成缘由redis

  • 表重复
  • 属性重复
  • 元组重复

冗余的坏处sql

  • 为了保证数据一致性,要维护冗余字段的成本高
  • 可能致使数据不一致

6. 范式

做用:消除或减小冗余,增进数据一致性。设计出高效优雅的数据库

分类:

  • 第一范式(1NF):要求属性不可分,具备原子性。下图的属性被分开来,关系型数据库设计不出来这种表

  • 第二范式(2NF):要求记录具备惟一性
  • 第三范式(3NF):要求字段不能有冗余,任何字段不能由其余字段派生
  • BC范式(BCNF):主属性不依赖于主属性
  • 第四范式(4NF):要求把同一表内的多对多关系删除
  • 第五范式(5NF):从最终结构创建原始结构

最佳实践(中庸版)

  • 通常,一个数据库设计符合3NF或BCNF就能够了
  • 过于范式化甚至会对数据库的逻辑可读性和使用效率起到阻碍
  • 适当增长冗余,达到以空间换时间的目的

最最佳实践(实践版)

  • 除非你真的有足够证据证实按照规范范式设计数据库会有性能问题并且这个性能问题没法解决,或者有足够证据证实你写入的数据是永远不会被修改的,不然不要轻易用性能做为借口反范式设计。
  • 数据库对于表链接的处理能力其实很是强大,关联几个十几个表,只要数据库结构设计合理,实际上是很是轻松的事情。
  • 在数据量没达到十万级别的时候,冗余根本不必。若是数据量和并发数都上来后,会在前面加一个ETL层,其中有Join好的AB(能够是数据库,也能够是别的缓存层)。ETL层和数据库层之间用MQ打通数据同步机制

7. 事务

1. 概念:

指对系统进行的一个逻辑单元,包括一组操做。会把数据库从一种一致状态切换为另外一种一致状态。普通文件系统是没有此特性的。mongodb

2. 事务需具有的特性(ACID)

  • 原子性(Atomic):要么彻底执行,要么彻底不执行,容许回滚
  • 一致性(Consistency):事务开始和结束的中间状态不能被其余事务看到
  • 隔离性(Isolation):并发事务直接的影响程度,好比一个事务会不会读到另外一个未提交的事务修改的数据
  • 持久性(Durability):事务提交后就保证不会丢失

3. 事务并发可能出现的问题

  • 脏读:事务A修改了数据,可是未提交,事务B读到了事务A未提交的更新结果,A提交失败,B就读到脏数据
  • 不可重复读:事务B在事务A提交前读到的结果,和提交后读到的结果可能不一样。好比,事务B在事务A提交前读到的结果,和提交后读到的结果可能不一样。不可重复读出现的缘由就是事务并发修改记录
  • 幻读:在同一个事务中,同一个查询屡次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交先后各执行了一次查询操做,发现后一次比前一次多了一条记录。幻读是因为并发事务增长记录致使的

4. 事务的隔离级别(由低到高)

  • RR(read uncommitted):最低的隔离级别,什么都不须要作。全部的并发事务问题都会发生。
  • RC(read committed):只有在事务提交后,其更新结果才会被其余事务看见。能够解决脏读问题。
  • RR(repeated read):同一事务中,对同一份数据的读取结果老是相同的。能够解决脏读、不可重复读。mysql默认级别,在此基础上作了优化
  • Serialization:串行化。隔离级别最高,牺牲并发性。能够解决并发事务的全部问题

5. 各类数据库对事务的支持状况

事务的定义极其严格,必须同时知足四个特性,可是数据库厂商处于各类目的,好比性能,并无严格知足ACID的要求thinkphp

  • mysql的NDB cluster引擎,不知足D(持久性)
  • Oracle数据库默认隔离级别为RC,不知足I(隔离性)
  • mysql的InnoDB引擎,彻底遵照ACID特性

二. MySQL概述

1. 特色

  • 关系型数据库
  • 插件式存储引擎

2. 架构

2.1 特色

单进程多线程模型数据库

2.2 存储引擎

  • InnoDBwindows

    • 支持事务
    • 实现sql标准的4种隔离级别,默认为RR
    • 支持行锁
    • mysql5.5.8以后默认的存储引擎(windows除外)
  • MyISAM:缓存

    • 不支持事务
    • 支持全文索引
    • 只缓存索引文件,不缓存数据文件
    • mysql5.5.8以前默认的存储引擎
  • NDB

    • 集群存储引擎
  • Archive

    • 支持高比例压缩存储
  • Memory(heap)

    • 数据所有放在内存中

    最新的mysql版本(8.0.12)支持的存储引擎

    mysql存储引擎

mysql->show engines; //查看全部支持的存储引擎和默认存储引擎
复制代码

三. InnoDB引擎架构

1. 架构和逻辑存储结构

  • 内存池的职责
    • 维护全部进程和线程须要访问的数据结构
    • 缓存磁盘数据
    • redo log缓冲
  • 后台线程的职责
    • 负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据
    • 负责将已修改的数据文件刷新到磁盘文件
    • 保证数据库异常时能恢复到正常运行状态

2. 后台线程

  • master thread:核心后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据一致性
  • io thread:负责io请求的处理
  • purge thread:回收事务提交后已经使用的undo页
  • page clean thread: 负责脏页的刷新

3. 缓存池

概述:一块内存区域,按页存放,每页默认16k,经过checkpoint机制刷新会磁盘,包含多种页类型

缓存的页类型:

  • 索引页
  • 数据页
  • undo页
  • 锁信息
  • ...

3. 缓存如何管理

  • LRU list:改进的缓存数据列表
  • free List: 空闲列表
  • flush list:脏页列表

4. 事务

4.1 InnoDB引擎对事务的支持

  • 事务的隔离性:默认隔离级别为RR,并使用Next-Key-Lock锁来避免幻读的产生
  • 事务的原子性和持久性:redo log(重作日志)来保证
  • 事务的一致性:undo log来保证(回滚操做)
mysql->select @@tx_isolation\G; //查看隔离级别
   mysql->select @@global.tx_isolation|G; //查看全局的事务隔离级别
复制代码

4.2 redo

  • 在事务提交时,记录事务的行为到文件系统缓冲
  • 以后调用fsync(刷新频率可调),将重作日志写入磁盘

4.3 undo

  • 事务的回滚操做,将数据回滚到修改以前的样子
  • 存放在数据库内部的特殊段中(segment),称为undo segment
  • 恢复的是逻辑日志,不是物理日志:由于一页里面可能有不少并发的事务,不能回滚整个页,影响别的事务
  • unduo还实现了MVCC:当用户读取一行时,若该记录被其余事务占用,当前事务科经过undo读取以前的行版本信息,实现非锁定读
  • undo log的同事会产生redo log

4.4 redo VS binlog

对比项 redo undo
操做者 InnoDB引擎层 数据库层
生效范围 InnoDb引擎 全部引擎
日志内容 物理格式日志,记录对于每一个页的修改 逻辑日志,记录SQL语句
大体格式 page(2,3) offset 32, value 1,2 insert into..
写入时机 事务提交后一次写入 事务进行中不断写入
一个事务对应记录 1条 多条,并发还会致使乱序
恢复速度

4.5 事务的提交

1. 显示提交

  • begin/start transaction:开启事务,start是存储过程专用的
  • commit:提交
  • rollback:回滚
  • savepoint:建立事务保存点
  • settransaction 手动设置隔离级别

2. 隐式提交

如下语句提交有隐式的commit操做

  • 修改或删除相关:alter, create, drop, rename,set password,add user,grant...
  • 管理相关:analyze, cache, check, load index, optimize, repair

4.6 innodb支持的锁

  • 共享锁(S Lock):容许多个事务读一行数据
  • 排它锁(X Lock):容许一个事务删除或更新一行数据
  • 行锁和表锁

5. 逻辑存储结构

5.1 架构图

  • 全部数据放在tablespace中

  • tablespace由segment(段),extent(区),page(页,block)组成

  • innodb引擎默认有一个共享表空间ibdata1

  • segment包括数据段(Leaf node segment),索引段(Non-Lefa),回滚段(Rollback)等

  • 区由连续的页组成,每一个区大小任什么时候候都为1MB,1MB=64页*每页16KB,每页大小可调整(需为2的倍数)

  • 页是Innodb管理的最小单元

  • 数据按行存放,每页最多容许16kb/2 -200 = 7992行记录

  • 行的存储格式有:Compact(默认),Redundant(兼容旧的格式)

    页结构

查看本机行存储格式
Compact格式

5.2 文件结构

  • .ibd 表结构文件
  • .frm 表数据文件

四. 数据库设计

1. 表关系设计

  • 1对1:在任意一张表中添加外建指向另外一张表的主键
  • 1对多:“多”中添加一个外键,指向“1”的主键
  • 多对多:添加一张关系表,两个外建分别指向两张表的主键

2. 表字段设计

2.1 表字段说明 参考

  • 数值类型的选择

    • tinyint(num) : 长度只是一个最大显示宽度,宽度不足前面补0,跟数据存储范围无关
    • char(num): num是字符的最大长度,不是字节。
    • 范围是怎么算出来的:1byte=8bit,1bit有0和1两种可能,因此1byte可表示无符号2^8=256个数字,可是若是有符号,须要1bit存储符号,因此只能存储正负各2^7=128个数字,0包含在正数范围内,因此正数最大是127
    类型 大小 范围(有符号) 范围(无符号) 用途
    tinyint 1字节 (-128, 127) (0, 255) 小整数值
    smallint 2字节 (-32768, 32767) (0, 65535) 大整数值
    mediumint 3字节 (-2^(3x7), 2^(3x7) -1 ) (0, 2^(3*8) - 1) 大整数值
    int/integer 4字节 .. .. 大整数值
    bigint 8字节 .. .. 极大整数值
    float 4字节 .. .. 单精度浮点数
    double 8字节 .. .. 双精度浮点数
    decimal decimal(M,D),若M>D,为M+2,不然D+2 小数
  • 字符串的选择

    类型 大小 用途
    char 0-255字节 定长字符串
    varchar 0-255字节 变长字符串
    tinyblob 0-255字节 不超过255个字符的二进制字符串
    tinytext 0-255字节 短文本字符串
    blob 0-65535字节 二进制长文本数据
    text 0-65535字节 长文本数据
    mediumblob 16M 中等长二进制文本数据
    mediumtext 16M 中等长文本数据
    longblob 4G 极大的二进制文本数据
    longtext 4G 极大文本数据
  • 时间类型的选择

    类型 大小 范围 格式 用途
    date 3字节 1000-01-01 9999-12-31 YYYY-MM-DD 日期值
    time 3字节 '-838:59:59' '838:59:59' HH:MM:SS 时间值
    year 1字节 1901 2155 YYYY 年分值
    timestamp 4字节 1970-01-01 2037年 YYYYMMDD HHMMSS 时间戳(空值或null会自动填充当前时间),存储数会随时区变化而变化
    datetime 8字节 1000-01-01 00:00:00 9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 日期和时间,存储数不会随时区变化而变化
  • 复合类型(技术上都是字符串类型)

    类型 说明
    enum 只容许从一个集合中取得一个值
    set 容许从一个集合中取得任意多个值

2.2 表字段设计原则参考

  • 主键通常使用自增加字段
  • 字段选择合理范围内最小的,大大减小磁盘IO读写开销,内存和cpu占用率
  • 选择相对简单的数据类型
  • 不要使用NULL。由于MYSQL对NULL字段索引优化不佳,增长更多的计算难度,同时在保存与处理NULL类形时,也会作更多的工做,因此从效率上来讲,不建议用过多的NULL。有些值他确实有可能没有值,怎么办呢?解决方法是数值弄用整数0,字符串用空来定义默认值便可
  • 在不能肯定字段须要多少字符时使用 VARCHAR 类型能够大大地节约磁盘空间、提升存储效率。但若是确切知道字符串长度,好比就在50~55之间,那就用 CHAR 由于 CHAR 类型因为自己定长的特性使其性能要高于 VARCHAR。如uuid,MD5
  • 复合类型咱们通常用tinyint,更快的时间更省的空间以及更容易扩展

3. 索引设计

3.1 什么是索引

  • 存储引擎用于加快查找速度(排好序)的一种数据结构
  • 索引会被存储到磁盘上

3.2 索引优势

  • 能轻易将查询性能提高几个数量级
  • 惟一索引保证数据惟一性
  • 减小分组和排序时间

3.3 索引缺点

  • 占用磁盘空间,大量索引可能致使比文件还大
  • 损耗性能,增删改查都要维护索引

3.4 索引数据结构

数据库 索引使用的数据结构
mysql B+树
mongodb B树(B-树)

B+树的特色

  • 多叉树,高度较低
  • 每一个节点可存储多个key
  • 非叶子节点存储key,叶子节点存储key和data
  • 叶子节点两两相连

为何是B+树?

普通平衡树的缺点
  • 数据量不大时,普通平衡树(AVL树,红黑树)性能极好。可是数据量巨大时,内存不够用,没法将数据所有加载到内存中,只能放到磁盘
  • 树的高度为LogN,致使磁盘IO次数过多影响效率
  • 调整树的平衡是经过旋转实现,若是不把所有数据加载进内存是没法完成旋转的
B-树的缺点
  • 非叶子节点也存储数据,每次磁盘io数据量是固定的,每一层索引范围小
  • 数据分散在每一个节点中,不支持范围查询
B+树的特有性质
  • 非叶子节点只存储key,每一层能索引的数据更多。每次io能看到更多数据
  • 树高度低(通常为3层左右),io次数少
  • 叶子节点两两相连,符合磁盘预读特性,减小io次数
  • 范围查询支持良好。真正数据只存储在叶子节点,范围查询只需遍历叶子节点
  • 每一个节点的大小设置为磁盘IO一次的大小(称为页,根据操做系统不一样而定,如16k)

3.5 索引设计原则

  • 索引并非越多越好,过多索引不只增长磁盘空间,并且更新插入数据都要动态维护索引,影响效率
  • 常常做为where条件字段须要创建索引
  • 数据量不多的表不要建索引,全表查询效率比遍历索引可能还快
  • 将使用频率高,区分度大的列放在索引前面。范围查询或不等于查询的列放在最后
  • 不一样值较多的列上创建索引,在不一样值较少的列上不要创建索引,好比性别字段只有男和女,就不必创建索引。若是创建索引不但不会提升查询效率,反而会严重下降更新速度
  • 当惟一性是某种数据自己的特征时,指定惟一索引。使用惟一索引需能确保定义的列的数据完整性,以提升查询速度
  • 在频繁排序或分组(即group by或order by操做)的列上创建索引,若是待排序的列有多个,能够在这些列上创建组合索引
  • 没有必要为同一字段创建重叠索引
  • 选择较短的数据类型,能够有效的减小索引的磁盘占用,提升索引的缓存效果
  • join多个表时,为join的字段创建索引,mysql内部会优化sql语句。且join的字段类型必须是相同的,字符串的字符集也必须相同

五. 数据库优化

1. 索引优化

1.1 索引使用原则

  • 查询语句中必须使用独立的列,不能含有表达式,不然不走索引

  • 符合索引由左到右生效,遇到范围查询就不走索引

    index(a, b, c)

    查询语句 索引是否生效
    where a=3 是,使用了a
    where a=3 and b=5 是,使用了a, b
    where c=4 and a=3 and b=5 是,使用了a,b,c.与查询顺序无关
    where b=3
    where a=3 and c=4 是,只使用了a
    where a>4 and b=5 and c=3 是,只使用了a
  • 数据类型出现隐式转换不会走索引

1.2 索引分析

explain分析

select_type: 查询类型,性能由高到低
  • simple 此查询不包含union或子查询(最多见)
  • primary 最外层查询
  • union union的第一个之外的查询
  • subquery 子查询的第一个select
  • derived 派生表的select(from字句的子查询)
table:查询涉及的表名(别名)
type:判断是全表扫描仍是索引扫描(很重要的字段
  • const/system 根据主键或者惟一索引查询到,只读取一次,速度很是快
  • eq_ref 等值引用。多表join,前表的每一个结果,只能匹配到后表的一行结果,比较一般是=,查询效率较高
  • ref 多表join,非惟一索引,或者使用了最左前缀规则索引的查询
  • range 使用索引范围查询,此类型下ref字段为NULL
  • index 全索引扫描,比all稍快
  • all 全表扫描,查询性能最差,对数据库是灾难
possible_keys:查询可能使用的索引,设置过多的索引会影响性能
key:查询真正使用的索引,通常一个查询只使用一个索引
ken_len:表示where查询使用了索引的字节数(不包括order by/group by,也不是索引自己的长度)

该字段能够评估组合索引是被彻底使用,仍是只有最左部分被使用,越小表示索引占用磁盘空间越小,索引更高效

  • 字符串
    • char(n): n字节长度
    • varchar(n): 若是是utf8编码,则是3n+2, 若是是utf8mb4编码,则是4n+2
  • 数值类型
    • tinyint: 1字节
    • smallint:2字节
    • mediumint:3字节
    • int:4字节
    • bigint:8字节
  • 时间类型
    • date:3字节
    • timestamp:4字节
    • datetime:8字节
  • 字段属性
    • NULL:多加1字节
    • NOT NULL: 不用多加1字节
ref:使用哪一个列或常熟与key一块儿从表中选择行
rows:扫描的行数(重要)
Extra:执行状况和描述
  • Using index 表示在索引树种就能够查找到所需数据,不用扫描数据文件,说明性能不错
  • Using where 使用了where条件限制哪些行匹配下一张表
  • Using temporary 使用临时表存储结果,一般发生在对不一样的列进行order by,效率不高,需优化
  • Using Filesort 须要额外的排序操做,不能经过索引顺序排序。查询时cpu资源消耗大,需优化

2. 参数优化

2.1 参数说明

  • innodb_buffer_size 缓冲区大小
  • innodb_buffer_pool_instance 缓冲池实例个数
  • innodb_old_blocks_pct 读取的页放入缓冲区LRU的位置,默认37%
  • innodb_old_blocks_time 读取的页等待多久才放入LRU
  • innodb_log_buffer_size undo日志缓冲区大小,默认8M
  • innodb_page_size 每一页的大小
  • max_connections 最大链接数
  • key_buffer_size
  • innodb_thread_concurrency 最大并发线程数
  • thread_cache_size 缓存的最大线程数
  • tmp_table_size 超过该值的用硬盘临时表,低于改值的直接放内存
  • query_cache_limit 超过此大小的查询将不缓存
  • query_cache_min_res_unit 缓存块的最小大小
  • query_cache_size 查询缓存大小
  • innodb_log_buffer_size 日志缓冲大小
  • slow_query_log = ON 开启慢查询
  • long_query_time = 3 超过3s的为慢查询
  • innodb_flush_log_at_trx_commit重作日志从缓冲刷新到磁盘的策略:0表示不记录redo日志

3. 主从优化

3.1 概念

经过配置主库和从库,主库负责读取删改,从库负责只读,作到读写分离,并根据读写要求的不一样配置不一样的系统参数

3.2 数据库主从原理

  • 主库打开binlog配置,对主库每次操做都会记录在binlog中
  • 从库经过io线程从主库读取binlog,传输到从库
  • 从库sql线程读取binlog,并应用到从库

3.3 主从配置(确保版本一致)

  • 主从服务器分别添加binlog配置
  • 重启服务
  • 查看主库当前记录的日志位置
  • 从库配置从主库读取到的位置,并开启同步

3.4 使用xtrabackup备份数据

该工具可在不停服的状况下,实现数据同步。

4. 分库分表

1. 垂直拆分

1.1 概念:列拆分,把列比较多的表拆分为多张表

1.2 原则:

  • 把不经常使用的字段单独放在一张表
  • 把text,blob等大字段拆分出来放在附表中
  • 常常组合查询的列放在一张表中

2. 水平拆分

2.1 概念:行拆分,将一张表的数据拆分为多张表存放

2.2 原则:

  • 一般根据id取模存放数据
  • 部分业务逻辑可言经过地区,年份等字段归档拆分(界面上限定住不让跨年查询)

2.3 难点及问题:乐视网分库分表

  • 扩展表时,旧的数据将所有失效,没法再扩展
  • 跨节点join, order by group by的问题,分布式事务等
  • 如何保证id的惟一性

2.4 经常使用的开源组件

  • mycat
  • sharding-sphere
  • tsharing

总结

1. 本文内容串起来以下:

  • 介绍了关关系型数据库和非关系型数据库
  • 知道关系型数据库最重要的特性是事务的一致性,而后介绍了事务的相关特性
  • 如何保证数据一致性:mysql底层作到RR级别事务隔离
  • 用户设计数据库时如何提升一致性:遵照范式,以减小冗余
  • 数据库查询效率怎么提高:设计好的表结构和索引
  • 数据库查询慢的时候怎么优化:介绍了几种优化手段

2. 和前言中咱们提到的几个问题,简短总结一下

怎么设计优雅的表结构?指导原则是什么?

大的前提是遵照范式以减小冗余,其次才综合业务量设置冗余。合理选择字段和创建索引

索引为何那么快?底层为何要用B+树?

索引是排好序的数据结构,B+树是为了适应磁盘数据的存储设计的结构,包括多叉结构下降高度较少io次数,非叶节点不存数据增长索引量,叶节点相连便于范围查询,节点大小设置为页大小充分利用io调用。

怎么设计好的索引? 怎么优化索引?

根据查询条件设置合适的组合索引,时经常使用explain分析并调整索引

经常使用系统参数表明什么意思?怎么优化参数?

mysql优化手段有哪些?

索引优化,参数优化,主从优化,分库分表等等

参考文献

  • 《mysql技术内幕(innodb存储引擎)》
  • 《高性能mysql》
相关文章
相关标签/搜索