2021年11月14日12:56:54
优化介绍:
注意:
不要听信你看到的关于优化的“绝对真理” ,而应该在实际的业务场景下通过测试来验证你关于执行计划以及响应时间的假设。
1 优化思路
1.0 优化方向
在数据库优化方向上主要的方向:安全,性能
安全------> 数据安全性
性能 -----> 数据的高性能访问
1.1 优化要考虑的问题
优化有风险,涉足需谨慎;
1.1.1 优化可能带来的问题
优化不能总是对一个单纯的环境进行,还很可能是一个复杂的已经投产的系统;
优化手段有很大的风险,一定要意识到和遇见到;
任何优化来说调优而带来问题,控制在可接受的范围内才是有成果的。
保持现状或者出现更差的情况都是失败的。
1.1.2 优化需求
稳定性和业务可持续性,通常比性能更重要。
优化不可避免涉及到变更,变更就更有风险;
优化性能编号,维持和变差是等概率的事情;
优化应给是各部门协同,共同参与的工作,任何单一部门都不能对数据库进行优化;
总结:优化工作,是由业务需要驱动的!
1.2 优化维度
硬件:cpu 、内存、存储、网络设备等
系统配置:服务器形同,数据库服务器参数
数据库表结构:高可用,分库分表、读写分离、存储引擎、表设计等
sql及索引:sql语句、索引使用等
优化成本进行考虑:硬件>系统配置>数据库表结构>sql及索引
优化效率进行考虑:硬件<系统配置<数据库表结构< sql及索引
1.3 优化的工具
mysqladmin #mysql客户端,可进行管理操作
mysqlshow #功能强大的查看shell命令
show [SESSION | CLOBAL] variables #查看数据库参数信息
show [SESSION | CLOBAL] status #查看数据库状态信息
show engine innodb staus innodb #引擎的所有状态
information_schema #获取元数据的方法
show processlist #查看当前所有连接的session状态
explain #获取查询语句的执行计划
how index #查询表的索引信息
show-log #记录慢查询的语句
mysqldumpslow #分析slowlog文件
不常用,但是很好用的工具
ZABBIX #监控主机、系统、数据
mysqlslap #分析慢日志
sysbench #压力测试工具
workbench # 管理、备份、监控、分析、优化工具
1.4 数据库使用和优化思路
1.4.1 应急调优思路
针对突然业务办理卡顿,无法正常的业务处理,需要立马解决的场景!
a、show processlist
b、explain分析查询计划,show index from table 分析索引
d、通过执行计划判断,索引问题(有没有、合不合理)或者语句本省问题
e、show status like ‘%lock%’ #查询锁的状态
f、seesion-id;#kill 问题session
1.4.2 常规调优的思路
针对周期性业务卡顿,还是能够使用,过段时间就好了
a、查看slowlog,分析slowlog,分析出查询慢的语句
b、按照一定优先级。进行一个一个的排查所有慢的语句
c 、分析top sql,进行explain调试,查询语句执行时间
d、调整索引或sql语句本身;
2 查询优化
2.1 MYSQL查询流程
A 客户端将查询发送服务器
B 服务器检查查询缓存,如果找到了,就冲查询缓存中返回结果,否则进行下一步
C 服务器解析,预处理
D 查询优化器优化查询
E 生成执行计划,执行引擎调用存储引擎API执行查询
F 服务器将结果返回给客户端
2.2 查询优化
sql 是我们和数据库交流最重要的部分,所以我们在调优的时候,需要花大量时间在sql调优上面。常见的分析手段有慢查询日志,explain分析查询,通过定位分析性能瓶颈,才能更好的优化数据库系统的性能。
2.2.1 慢查询
慢查询日志开启:
在配置文件my.cnf或者my.ini中在【mysqld】一行下面加入两个配置参数
log-slow-queries=/data/mysqldata/slow-query.log
long_query_time=5
慢查询分析:
我们可以通过打开log文件查看哪些sql 执行效率低下。从日志中,可以发现查询时间超过5秒的sql,而小于5秒的没有出现在日志中。
如果慢查询日志中记录内容很多,可以使用mysqldumpslow工具,(mysql客户端安装自带)来对慢查询日志进行分类汇总。
mysqldumpslow 使用可以二次百度
当然在开启慢查询日志后,由于日志记录操作,在一定程度上会占用cpu资源影响mysql性能。所以我们建议可以阶段性的开启慢sql 日志,这样可以帮我们在sql优化过程中,快速的定位系统的瓶颈。
2.2.2 explain
ID 不重要
select_type
simple :简单select 不适用union或者子查询
primary 最外面的select 主查询
union: union中的第二个或者后面的select语句
dependent union :union中的第二个或者后面的select语句,取决于外面的查询
union result :union的结果
subquery :子查询的第一个select
dependent subquery :子查询中的第一个select。取决于外面的查询
derived:到处表的select(from自居中的子查询)
table :
显示这查询的数据关于那张表
TYPE
区间索引,这是重要的列。显示连接使用何种类型。从最好的到最差的“:
system》const》eq_ref > ref > ref_or_null > index_merge >unique_subquery >index_subquery >range>index >all
一般来说,得保证查询至少达到range级别,最好达到ref
possible-keys
指出mysql 能使用那些索引在该表中找到行,如果是空的,没有相关的索引。这时要提高性能,可以通过检查where自居,看是否引用某些字段,或者检查字段不适合索引。
key
实际使用到的索引。如果为null,则没有使用索引。如果为primary的话,表示使用了主键
ref
显示使用哪个列或常数与key一起从表中选择行
rows
显示mysql 认为它执行时查询时必须检查的行数
extra
3 索引优化
3.1 索引介绍
索引的目的是在于提高查询效率,索引是快速搜索的关键。mysql索引的建立对于mysql的高效运行也是很重要的。对于少量的数据我们加索引和不加索引都是一样的。
索引就好比书本的目录,如果没有目录那我们找东西就要从书的第一页查询到最后一页,这个过程就相当于我们在做全表的扫描,当你的数据记录条数越多,你的查询效率就越慢。而索引则能够让我们快速定位到这个记录的位置。从而找到我们想要的结果。
看下面的案例:
创建表结构
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) COLLATE utf8_bin DEFAULT NULL,
`number` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2279339 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
存储过程来模拟数据
DROP PROCEDURE IF EXISTS tb_user_insert;
CREATE PROCEDURE tb_user_insert()
BEGIN
DECLARE i INT;
set i=0 ;
START TRANSACTION;
WHILE i< 1000000 DO
INSERT INTO t_user (`name`,`number` ) VALUES( CONCAT("zhangsan",i),i);
set i=i+1;
END WHILE;
COMMIT;
END;
实验结果:通过两个执行计划对比,我们很明显发现第一次做的是全表扫描,第二次则用索引定位的方式,快速返回结果。所以适当的增加索引是可以提高sql的执行效率的。当然千万别乱加索引。
未建立索引:
建立索引:
可以看到通过索引的方式,我们可以提高查询效率。
3.2 索引类型
主键索引
它是一个特殊的唯一索引,不允许有空值,一般在建表的时候创建主键索引。
PRIMARY KEY (`ID`)
唯一索引 UNIQU
唯一索引列的值必须唯一,但是允许有空值,如果是组合索引,则列值组合必须是唯一。可以在创建表的时候指定,也可以修改表结构。
UNIQUE KEY `NUM` (`NUM`) USING BTREE;
普通索引 INDEX
这个是基本的索引,它没有任何限制。可以在建表的时候指定,也可以修改表结构。
组合索引 INDEX
索引分单列索引,和组合索引(也有人叫联合索引),单列索引,即一个索引质只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引即一个索引包含多个列;
KEY `NUM` (`NUMBER`,`NAME`) USING BTREE;
注意:组合索引前面索引必须要先使用,后面的索引才能使用;
全文索引 fulltext
全文索引,也称为全文检索。是目前搜索引擎使用的一种关键技术。它能够利用分词技术等多种算法智能分析出文本文字中文关键字词使用的频率和重要性,然后按照一定算法规则只能地帅选出我们想要的搜索结果。
3.3索引的存储结构
3.3.1 BTree 索引
mysql中普遍使用B+Tree做索引,也就是BTREE.
特点:
BTREE索引是B+树的机构存储数据
BTREE索引能够加快数据的查询速度
BTREE 索引更适合进行范围查找
使用场景:
全值匹配查找,如根据订单号查询
联合索引时会遵循最左前缀匹配原则,即最左优先
匹配列前缀查询,如sn like ‘987%’
配范围值查找,如 sn>1231
只访问索引的查询
3.3.2 哈希索引
哈希索引在mysql中使用的并不多,目前主要是menory存储引擎使用,在menory存储引擎中将哈希索引作为默认的索引类型。
所谓的hash索引,实际就是通过一定的hash算法,将需要的索引键值进行哈希运算,然后将得到的hash值存入一张hash表中。然后每次需要检索的时候,都会将检索的条件进行相同的hash运算,然后在和hash表中的hash值进行比较得到相应的信息。
特点:
hash索引仅仅只能满足= in 《=》查询,不能使用范围查询
hash索引无法被利用来避免数据的排序操作
hash索引不能利用部分索引键查询
hash索引在任何时候都不能避免表扫描
hash索引遇到大量的hash值相等情况时候性能并不一定就会比BTREE索引高
3.3.2 Full-text 全文检索
full-text 索引页就是我们说的全文索引,mysql 中仅有myisam和innodb存储引擎支持。
对于文本大对象,或者较大的char类型数据,如果使用普通的索引,那么匹配文本前几个字符还是可行的,但是想要陪陪文件中间的几个单词。那么就要使用like 来匹配,这样需要很长的时间来处理。响应时间会大大的增加。这种情况就要是用full-text索引了,在生成full-text索引时,会为文本生成一份单词的清单,在索引的时根据这个单词的清单来索引。
对于较大的数据集,把数据添加到一个没有full-text的索引表,然后添加full-text索引的速度会远大于把数据添加到有full-text的索引表中。
针对较大的数据生成全文索引非常消耗时间和空间
5.6版本前的mysql 自带全文索引只能使用myisam存储引擎,如果是其他数据的数据引擎,那么全文索引将不会生效。5.6版本后innodb存储引擎开始支持全文索引。
在mysql中,全文索引对应为有用,目前还不支持中文。5.7版本之后使用ngram插件开始支持中文。
在mysql中,如果检索字符太短则无法检索得到预期的结果,检索字符串长度至少为4字节。
3.4 索引的使用
索引在使用的过程中能够带来查询速度的提升,但是也会对性能有一些损失。
索引会增加写操作的成本
太多索引会增加查询优优化器的选择时间
当创建索引带来好多多与消耗的时候,才是最优的选择。
使用索引的场景:
逐渐自动创建唯一索引
经常做为作为查询条件在where 或者order by 语句中出现的列要建立索引
作为排序的列要建立索引
查询与其他表关联的字段,外键关系建立索引
高并发添加下倾向建立组合索引
用于聚合函数的列可以建立索引,如count sum等函数
不适用索引的场景
有大量的重复的列不单独建立索引
表记录太少不要建立索引,因为没有太大作用
不会做为查询的列不要建立索引
4 存储优化
4.1 存储引擎介绍
大家通过上表发现,只有innodb支持事务,所以我们的业务系统一般都是用innodb
4.1.1 innodb存储引擎
事务的四大属性:
原子性
要么全部成功,要么全部失败
一致性
一致性值事务必须是数据库从一个一直性状态变换到另一个一致性状态。也就是说事务执行之前和之后都必须处于一致性状态。
好比,转账前AB两个用户账户余额共计1000元,转账后,AB两个账户余额共计还是1000元,这就是事务的一致性。
隔离性
隔离性,是指多个用户并发访问数据库是,比如同时操作一张表,数据库为每个用户开启的事务,不能被其他的事务所干扰,多个并发事务之间相互隔离。
持久性
持久性是指一个事务一旦提交了,那么对数据库中的数据改变就是永久性的。即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
事务的隔离级别:
由低到高依次为Read uncommitted(未授权读取、读未提交)、
Read committed(授权读取、读提交)、
Repeatable read(可重复读取)、
Serializable(序列化),
这四个级别可以逐个解决脏读、不可重复读、幻象读这几类问题。
使用场景:
经常update、insert的表,使用处理多并发的写请求
支持事务必选innodb
可以从灾难中恢复(日志+事务回滚)
外键约束、列属性AUTO_INCREMENT支持
4.1.2 MYISAM 存储引擎
特点:
MYISAM 不支持事务,不支持外键。select/insert为主的应用可以使用该引擎
每个MYISAM在存储成3个文件,拓展名分别为:
frm :存储表定义(表结构等信息)
MYD,存储数据
MYI,存储索引
不同的MYISAM表的索引文件和数据文件可以放置到不同的路劲下
MYISAM类型的表提供修复工具,可以使用check table语句来检查MYISAM表健康,并用REPAIR TABLE语句修复一个损坏的MYISAM表
在mysql5.6之前,只有MYISAM支持full-text全文索引
使用场景:
经常select/insert的表,插入不频繁,查询非常频繁
不支持事务
做很多count的计算
4.1.3 MYISAM和innodb区别
innodb和myisam是需对人在使用mysql时最常用的两个存储引擎,这两个存储引擎各有优劣,视具体应用而定。 myisam 不支持事务而innodb支持。myisam强调性能,执行速度比innodb类型更快,而innodb提高事务支持以及外键等高级数据库功能。
具体实现的差别:
myisam是非事务安全型的,而innodb是事务安全型的
myisam 是表级锁,而innodb支持行级锁
myisam不支持外键,而innodb支持外键
myisam 相对简单,所以效率上要犹豫innodb。小型应用可以考虑使用myisam
innodb表比myisam表更安全
4.2 存储优化
4.2.1 禁用索引
对于使用索引的表,插入记录时,mysql 会对插入的记录建立索引。如果插入大量的数据,建立索引会降低 插入数据的速度。为了解决这个问题,可以在批量插入数之前禁用索引,在数据插入完成后再开启索引。
#禁用索引语句:
ALTER TABLE TABLE_NAME DISABLE KEYS;
#启用索引语句:
ALTER TABLE TABLE_NAME ENABLE KEYS;
myisam对于空表批量插入数据,则不需要进行该操作,因为myisam 引擎的表示在导入数据后才建立索引。
4.2.2 禁用唯一性检查
唯一性校验会降低插入记录的速度,可以在如记录前禁用唯一性检查。插入数据后再开启。
#禁用
SET UNIQUE_CHECKS=0;
#开启
SET UNIQUE_CHECKS=1;
4.2.3 禁用外键
插入数据之前执行禁止对外键的检查,数据插入完成后再恢复,可以提高插入的速度;
#禁用
SET FOREIGN_KEY_CHECKS=0;
#开启
SET FOREIGN_KEY_CHECKS=1;
4.2.4 批量插入数据
一次插入多条记录;
INSERT INTO TABLE_NAME () VALUES (),();
4.2.5 禁止自动提交
插入数据之前执行数据的自动哦调教,数据插入完成后在恢复,可以提高插入的速度。
#禁用
SET AUTOCOMMIT=0;
#开启
SET AUTOCOMMIT=1;
5 数据库结构优化
5.1 优化表结构
尽量将表指点定义为not null 约束,这个是mysql中含有空值列很难进行查询优化,null值会是索引即索引统计信息变的很复杂
对于只包含特定类型的字段,可以使用enum se等数据类型
数值型字段比较字符串的比较效率会高的多。字段进项使用最小,最简单的数据类型。比如ip地址可以使用int类型
进项使用TINYINT SMALLINT MEDIUMINT 作为证书的类型而非int。如果非负责加上UNSIGNED。但对证书类型指定宽度,如果int(11)没有任何作用,因为指定类型表示范围已确定。
varchar 长度值分配真需要的空间
金莲使用timestamp而非datetime,但是timetamp只能表示1970-2038年,比datetime表示范围小的多,而且timestamp的值因时区不同而不同。
单标不要太多字段,建议在20以内
合理的加入冗余字段可以提高查询速度, 最常见的订单详情表,适当的冗余可以避免多表的查询。
5.2 表拆分
5.2.1 垂直拆分
5.2.2 水平拆分
5.3表分区
表分区有什么好处?
与单个磁盘或文件系统分区相比,可以存储更多的数据。
对于那些已经失去保存意义的数据,通常可以通过删除与那些数据有关的分区,很容易地删除那些数据。相反地,在某些情况下,添加新数据的过程又可以通过为那些新数据专门增加一个新的分区,来很方便地实现。
一些查询可以得到极大的优化,这主要是借助于满足一个给定WHERE语句的数据可以只保存在一个或多个分区内,这样在查找时就不用查找其他剩余的分区。因为分区可以在创建了分区表后进行修改,所以在第一次配置分区方案时还不曾这么做时,可以重新组织数据,来提高那些常用查询的效率。
涉及到例如SUM()和COUNT()这样聚合函数的查询,可以很容易地进行并行处理。这种查询的一个简单例子如 “SELECT salesperson_id, COUNT (orders) as order_total FROM sales GROUP BY salesperson_id;”。通过“并行”,这意味着该查询可以在每个分区上同时进行,最终结果只需通过总计所有分区得到的结果。
通过跨多个磁盘来分散数据查询,来获得更大的查询吞吐量。
分区适用于例如日志记录,查询少。一般用于后台的数据报表分析。对于这些数据汇总需求,需要很多日志表去做数据聚合,我们能够容忍1s到2s的延时,只要数据准备能够满足需求就可以。
mysql 主要支持4中分区:
range分区
list预定义列表分区
hash分区
key键值分区
录入使用key键值分区:
#通过id 进行分区。分成8个区
DROP TABLE IF EXISTS `TB_LOG`;
CREATE TABLE `TB_LOG` (
`ID` int(11) NOT NULL,
`NAME` varchar(10) DEFAULT NULL,
`AGE` smallint(6) NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY KEY(ID)
PARTITIONS 8;
[root@localhost ~]# systemctl restart docker
[root@localhost ~]# docker start liubijun_mysql
liubijun_mysql
[root@localhost ~]# docker exec -it liubijun_mysql /bin/bash
root@0a816c1be4f2:/# cd /var/lib/mysql/tensquare_article
root@0a816c1be4f2:/var/lib/mysql/tensquare_article# ls -l
total 880
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p0.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p1.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p2.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p3.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p4.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p5.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p6.ibd
-rw-r-----. 1 mysql mysql 114688 Nov 20 03:28 TB_LOG#P#p7.ibd
-rw-r-----. 1 mysql mysql 114688 Aug 20 2019 tb_article.ibd
-rw-r-----. 1 mysql mysql 114688 Apr 22 2019 tb_channel.ibd
-rw-r-----. 1 mysql mysql 114688 Apr 22 2019 tb_column.ibd
root@0a816c1be4f2:/var/lib/mysql/tensquare_article#
表分区的限制因素
一个表最多只能有1024个分区。
MySQL5.1中,分区表达式必须是整数,或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。
如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
分区表中无法使用外键约束。
MySQL的分区适用于一个表的所有数据和索引,不能只对表数据分区而不对索引分区,也不能只对索引分区而不对表分区,也不能只对表的一部分数据分区。
5.4 读写分离
大型的网站会有大量的并发访问。如果还是传统的数据存储方案。只是靠一台服务器处理,如此多的数据库连接,读写操作,数据库必然会崩溃,数据丢失的话,后果不堪设想。,这个时候我们就要考虑如何降低单台服务器使用的的压力,提升整个数据库服务的承载能力。
我们呢一般情况多都读多写少,也就是说对于数据库的读的压力较大,这样分析我们可以采用数据的集群方案。其中一个主库,负责写入数据,我们称为写库。其他的都是从库,负责读取数据,我们称为读库。
mysql 自带主从复制功能,我们可以使用主从复制的主库作为写库,从库和主库进行数据同步。那么可以使用多个从库作为读库,以完成读写分离的效果。
5.5数据库集群
如果访问量非常大,虽然读写分离能够缓解压力,但是一旦写操作一台服务器都不能承受,我们这个时候就好考虑使用多台服务器实现写操作。
我使用的是mycat搭建mysql 集群。针对id 进行策略,写到多台数据库服务器上。由mycat负责维护集群节点的使用。
6硬件优化
6.1 内存
足够大的内存,是提高mysql数据库性能的方式之一。内存的io比硬盘快的多。可以增加系统的的缓冲区容量,使得数据在内存停留的时间更长,以减少磁盘的io。
6.2磁盘
mysql每秒钟都在进行大量、复杂的查询操作。对磁盘的读写量可想而知。所以通常认为磁盘的I/O是制约mysql性能的最大因素之一,对于日均访问量在100万PV以上的系统。由于磁盘的I/O的制约,mysql的性能非常低下。考虑一下几种解决方案:
使用ssd或者pcie-ssd设备,至少获得数百甚至上万被的iops提升。
购置阵列卡,可以明显提升iops
尽量选用RAID-10,而非RAID-5
是用机械硬盘的话,尽量选用硬盘转速高的。一般分为5400,7200,15000(服务器建议15000)
6.3 CPU
cpu 仅仅只能决定运算的速度,及时是运算速度都还取决于内存之间的总线带宽以及内存本身的速度。但是一般情况下,我们都要悬着较快的cpu。
关闭节能模式。操作系统和cpu硬件配合。系统不繁忙的时候,为了节约电能和减低温度。他会将cpu降频。为了保证mysql能够充分的利用cpu资源,建议设置cpu为性能最大模式。
6.4 网络
应该进可能的悬着高网络延时地,吞吐量高的设备;
网路延时:不同的网路设备延时会有差异。建议延时越低越好。
吞吐量:对于数据库集群来说,各个节点之间的网络吞吐量可能直接决定集群的处理能力。
7 缓存优化
7.1 查询缓存
因为redis的高速发展。大家简单了解下就好。
原文: https://blog.csdn.net/shi860715/article/details/109845709