浅谈mysql性能优化(配置、慢查询、索引、表分区)

MySql性能优化

  • Mysql配置
    • max_connections 最大连接进程数
    • table_open_cache 表高速缓存的大小
    • key_buffer_size 索引的缓冲区大小
    • innodb_buffer_pool_size     innodb缓冲池大小
  • 慢查询分析
    • mysqldumpslow的介绍
    • mysqldumpslow的使用
  • 索引优化
    • 索引类型
    • 索引算法
    • 索引形式
    • 注意事项
    • 分析索引
  • 表分区
    • 分区类型
    • 建表时直接创建分区(RANGE类型)
    • 在已有表上设置分区(重构表分区)
    • 在已有分区的表上添加一个分区
    • 删除一个分区
    • 拆分表分区
    • 合并表分区
    • 查询表分区的数据
    • 建表时直接创建分区并将分区数据映射到指定路径或磁盘(LIST类型)
    • HASH分区 和 KEY分区
  • 小技巧

Mysql配置

max_connections 最大连接进程数

MySQL的最大连接数,如果服务器的并发连接请求量比较大,建议调高此值,以增加并行连接数量,当然这建立在机器能支撑的情况下,因为如果连接数越多,介于MySQL会为每个连接提供连接缓冲区,就会开销越多的内存,所以要适当调整该值,不能盲目提高设值。
用一下命令查看当前链接数和最大连接数。

# 查看当前链接的线程数
show status like 'thread%';

# 查看最大使用链接数
show status like 'Max_used_connections';

table_open_cache 表高速缓存的大小

    MySQL每打开一个表,都会读入一些数据到table_open_cache缓存中,当MySQL在这个缓存中找不到相应信息时,才会去磁盘上读取。
    通过检查峰值时间的状态值Open_tables和Opened_tables,其中open_tables表示当前打开的table总和,即所有connection打开的table总数,opened_tables表示打开过的表的数量总和,只有show global status才能看到它的值。

#查看table_open_cache配置信息
SHOW VARIABLES LIKE 'table_open_cache%';

#查看当前打开的table总和 和 打开过的表的数量总和
SHOW GLOBAL  STATUS LIKE 'Open%tables';

比较适合的值:
Open_tables / Opened_tables >= 0.85
Open_tables / table_open_cache <= 0.95
    如果对此参数的把握不是很准,有个很保守的设置建议:把MySQL数据库放在生产环境中试运行一段时间,然后把参数的值调整得比Opened_tables的数值大一些,并且保证在比较高负载的极端条件下依然比Opened_tables略大。

key_buffer_size 索引的缓冲区大小

    增加它可得到更好处理的索引(对所有读和多重写),到你能负担得起那样多。对于内存在4GB左右的服务器该参数可设置为384M或512M。通过检查状态值Key_read_requests和Key_reads,可以知道key_buffer_size设置是否合理。比例key_reads/key_read_requests应该尽可能的低,至少是1:100,1:1000更好。注意:该参数值设置的过大反而会使服务器整体效率降低。

#查看请求索引数和读取索引数状态值
show status like 'Key_read%';

innodb_buffer_pool_size     innodb缓冲池大小

  • InnoDB使用一个缓冲池来保存索引和原始数据, 不像MyISAM。 这里你设置越大,你在存取表里面数据时所需要的磁盘I/O越少
  • 在一个独立使用的数据库服务器上,你可以设置这个变量到服务器物理内存大小的70-80%
  • 不要设置过大,否则,由于物理内存的竞争可能导致操作系统的换页颠簸
  • 注意在32位系统上你每个进程可能被限制在 2-3.5G 用户层面内存限制, 所以不要设置的太高

慢查询分析

mysqldumpslow的介绍

参数说明:
-s,是order的顺序
   al 平均锁定时间
   ar 平均返回记录时间
   at 平均查询时间(默认)
   c 计数
   l 锁定时间
   r 返回记录
   t 查询时间
-t,是top n的意思,即为返回前面多少条的数据
-g,后边可以写一个正则匹配模式,大小写不敏感

