mysql查询的过程图
为什么要优化
- 系统的吞吐量瓶颈往往出现在数据库的访问速度上
- 随着应用程序的运行,数据库的中的数据会越来越多,处理时间会相应变慢
- 数据是存放在磁盘上的,读写速度无法和内存相比
如何优化
- 设计数据库时:数据库表、字段的设计,存储引擎
- 利用好MySQL自身提供的功能,如索引等
- 横向扩展:MySQL集群、负载均衡、读写分离
- SQL语句的优化(收效甚微)
一、字段设计阶段
选取最适用的字段属性
1. 字段的宽度设得尽可能小
MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。
2. 尽量把字段设置为NOTNULL
在可能的情况下,应该尽量把字段设置为NOTNULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
3. 确定数据定义为ENUM类型
对于某些文本字段,例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。这样,我们又可以提高数据库的性能。
4. 单表字段不宜过多,可以预留字段
满足业务需求的前提下二三是个字段就是极限了,可以预留字段便于扩展。
遵循数据表的设计规范
1. 第一范式(1NF)
字段值具有原子性,不能再分(所有关系型数据库系统都满足第一范式); 例如:姓名字段,其中姓和名是一个整体,如果区分姓和名那么必须设立两个独立字段;(字段不可分)。
2. 第二范式(2NF)
一个表必须有主键,即每行数据都能被唯一的区分;备注:必须先满足第一范式;(有主键,非主键字段依赖主键。)
3. 第三范式(3NF)
一个表中不能包涵其他相关表中非关键字段的信息,即数据表不能有沉余字段;备注:必须先满足第二范式;(非主键字段不能互相依赖)
二、存储引擎选择
MyISAM和Innodb比较
- InnoDB支持事物,而MyISAM不支持事物
- InnoDB支持行级锁,而MyISAM支持表级锁
- InnoDB支持MVCC(多版本并发控制,无非就是乐观锁的一种实现方式), 而MyISAM不支持
- InnoDB支持外键,而MyISAM不支持
- InnoDB不支持全文索引,而MyISAM支持。
三、索引
什么是索引
关键字与数据的映射关系称为索引(==包含关键字和对应的记录在磁盘中的地址==)。关键字是从数据当中提取的用于标识、检索数据的特定内容。
索引为什么快
- 关键字相对于数据本身,数据量小
- 关键字是有序的,二分查找可快速确定位置
索引类型
- 普通索引(
key
) - 唯一索引(
unique key
) - 主键索引(
primary key
) - 全文索引(
fulltext key
)
三种索引的索引方式是一样的,只不过对索引的关键字有不同的限制:普通索引:对关键字没有限制。唯一索引:要求记录提供的关键字不能重复。主键索引:要求关键字唯一且不为null
四、缓存查询
查看是否开启
开启操作
windows上是my.ini
,linux上是my.cnf
在[mysqld]
段中配置query_cache_type
:
- 0:不开启
- 1:开启,默认缓存所有,需要在SQL语句中增加
select sql-no-cache
提示来放弃缓存 - 2:开启,默认都不缓存,需要在SQL语句中增加
select sql-cache
来主动缓存(==常用==)
在客户端设置缓存大小
开启缓存之后设置缓存大小:set global query_cache_size=
64
*
1024
*
1024
;
缓存失效问题(大问题)
当数据表改动时,基于该数据表的任何缓存都会被删除。(表层面的管理,不是记录层面的管理,因此失效率较高)
注意事项
- 应用程序,不应该关心
query cache
的使用情况。可以尝试使用,但不能由query cache
决定业务逻辑,因为query cache
由DBA来管理。 - 缓存是以SQL语句为key存储的,因此即使SQL语句功能相同,但如果多了一个空格或者大小写有差异都会导致匹配不到缓存。
五、分区
一般情况下我们创建的表对应一组存储文件,使用MyISAM
存储引擎时是一个.MYI
和.MYD
文件,使用Innodb
存储引擎时是一个.ibd
和.frm
(表结构)文件。
当数据量较大时(一般千万条记录级别以上),MySQL的性能就会开始下降,这时我们就需要将数据分散到多组存储文件,保证其单个文件的执行效率。
创建表示创建分区:
查看data
目录:
服务端的表分区对于客户端是透明的,客户端还是照常插入数据,但服务端会按照分区算法分散存储数据。
MySQL提供的分区算法
分区依据的字段必须是主键的一部分,分区是为了快速定位数据,因此该字段的搜索频次较高应作为强检索字段,否则依照该字段分区毫无意义。
- hash(field):相同的输入得到相同的输出。输出的结果跟输入是否具有规律无关。==仅适用于整型字段==
- key(field):和
hash(field)
的性质一样,只不过key
是==处理字符串==的,比hash()
多了一步从字符串中计算出一个整型在做取模操作。 - range算法:是一种==条件分区==算法,按照数据大小范围分区(将数据使用某种条件,分散到不同的分区中)。
- list算法:也是一种条件分区,按照列表值分区(
in (值列表)
)。
分区的使用
当数据表中的数据量很大时,分区带来的效率提升才会显现出来。
只有检索字段为分区字段时,分区带来的效率提升才会比较明显。因此,==分区字段的选择很重要==,并且==业务逻辑要尽可能地根据分区字段做相应调整==(尽量使用分区字段作为查询条件)。
六、集群
主从复制
读写分离(基于主从)
负载均衡
- 轮询
- 加权轮询:按照处理能力来加权
- 负载分配:依据当前的空闲状态(但是测试每个节点的内存使用率、CPU利用率等,再做比较选出最闲的那个,效率太低)
高可用
在服务器架构时,为了保证服务器7x24不宕机在线状态,需要为每台单点服务器(由一台服务器提供服务的服务器,如写服务器、数据库中间件)提供冗余机。
对于写服务器来说,需要提供一台同样的写-冗余服务器,当写服务器健康时(写-冗余通过心跳检测),写-冗余作为一个从机的角色复制写服务器的内容与其做一个同步;当写服务器宕机时,写-冗余服务器便顶上来作为写服务器继续提供服务。对外界来说这个处理过程是透明的,即外界仅通过一个IP访问服务。
七、典型SQL
线上DDL
DDL(Database Definition Language)是指数据库表结构的定义(create table
)和维护(alter table
)的语言。在线上执行DDL,在低于MySQL5.6
版本时会导致全表被独占锁定,此时表处于维护、不可操作状态,这会导致该期间对该表的所有访问无法响应。但是在MySQL5.6
之后,支持Online DDL
,大大缩短了锁定时间。
优化技巧是采用的维护表结构的DDL(比如增加一列,或者增加一个索引),是==copy==策略。思路:创建一个满足新结构的新表,将旧表数据==逐条==导入(复制)到新表中,以保证==一次性锁定的内容少==(锁定的是正在导入的数据),同时旧表上可以执行其他任务。导入的过程中,将对旧表的所有操作以日志的形式记录下来,导入完毕后,将更新日志在新表上再执行一遍(确保一致性)。最后,新表替换旧表(在应用程序中完成,或者是数据库的rename,视图完成)。
但随着MySQL的升级,这个问题几乎淡化了。
数据库导入语句
在恢复数据时,可能会导入大量的数据。此时为了快速导入,需要掌握一些技巧:
导入时先禁用索引和约束:alter table table-name disable keys
待数据导入完成之后,再开启索引和约束,一次性创建索引:alter table table-name enable keys
- 数据库如果使用的引擎是
Innodb
,那么它==默认会给每条写指令加上事务==(这也会消耗一定的时间),因此建议先手动开启事务,再执行一定量的批量导入,最后手动提交事务。 - 如果批量导入的SQL指令格式相同只是数据不同,那么你应该先
prepare
==预编译==一下,这样也能节省很多重复编译的时间
limit offset,rows
尽量保证不要出现大的offset
,比如limit 10000,10
相当于对已查询出来的行数弃掉前10000
行后再取10
行,完全可以加一些条件过滤一下(完成筛选),而不应该使用limit
跳过已查询到的数据。这是一个==offset
做无用功==的问题。对应实际工程中,要避免出现大页码的情况,尽量引导用户做条件过滤。
select * 要少用
即尽量选择自己需要的字段select
,但这个影响不是很大,因为网络传输多了几十上百字节也没多少延时,并且现在流行的ORM框架都是用的select *
,只是我们在设计表的时候注意将大数据量的字段分离,比如商品详情可以单独抽离出一张商品详情表,这样在查看商品简略页面时的加载速度就不会有影响了。
order by rand()不要用
它的逻辑就是随机排序(为每条数据生成一个随机数,然后根据随机数大小进行排序)。如select * from student order by rand() limit 5
的执行效率就很低,因为它为表中的每条数据都生成随机数并进行排序,而我们只要前5条。
解决思路:在应用程序中,将随机的主键生成好,去数据库中利用主键检索。
单表和多表查询
多表查询:join
、子查询都是涉及到多表的查询。如果你使用explain
分析执行计划你会发现多表查询也是一个表一个表的处理,最后合并结果。因此可以说单表查询将计算压力放在了应用程序上,而多表查询将计算压力放在了数据库上。
现在有ORM框架帮我们解决了单表查询带来的对象映射问题(查询单表时,如果发现有外键自动再去查询关联表,是一个表一个表查的)。
count(*)
在MyISAM
存储引擎中,会自动记录表的行数,因此使用count(*)
能够快速返回。而Innodb
内部没有这样一个计数器,需要我们手动统计记录数量,解决思路就是单独使用一张表:
limit 1
如果可以确定仅仅检索一条,建议加上limit 1
,其实ORM框架帮我们做到了这一点(查询单条的操作都会自动加上limit 1
)。
八、慢查询日志
用于记录执行时间超过某个临界值的SQL日志,用于快速定位慢查询,为我们的优化做参考。
开启慢查询日志
配置项:slow_query_log
。
可以使用show variables like ‘slov_query_log’
查看是否开启,如果状态值为OFF
,可以使用set GLOBAL slow_query_log = on
来开启,它会在datadir
下产生一个xxx-slow.log
的文件
设置临界时间
配置项:long_query_time
查看:show VARIABLES like ‘long_query_time‘,单位秒
设置:set long_query_time=0.5
实操时应该从长时间设置到短的时间,即将最慢的SQL优化掉。
查看日志
一旦SQL超过了我们设置的临界时间就会被记录到xxx-slow.log
中。
九、profile信息
开启profile
开启后,所有的SQL执行的详细信息都会被自动记录下来
查看profile信息
通过Query_ID查看某条SQL所有详细步骤的时间
十、典型的服务器配置
max_connections
,最大客户端连接数
table_open_cache
,表文件句柄缓存(表数据是存储在磁盘上的,缓存磁盘文件的句柄方便打开文件读取数据)
key_buffer_size
,索引缓存大小(将从磁盘上读取的索引缓存到内存,可以设置大一些,有利于快速检索)
innodb_buffer_pool_size
,Innodb
存储引擎缓存池大小(对于Innodb
来说最重要的一个配置,如果所有的表用的都是Innodb
,那么甚至建议将该值设置到物理内存的80%,Innodb
的很多性能提升如索引都是依靠这个)
innodb_file_per_table
(innodb
中,表数据存放在.ibd
文件中,如果将该配置项设置为ON
,那么一个表对应一个ibd
文件,否则所有innodb
共享表空间)
原文地址:https://www.cnblogs.com/woxbwo/p/11525330.html