Mysql深入学习
实例和故事
什么影响了Mysql的性能
磁盘的配置和选择
- 虽然内存可以很大程度上提高数据库的性能,但是数据库最终还是要将数据写到磁盘进行持久保存,所以磁盘IO仍然很重要
- 常见的磁盘
- 传统的机械硬盘
特点:最常见、使用最多、价钱低、存储空间大、读写速度慢- 读取数据过程
- 移动磁头到磁盘表面上的正确位置—数据访问
- 等待磁盘旋转,磁盘旋转完后所需的数据都会被磁头读出—数据传输
- 如何选择?
- 存储容量—目前机械硬盘的存储容量都非常大,但是有时候服务器的磁盘容量并不大需要使用RAID增加磁盘性能
- 传输速度—就是磁盘旋转的速度
- 访问时间—就是移动磁头到磁盘表面正确位置需要的时间
- 主轴转速—越快越好,越快那么磁盘旋转就越快、磁头的移动速度也越快
- 物理尺寸—其他四点相同的情况下,物理尺寸越小那么磁头移动的距离就越小花费时间更短。
- 读取数据过程
- 传统的机械硬盘
使用RAID增加传统机械硬盘的性能
- RAID的作用就是可以把多个容量较小的磁盘组成一个容量更大的磁盘(这样就可以用多个尺寸小的磁盘代替一个尺寸大的磁盘提高访问速度),并且提供数据冗余保证数据完成性
- RAID有多个级别
- RAID0是最早出现的RAID模式,只需要两块或两块以上的硬盘即可,成本低,可以提供整个磁盘的性能和吞吐量,但是没有提供数据冗余和错误修复能力。
RAID0就相当于把多个磁盘串联在一起,写入数据时可以同时向三个磁盘写入这样效率就相当于提升了三倍,但由于不提供数据冗余一旦一块磁盘坏掉那么数据就丢失了,坏掉的概率就是使用一块磁盘的三倍,所以适合于不担心数据丢失的情况,比如可以直接从其他数据库进行备份的备库。 - RAID1又称磁盘镜像,原理就是数据在写入一块磁盘的同时,会在另一块磁盘上生成镜像文件,最大限度保证系统可靠性和可修复性。—用于两块磁盘
- RAID5把奇偶校验块分散在多个磁盘上,如果任何一个磁盘数据失效,都可以通过其他磁盘的数据以及奇偶校验快恢复失效磁盘数据,但是如果两块磁盘同时失效那么整个卷都将无效
- RAID10,对数据库性能最好的一种RAID级别,它是对磁盘先做RAID1然后在对各组RAID1做RAID0,所以读写都有良好的性能。
- RAID级别的选择:
- RAID0是最早出现的RAID模式,只需要两块或两块以上的硬盘即可,成本低,可以提供整个磁盘的性能和吞吐量,但是没有提供数据冗余和错误修复能力。
固态存储SSD或PCIe卡
- 特点
- 固态硬盘比机械硬盘有更好的随机读写性能, 固态硬盘随机读的性能还要优于随机写。对数据库来说随机读写是很重要的,因为就算是机械硬盘顺序读写性能也比较高可以满足数据库的需要。
- 固态硬盘能更好的支持并发
- 固态硬盘相比机械硬盘更容易损坏
- SSD固态存储硬盘
- 使用SATA接口,直接插在主机的SATA接口就可以直接使用,不需要安装额外的驱动。
- 和机械硬盘一样支持RAID技术,但是使用的RAID控制器不同
- 由于使用SATA接口,所以SSD的速度受SATA接口的限制,如果将支持SATA3.0的SSD插在SATA2.0接口上,那么SSD的速度就会从3.0接口下降到2.0接口。
- PCIe卡
- 不能使用SACA接口,使用PCIE接口需要独特的驱动和配置
- 读写速度不受接口限制,所以性能相比SSD要好,价钱也比SSD要高。
- 但是PCIe卡吃内存和CPU
- 固态硬盘使用场景
- 适用于存在大量随机IO的场景。
大多数数据库服务器使用的都是机械硬盘,而机械硬盘的随机IO性能很差,所以在使用mysql的时候,会对数据库进行设计尽可能将随机IO变为顺序IO提高IO性能。
随机IO:写数据文件:ibd, MYD,MYI。
顺序IO:主要是在日志方面,尽量减少记录日志对整个系统的影响
增加内存,将存储的数据缓存在内存中,在内存中做数据读写是顺序IO,这样就可以减少随机IO,但是内存并不能无限制使用,所以还是需要使用固态硬盘这样随机IO速度会快一点。 - 解决单线程IO瓶颈
低版本的Mysql只支持单线程执行sql,不能通过多线程的方式提高IO速度,就只能通过使用固态硬盘提高一下IO速度。 - 如果只有一块固态硬盘应该用在从服务器,因为主服务器可以采用多线程写入,而mysql的主从复制中从服务器是使用单线程进行数据复制,所以将固态硬盘用在从服务器提高从服务器的复制效率减少主从复制延迟。
- 适用于存在大量随机IO的场景。
网络存储SAN、NAS
适用于存储数据库备份文件,当数据库损坏时,可以创建一个新的数据库实例将数据导入。由于本身随机IO性能差所以不适合作为数据库服务器。
- 网络对性能的影响
- 网络带宽对性能的影响。当数据库服务器的网络带宽被占满的时候,Web服务器对数据库的请求就会被阻塞,导致服务器再向数据库发送请求就会建立新连接,当数据库连接数满的时候,在建立连接就会报错。
- 网络质量对性能的影响。网络不好经常丢包,就会导致大量网络包的重复发送,这就进一步增加了网络的负担,可能导致网络风暴将整个网络的带宽占满,网络陷入瘫痪。
- 修改建议
- 采用高性能和高带宽的网络接口设备和交换机
- 每个服务器都会有多个网卡,将多个网卡绑定成一个使用,增强可用性和带宽
- 尽可能进行网络隔离,重点是内外网隔离以及企业网络和业务网路进行隔离
- 服务器硬件对性能影响总结
- CPU
- 64位CPU一定要工作在64位的操作系统下
- 对于并发高的场景CPU的核数比较重要
- 对于计算密集的场景以及复杂的SQL则频率越高越好
- 内存
- 选择主板所能使用的最高频率的内存,频率越高读写越快
- 内存的大小对性能很重要,所以尽可能的大,这样就可以把更多的数据放在内存将随机IO转换成顺序IO
- 硬盘
- 性能由高到低
PCIe -> SSD -> Raid10 -> 机械硬盘 -> SAN网络存储做数据库文件备份
- 性能由高到低
- CPU
操作系统对性能的影响
- windows下的mysql不区分大小写,linux下的mysql区分大小写 。可以通过配置mysql参数强制mysql数据库实例的名字和表的名字小写。
- CentOS系统参数优化
- 内核相关参数(/etc/sysctl.conf)
- 网络参数:
net.core.somaxconn——端口最大连接数net.core.somaxconn——端口最大连接数
net.core.netdev_max_backlog——等待进入服务器的数据包的最大队列
net.ipv4.tcp_max_syn_backlog——等待进入服务器的SYN包的最大队列
提高并发数
net.ipv4.tcp_fin_timeout=10
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_tw_recycle=1
加快空闲TCP连接的回收,如果有大量请求而连接占满了此时会出现数据库无法连接错误
net.core.wmem_default=87380
net.core.wmem_max=16777216
net.core.rmem_default=87380
net.core.rmem_max=16777216
TCP发送、接收缓冲区的默认值、最大值
net.ipv4.tcp_keppalive_time=120——发送keepalive消息的时间间隔,探测连接是否有效
net.ipv4.tcp_keepalive_intvl=30——当发送keepalive消息未收到响应时,重发的时间间隔
net.ipv4.tcp_keepalive_probes=3——在认定tcp连接无效前发送消息个数 - 内存参数
kernel.shmmax = 4294967295——定义单个共享内存段的最大值。1)这个参数应该设置的足够大,以便能在一个共享内存段中容纳整个Innodb缓冲池大小,如果比较小那么启动实例的时候就需要创建多个共享内存段启动较慢。 2)这个值的大小对于64位的linux系统,最大值为物理内存值-1byte,建议为大于物理内存的一半,一般大于Innodb缓冲池大小即可。
vm.swappiness=0——在Linux中磁盘有一个特殊分区叫做交换分区,当操作系统没有足够内存时就会将一些虚拟内存写到磁盘的交换区进行内存交换,这对Mysql的性能有灾难性的影响。 设置该参数为0就是告诉linux除非虚拟内存满了,否则不要使用交换区。
- 网络参数:
- 内核相关参数(/etc/sysctl.conf)
- 增加资源限制(/etc/security/limit.conf)
- 将* soft nofile 65535、* hard nofile 65535加到limit.conf文件末尾,设置同时可打开文件数最大值为65535。重启才能生效。
- 磁盘调度策略
- 默认是cfq策略对数据库不太友好,deadline(截止时间调度策略)是对数据库应用最好的选择。
- echo deadline > /sys/block/sda/queue/scheduler——通过该命令可以将磁盘调度策略设置为deadline策略。
- 文件系统对性能的影响
linu常见的文件系统有:EXT3、EXT4、XFS。其中XFS文件系统性能最好。
Mysql体系结构
客户端:负责连接处理、授权认证、安全等功能。
Mysql服务层:所有跨存储引擎的功能都在该层实现。比如Select语句,不管什么存储引擎它的功能就是查询出数据按条件进行过滤,这个功能是在服务层实现的,但是具体怎么查由存储引擎层实现。
存储引擎层:特点就是灵活,可以根据不同应用的特点选择不同的存储引擎。存储引擎是基于表的,在一个库中可以使用多种存储引擎但是不推荐。
Mysql常用存储引擎之MyISAM
- MyISAM是Mysql5.5之前版本默认存储引擎
- MyISAM存储引擎表有三个存储文件frm、MYD、MYI。其中frm并不是特有的文件,所有存储引擎都有该文件,该文件用于记录表结构。MYD存储是数据信息,MYI存储的是索引信息。
- 特性
- 并发性和锁级别
MyISAM使用的是表级锁,在对表中数据做写操作时,会对整个表加排他锁,在对表中数据做读操作时,会对整个表加共享锁。对于读写混合的操作效率较低,但是对于只读操作效率还是可以的—因为共享锁不会阻塞读操作。 - 表损坏修复
check table tablename——对表进行检查,通过msg_Text查看表是否损坏
repair table tablename——对表进行修复 - MyISAM表支持的索引类型
- 全文索引
- Clob、Blob类型字段的前缀索引
- MyISAM表支持数据(包括索引)压缩
如果表中数据只为查询使用,而不存在写操作,那么可以对表进行压缩,减少磁盘占用空间
压缩后会产生一个myIsam.OLD文件,OLD文件是压缩前的数据文件
数据压缩后的表不能进行查询
命令:myisampack -b -f(强制压缩) myIsam.MYD - 限制
Mysql5.0版本及之前的版本,MyIsam表默认最大为4G,可以在建表时通过修改MAX_Rows和AVG_ROW_LENGTH修改表最大值。
Mysql5.0版本后默认表存储最大数据为256TB。 - 适用场景:
- 由于不支持事务,适合非事务型应用。比如报表、数据仓库。
- 由于可以对表进行压缩,适合只读类应用。
- 空间类应用。在Mysql5.7之前MyISAM是唯一支持空间函数的存储引擎。
- 其他情况基本使用Innodb存储引擎。
- 并发性和锁级别
Innodb存储引擎
- Mysql5.58开始将Innodb作为默认存储引擎,适合大部分应用
- Innodb将每个表中的数据(包括索引)存储在表空间。如果innodb_file_per_table参数的值为ON,那么会为每一张表创建一个文件扩展名为ibd的表空间;如果参数值为OFF,那么使用系统表空间ibdataX(X是从1开始的数字)存储表中数据。
- 查看mysql参数命令,show variables like ‘参数名’
- Mysql5.5及之前版本该参数值默认为OFF
- 系统表空间和独立表空间如何选择
- 删除表中数据后,表空间的大小并不会减小。系统表空间无法简单的收缩文件大小,而独立表空间可以通过optamize table命令收缩表空间文件大小。
- 使用系统表空间,所有表的数据存储在一个表空间文件,对多张表的数据进行操作在文件系统层面是按顺序进行的;而独立表空间,每张表拥有一个文件,对多张表进行操作在文件系统层面可以同时对多个文件进行操作。
- 推荐使用独立表空间。Mysql5.6之后默认就是独立表空间。
- 把原来存储在系统表空间中的表转移到独立表空间的方法
- 使用mysqldump导出所有表的数据
- 停止Mysql服务,在my.cnf文件中将innodb_file_per_table参数的值设置为ON,删除Innodb相关文件
- 重启Mysql服务,重建Innodb系统表空间,此时系统表空间不包含任何表的数据
- 导入所有表数据
- Innodb存储引擎的特性
- Innodb是一种事务型存储引擎,完全支持事务的ACID特性。
- Innodb通过Redo Log(重做日志)和Undo Log(回滚日志)来实现原子性、一致性、持久性
- 使用Redo Log实现持久性。Redo Log由Redo Log缓冲区和Redo Log文件组成,Redo Log文件叫做ib_logfileX(X是数字)。
- undo Log存储修改前的数据,当事务执行失败或者使用rollback命令,会读取undo Log中的数据进行事务回滚,保证事务的原子性、一致性。
- 当数据库对数据做修改的时候,需要把数据从磁盘读到buffer pool中,然后在buffer pool中进行修改,当buffer pool 中的数据修改完后,将修改后的数据存储到Redo Log(顺序IO),提交事务将Redo Log中的数据刷新到磁盘数据文件。
假设修改 tba 表中 id=2的行数据,把Name=‘B’ 修改为Name = ‘B2’ ,那么redo日志就会用来存放Name='B2’的记录,如果事务提交在从redo日志flush 到磁盘文件时出现异常,可以使用redo log实现重做操作,保证事务的持久性。
- Innodb支持行级锁,这样在进行读写操作的时候,锁住的数据更少可以最大程度支持并发,行级锁是由存储引擎层实现的。
- 什么是锁
- 锁的主要作用是管理共享资源的并发访问
- 用于实现事务的隔离性
- 锁的类型
- 共享锁(读锁),读锁是共享的,多个读操作可以使用同一个锁不会相互阻塞。
- 排他锁(写锁),写锁是排他的,排斥所有的锁也就是会阻塞读写操作。
- 锁的粒度:
- 行级锁——只锁住一行的数据
- 表级锁——锁住整个表的数据
数据库设计对性能的影响
- 过分的反范式化为表建立太多的列
- 过分的范式化会将一张表拆成多张表,造成太多的表关联
- 使用不恰当的分区表
- 使用外键
这些都会降低效率
总结
- 性能优化顺序
- 数据库表结构设计、SQL语句
- 存储引擎的选择和Mysql服务器参数配置
- 操作系统的选择和参数配置
- 提升硬件
MySql基准测试
- 什么是基准测试
基准测试就是一种简单的压力测试,不会考虑业务逻辑,只是评估服务器在不同压力下的性能。 - 基准测试的目的
- 建立Mysql服务器的性能基准线
- 模拟比当前系统更高的负载,找出系统的扩展瓶颈
在一定的数据量下,增加并发量,观察QPS、TPS,找到性能最优时的并发量 - 测试不同的硬件、软件和操作系统配置下的性能
- 证明硬件升级后的服务器的配置是否正确
- Mysql基准测试的常见指标
- TPS:每秒处理的事务数
- QPS:每秒处理的查询数
- SQL执行的响应时间(百分比响应时间比如90%都是5s就可以认为是5s)
- 并发量:同时处理的查询请求的数量
- 基准测试的步骤
- 设计
- 对Mysql服务器进行基准测试
- 使用什么样的数据
- 如果想接近实际情况,可以使用生产SQL、生产数据
- 如果只是修改了某些参数,可以使用测试工具生成测试数据、测试sql进行测试
- 准备测试数据以及系统数据(CPU使用率、磁盘IO、网络流量)收集脚本
- 收集测试数据脚本
2:时间间隔
3:测试数据文件所在目录
4:脚本运行标识文件
6:Mysql命令所在位置
8:循环,只要运行标识存在就会一直循环
9:测试数据文件名
13:收集系统负载情况
15:收集Mysql全局信息
17:收集innodb存储引擎状态信息
19:Mysql当前连接线程情况 - 运行基准测试
- 保存及分析基准测试结果
- 基准测试中容易忽略的问题
- 使用生产环境数据时只使用了部分数据—应该使用生产库的完全备份数据
- 在多用户场景下只做单用户测试——应该模仿多线程并发访问
- 在单服务器上测试分布式应用——如果生产库做了读写分离或者主从复制,那么测试的数据库应该具有相同的结构,因为读写分离、主从复制会具有一些性能损耗。
- 反复执行同一查询——容易命中缓存,无法反映真实性能。
- 设计
- 基准测试工具
mysqlslap是Mysql服务器自带的基准测试工具,随mysql一起安装。- 特点
- 可以模拟并发,并且收集相关信息
- 可以指定或者生成sql脚本
- 常用参数说明
- –auto-generate-sql:由工具自动生成sql脚本进行测试
- –auto-generate-sql-add-autoincrement:在生成的表中增加自增Id
- –auto-generate-sql-load-type:测试使用的sql类型,默认是混合包含增、删、改、查
- –auto-generate-sql-write-number:初始化数据时生成的数据量
- –concurrency:并发执行的线程数
- –engine:要测试表的存储引擎,可以使用逗号分割多个存储引擎,不指定使用默认的存储引擎。如果指定多个存储引擎,会按顺序进行测试,每测试一个存储引擎会将上一个存储引擎表删除,所以不能和–no-drop共用。
- –no-drop:指定不清理测试数据。
- –iterations:测试运行次数。每进行一次测试都会将上一次测试的数据删除,所以不能和no-drop共用。
- –number-of-queries:指定每一个线程执行的查询数量
- –debug-info: 指定输出额外的内存、CPU统计信息
- –number-int-cols:指定测试表中包含的int列的个数
- –number-char-cols:指定测试表中包含的varchar列的个数
- –create-schema:指定用于测试的数据库的名称
- –query:指定自定义sql脚本
- –only-print:不运行测试脚本,只打印测试脚本,查看测试脚本是否符合我们的需要
- 特点
Mysql数据库结构优化
数据库结构优化介绍
- 数据库结构优化的目的
- 减少数据冗余。数据冗余—相同的数据在某几个地方都存在或者某一列的数据可以由其他列得到。
- 尽量避免数据出现更新、插入、删除异常
- 插入异常:表中的某个实体随着另一个实体而存在
- 更新异常:更新某个实体的单独属性时,需要对多行数据进行更新
- 删除异常:删除表中某一实体会导致其他实体的消失
- 节约数据存储空间
- 提高查询效率
- 数据库结构设计的步骤
- 需求分析
全面了解产品设计的存储需求、数据处理需求、数据的完整性和安全性 - 逻辑设计
设计数据实体之间的逻辑关系,首先根据数据库的三范式进行表结构设计减少数据冗余和数据维护异常,然后根据具体的查询sql在进行反范式化设计提高sql查询性能。 - 物理设计
选择合适的存储引擎以及为表中的字段选择合适的数据类型 - 维护优化
随着业务的不断变化,根据实际情况对索引、表结构进行优化。
- 需求分析
- 数据库三范式
- 第一范式
- 表中所有字段都是单一属性,不可再分
- 表中所有字段都是基本数据类型
- 设计出来的表都是二维表
- 第二范式
- 一个表中只能有一个单一的业务主键,或者非主属性对复合主键中的所有主键列都有依赖关系—非主属性不能依赖于部门业务主键列
- 第三范式
- 表中的非主属性不能传递依赖于主键列。
- 第一范式
- 反范式化设计
反范式化设计就是为了提高查询性能违反范式化要求,允许存在部分数据冗余。 - 范式化和反范式化的优点与缺点
- 范式化的优点与缺点
- 优点
- 尽量减少冗余数据
- 冗余数据越少表的列越少,表越小,更新的效率越高
- 缺点
- 对于查询需要对多个表进行关联
- 更难进行索引优化—多个常用的列被分割在多个表中,如果在一个表中可以针对多个列建立一个覆盖索引。
- 优点
- 反范式化的优点与缺点
- 优点
- 减少表关联
- 可以更好的进行索引优化
- 缺点
- 产生数据冗余和数据维护异常
- 降低数据更新的效率
- 优点
- 范式化的优点与缺点
- 物理设计涉及的内容
- 定义数据库、表、字段的命名规范
- 都采用小写字母,多个单词用_分开
- 遵守表意性原则
- 选择合适的存储引擎
基本都采用innodb存储引擎 - 为表中的字段选择合适的数据类型
当一个列可以选择多种数据类型时,优选考虑数字类型,其次是日期类型或二进制类型,最后是字符类型。对于相同级别的类型,优先考虑占用空间小的数据类型。- 选择正确的整数类型—对于整数类型来说限制宽度没有用,要根据存储数据的范围选择占用空间小的整数类型
- 选择正确的实数类型
DECIMAL(18,9)代表最大18位数,有9位小数。需要9个字节存储
MYSQL5.0版本之后DECIMAL最多允许存储65个数字 - 选择正确的字符串类型
varchar和char的长度单位是字符,一个字符是三个字节- varchar类型
- 存储特点
- varchar用于存储变长字符串,只占用必要的存储空间
- 列的最大长度小于255,需要占用一个额外的字节记录字符串长度
- 列的最大长度大于255,需要占用两个额外的字节记录字符串长度
- 长度选择
- 根据业务存储的数据选择最小的符合需求的长度
- varchar虽然是变长字符串,在磁盘中存储数据的时候是按照数据的实际大小进行存储,但是mysql为了更有效的优化查询在内存对varchar使用的是固定长度例如隐式的临时表。
- MYSQL5.7及之前修改varchar列的宽度是需要锁表的,MYSQL5.7之后修改varchar列的宽度小于255不需要锁表大于255需要锁表
- 适用场景
- 字符串最大长度比平均大很多
- 字符串很少被更新—由于是变长的容易产生内存碎片
- 使用了多字节字符集存储字符串—例如UTF-8一个汉字是三个字节,一个数字或者字母是一个字节
- 存储特点
- char类型
- 存储特点
- char类型是定长的
- 字符串存储在char类型的列中会删除末尾的空格
- char类型的最大长度是255
- 适用场景
- 适合存储字符串长度相似的值
- 适合存储短字符串—例如性别,char(1)只占用3个字节,而varchar(1)占用4个字节
- 适合经常更新的字符串列
- 存储特点
- varchar类型
- 正确存储日期类型
- 选择正确的整数类型—对于整数类型来说限制宽度没有用,要根据存储数据的范围选择占用空间小的整数类型
- 定义数据库、表、字段的命名规范