注意:
Windows下使用mysqldumpslow需要先安装Perl。
然后使用 perl D:\mysql\bin\mysqldumpslow.pl 后接参数

mysqldumpslow的使用

参考一下示例

#返回查询最慢的20个SQL,并写入到slow_top20.txt文件
mysqldumpslow -t 20 /var/lib/mysql/slowquery.log > slow_top20.txt

#返回记录集最多的20个SQL
mysqldumpslow -s r -t 20 /var/lib/mysql/slowquery.log

#得到访问次数最多的20个SQL
mysqldumpslow -s c -t 20 /var/lib/mysql/slowquery.log

#得到按照时间排序的前20条里面含有左连接的查询语句
mysqldumpslow -s t -t 20 -g “left join” /var/lib/mysql/slowquery.log

#得到按照平均锁表时间排序的前20个SQL
mysqldumpslow -s al -t 20 /var/lib/mysql/slowquery.log

索引优化

索引类型

  • PRIMARY(主键索引)
  • UNIQUE (唯一索引)
  • KEY(普通索引)
  • FULLTEXT(全文索引) Innodb引擎在5.6版本才加上,且不支持中文,5.7版本以后支持中文,但查询效率远不如Sphinx和Lusene或ElasticSearch。

索引算法

  • HASH (key->value的形式存储,不支持区间查询和排序,适用于用户名/邮箱/手机号/身份证号等)
  • BTREE(树形存储,支持区间查询和排序,较为常用)
  • RTREE(多维空间搜索算法,本文不做探讨)

索引形式

  • 单列索引
  • 多列(联合索引/复合索引)

注意事项

  • 联合索引:(a,b,c)相当于(a)、(a,b)、(a,b,c)
  • MySql5.6的虽然支持全文索引,但需要使用MATCH()…AGAINST语法,且不支持中文,建议用Elasticsearch。
  • != 、<> 、not in 、 is null 、 is not null 通常不会走索引
  • 单独的>、>= 或 <、<= 可能会导致不走索引
  • Like ‘%abc%’不走索引,’abc%’走索引
  • 不要过度建立索引
  • 不要建立重复的索引

分析索引

查看是否执行索引: EXPLAIN或者DESC后接SQL语句( SQL语句不会真正被执行)

EXPLAIN SELECT * FROM est_client WHERE cle_phone='123';

索引分析
参数详解
±—±------------±------±------±------------------±--------±--------±------±-----
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
±—±------------±------±------±------------------±--------±--------±------±-----
下面对各个属性进行了解:

1、id:这是SELECT的查询序列号

2、select_type:select_type就是select的类型,可以有以下几种:

SIMPLE:简单SELECT(不使用UNION或子查询等)

PRIMARY:最外面的SELECT

UNION:UNION中的第二个或后面的SELECT语句

DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询

UNION RESULT:UNION的结果。

SUBQUERY:子查询中的第一个SELECT

DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询

DERIVED:导出表的SELECT(FROM子句的子查询)

3、table:显示这一行的数据是关于哪张表的

4、type:这列最重要,显示了连接使用了哪种类别,有无使用索引,是使用Explain命令分析性能瓶颈的关键项之一。
结果值从好到坏依次是:
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL
一般来说,得保证查询至少达到range级别,最好能达到ref,否则就可能会出现性能问题。

5、possible_keys:列指出MySQL能使用哪个索引在该表中找到行

6、key:显示MySQL实际决定使用的键(索引)。如果没有选择索引,键是NULL

7、key_len:显示MySQL决定使用的键长度。如果键是NULL,则长度为NULL。使用的索引的长度。在不损失精确性的情况下,长度越短越好

8、ref:显示使用哪个列或常数与key一起从表中选择行。

9、rows:显示MySQL认为它执行查询时必须检查的行数。

10、Extra:包含MySQL解决查询的详细信息,也是关键参考项之一。

表分区

分区类型

RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区。
LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择。
HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL 中有效的、产生非负整数值的任何表达式。
KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值。

注意:用于创建表分区的字段必须设置为主键,可以在原主键上新增一个主键做为联合主键

建表时直接创建分区(RANGE类型)

CREATE TABLE `news` (
`id` INT NOT NULL AUTO_INCREMENT,
`title` VARCHAR(50) NULL,
`content` TEXT NULL,
`create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`, `create_time`)
)
COLLATE='utf8_general_ci'
ENGINE=INNODB

PARTITION BY RANGE (TO_DAYS(create_time))
(
PARTITION p_2016 VALUES LESS THAN (TO_DAYS('2017-01-01')),
PARTITION p_2017 VALUES LESS THAN (TO_DAYS('2018-01-01')),
PARTITION p_2018 VALUES LESS THAN (TO_DAYS('2019-01-01'))
);

在已有表上设置分区(重构表分区)

ALTER TABLE `news`  PARTITION BY RANGE (TO_DAYS(create_time))
(
PARTITION p_2016_2017 VALUES LESS THAN (TO_DAYS('2018-01-01')),
PARTITION p_2018 VALUES LESS THAN (TO_DAYS('2019-01-01'))
);

在已有分区的表上添加一个分区

ALTER TABLE `news`  ADD PARTITION (
PARTITION p_2019 VALUES LESS THAN (TO_DAYS('2020-01-01'))
);

删除一个分区

ALTER TABLE `news` DROP PARTITION p_2019;

谨慎使用,删除表分区会导致表分区内的数据也被删除,当然也可以巧妙的用做快速删除表分区内全部数据的一种方法。

拆分表分区

ALTER TABLE news REORGANIZE PARTITION p_2018 INTO (
PARTITION p_2018_Q1 VALUES LESS THAN (TO_DAYS('2018-04-01')),
PARTITION p_2018_Q2 VALUES LESS THAN (TO_DAYS('2018-07-01')),
PARTITION p_2018_Q3 VALUES LESS THAN (TO_DAYS('2018-10-01')),
PARTITION p_2018_Q4 VALUES LESS THAN (TO_DAYS('2019-01-01'))
);

合并表分区

ALTER TABLE news REORGANIZE PARTITION
p_2018_Q1,p_2018_Q2,p_2018_Q3,p_2018_Q4 INTO
(
PARTITION p_2018_half1 VALUES LESS THAN (TO_DAYS('2018-07-01')),
PARTITION p_2018_half2 VALUES LESS THAN (TO_DAYS('2019-01-01'))
);

查询表分区的数据

SELECT * FROM news PARTITION(p_2017);
SELECT * FROM news PARTITION(p_2018_half1,p_2018_half2);

建表时直接创建分区并将分区数据映射到指定路径或磁盘(LIST类型)

CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`group_id` INT(11) NOT NULL,
`user_name` VARCHAR(50) NOT NULL,
PRIMARY KEY (`id`, `group_id`)
)
COLLATE='utf8_general_ci'
ENGINE=INNODB

PARTITION BY LIST (group_id)
(
PARTITION p_group_a VALUES IN (1,3,8)
INDEX DIRECTORY = '/var/data/test_db/user'
DATA DIRECTORY = ‘/var/data/test_db/user ',
PARTITION p_group_b VALUES IN (2,4,5),
INDEX DIRECTORY = ‘/var/data/test_db/user '
DATA DIRECTORY = ‘/var/data/test_db/user ',
PARTITION p_group_c VALUES IN (6,7)
INDEX DIRECTORY = ‘/var/data/test_db/user '
DATA DIRECTORY = ‘/var/data/test_db/user ',
 );

此方法可以解决一些磁盘空间分配和磁盘IO性能的问题。

HASH分区 和 KEY分区

本文暂不做介绍

小技巧

#查看所有进程,查看Command、Time、State和Info,可以分析死锁情况
show processlist; 

#结束进程,发现死锁的进程,可以用命令直接杀掉
kill 12041;

#查询是否锁表
show OPEN TABLES where In_use>0;

#正在运行的,包括在锁的和待锁的
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX;

#查看正在被锁住的
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

#查看等待锁的
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

你可能感兴趣的:(浅谈mysql性能优化(配置、慢查询、索引、表分区))