(基础)Mysql数据库性能提升01

背景

优化数据库性能

写在前面
主要内容的链接https://bbs.51cto.com/thread-10961-1.html
link
临时表相关https://www.cnblogs.com/mikeluwen/p/7651144.html
link

以下是部分内容的总结和注释

数据库设计

1.选取最适用的字段属性
2.应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
3.例如“省份”或者“性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多。
4.连接(JOIN.. 之所以更有效率一些,是因为 MySQL不需要在内存中创建临时表(子查询会创建)来完成这个逻辑上的需要两个步骤的查询工作。
5.使用联合(UNION)来代替手动创建的临时表(只需要用 UNION作为关键字把多个 SELECT 语句连接起来就可以了,要注意的是所有 SELECT 语句中的字段数目要想同)
SELECT Name, Phone FROM client
UNION
SELECT Name, BirthDate FROM author
UNION
SELECT Name, Supplier FROM product
6.锁定表LOCK TABLE inventory WRITE;防止DML;UNLOCK TABLES;
7.尽量不要对数据库中某个含有大量重复的值的字段建立索引。例如ENUM类型的字段(枚举)
8.注意数据更新也会更新索引(大数据量下,会很慢)
9.优化的查询语句,最好是在相同类型的字段间进行比较的操作。(防止类型转换用不到索引(这跟需要转换的类型兼容性有关),自动类型转换)
10.在建有索引的字段上尽量不要使用函数进行操作。
例如,在一个DATE类型的字段上使用YEAE()函数时,将会使索引不能发挥应有的作用。所以,下面的两个查询虽然返回的结果一样,但后者要比前者快得多。
SELECT * FROM order WHERE YEAR(OrderDate)<2001;
SELECT * FROM order WHERE OrderDate<"2001-01-01";
同样的情形也会发生在对数值型字段进行计算的时候:
SELECT * FROM inventory WHERE Amount/7<24;
SELECT * FROM inventory WHERE Amount<24*7;
以上也就是说要避免列的函数或是公式运算。
11.在搜索字符型字段时,我们有时会使用 LIKE 关键字和通配符,这种做法虽然简单,但却也是以牺牲系统性能为代价的。例如下面的查询将会比较表中的每一条记录。
SELECT * FROM books WHERE name like "MySQL%"
12.但是如果换用下面的查询,返回的结果一样,但速度就要快上很多:
SELECT * FROM books WHERE name>="MySQL"and name<"MySQM"
未完待续...
不要使用外键:(不推荐)
在这里,外键可以把customerinfo 表中的CustomerID映射到salesinfo表中CustomerID,任何一条没有合法CustomerID的记录都不会被更新或插入到salesinfo中。
CREATE TABLE customerinfo
(
CustomerID INT NOT NULL ,
PRIMARY KEY ( CustomerID )
) TYPE = INNODB;

CREATE TABLE salesinfo
(
SalesID INT NOT NULL,
CustomerID INT NOT NULL,
PRIMARY KEY(CustomerID, SalesID),
FOREIGN KEY (CustomerID) REFERENCES customerinfo
(CustomerID) ON DELETE CASCADE
) TYPE = INNODB;
注意例子中的参数“ON DELETE CASCADE”。该参数保证当 customerinfo 表中的一条客户记录被删除的时候,salesinfo 表中所有与该客户相关的记录也会被自动删除。

内存临时表

在某些情况下,服务器在处理语句时创建内部临时表,而用户无法直接控制临时表何时发生,完全有MySQL内部自行决定。

MySQL在以下几种情况会创建临时表:

1UNION查询(MySQL 5.7起,执行UNION ALL不再产生临时表,除非需要额外排序。);

2、用到TEMPTABLE算法或者是UNION查询中的视图;

3ORDER BYGROUP BY的子句不一样时;

4、表连接中,ORDER BY的列不是驱动表中的;

5DISTINCT查询并且加上ORDER BY时;

6SQL中用到SQL_SMALL_RESULT修饰符的查询;

7FROM中的子查询(派生表);

8、子查询或者semi-join时创建的表;

9、评估多表UPDATE语句;

10、评价GROUP_CONCAT()COUNT(DISTINCT) 表达式计算;

要确定语句是否需要临时表,请使用EXPLAIN并检查Extra列以查看是否显示Using temporary。但对于派生或实物化的临时表EXPLAIN不一定会显示Using temporary。

MySQL内部参数tmp_table_size表示内部的临时表的最大值,其实生效的是tmp_table_size和max_heap_table_size这两个值之间的最小的那个值。当创建的临时表超过这个值(或者max_heap_table_size)时,MySQL将会在磁盘上创建临时表。
show global variables like '%table_size%';
+---------------------+-----------+
| Variable_name       | Value     |
+---------------------+-----------+
| max_heap_table_size | 16777216  |
| tmp_table_size      | 134217728 |
+---------------------+-----------+
当服务器创建内部临时表(在内存或磁盘上)时,会增加Created_tmp_tables状态变量(SHOW PROCESSLIST可以看到)。如果服务器在磁盘上创建表(最初或通过转换内存中的表),它会增加Created_tmp_disk_tables状态变量。
show global status like '%Created_tmp%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Created_tmp_disk_tables | 35    |
| Created_tmp_files       | 6     |
| Created_tmp_tables      | 76    |
+-------------------------+-------+
通过检查Created_tmp_disk_tables和Created_tmp_tables这两个global状态值来判断在磁盘上创建临时表的次数来进行相应的调优。

磁盘临时表

1、表中存在BLOBTEXT列;

2、在SELECT列表中存在任何字符串列的最大长度大于512(二进制字符串的字节,非二进制字符的字符),如果被UNIONUNION ALL使用;

3SHOW COLUMNS FROM DB和DESCRIBE语句中使用BLOB作为用于某些列的类型;

服务器不使用符合特定条件的UNION语句的临时表。相反, 它只保留临时表创建执行结果列类型转换所需的数据结构。该表没有完全实例化, 并且没有写入或读取任何行,行直接发送到客户端。结果是减少了内存和磁盘要求, 并且在第一行发送到客户端之前的延迟较小, 因为服务器不需要等到执行最后一个查询块。解释和优化器跟踪输出反映了此执行策略: UNION结果查询块不存在, 因为该块对应于从临时表中读取的部分。

用于临时表的存储引擎

内部临时表可以在内存中保持并且由MEMORY存储引擎处理,或者由存储在磁盘上的InnoDB或MyISAM存储引擎处理。

如果内部临时表被创建为内存中的表,但是变得太大后,MySQL会自动将其转换为磁盘表。内存中临时表的最大大小是由tmp_table_size和max_heap_table_size 两个值中的较小值的决定。这与使用create table显式创建的内存引擎表不同,对于此类表,只有max_heap_table_size系统变量确定允许表增长的大小, 并且不能转换为磁盘上的格式。

从MySQL 5.7.5开始,新增一个系统选项internal_tmp_disk_storage_engine可定义磁盘临时表的引擎类型为InnoDB,而在这以前,只能使用MyISAM。
show global variables like '%internal_tmp_disk_storage_engine%';
+----------------------------------+--------+
| Variable_name                    | Value  |
+----------------------------------+--------+
| internal_tmp_disk_storage_engine | InnoDB |
+----------------------------------+--------+

而在MySQL 5.6.3以后新增的系统选项default_tmp_storage_engine是控制CREATE TEMPORARY TABLE创建的临时表的引擎类型,在以前默认是MEMORY,不要把这二者混淆了。
show global variables like '%default_tmp_storage_engine%';
+----------------------------+--------+
| Variable_name              | Value  |
+----------------------------+--------+
| default_tmp_storage_engine | InnoDB |
+----------------------------+--------+

如下,创建一个显式临时表(当前线程退出临时表就会删除,表也不会复制到从库,临时表文件会):
CREATE TEMPORARY TABLE tt(id int); 

在tmpdir参数控制存放磁盘路径的目录下有frm文件,如下:
show global variables like 'tmpdir';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| tmpdir        | /tmp  |
+---------------+-------+
ls /tmp
#sql1bd9c_a_0.frm
#sql1bd9c_a_0.ibd

但是这里只能看到frm文件,MySQL 5.7开始增加了临时表空间ibtmp1,数据都在ibtmp1空间中存储。

临时表存储格式

内存中临时表由MEMORY存储引擎管理,MEMORY存储引擎使用固定长度的行格式。并将VARCHARVARBINARY类型填充到最大列长度,实际上将它们存储为CHARBINARY类型。

在磁盘上的临时表由管理InnoDB或MyISAM存储引擎(取决于internal_tmp_disk_storage_engine设置)。两个引擎使用dynamic-width行格式存储临时表。列只需要尽可能多的存储空间,与使用固定长度行的磁盘表相比,减少了磁盘I/O和空间要求以及处理时间。

对于最初在内存中创建内部临时表的语句,然后将其转换为磁盘表,可能会通过跳过转换步骤并在磁盘上创建表来实现更好的性能。所述big_tables系统变量可以用来迫使内部临时表的磁盘存储。
show global variables like '%big_tables%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| big_tables    | OFF   |
+---------------+-------+
 

临时表空间使用

MySQL 5.7起,开始采用独立的临时表空间(和独立的undo表空间不是一回事哟),命名ibtmp1文件,初始化12M,且默认无上限。

选项innodb_temp_data_file_path可配置临时表空间相关参数。
show global variables like '%innodb_temp_data_file_path%';
+----------------------------+-----------------------+
| Variable_name              | Value                 |
+----------------------------+-----------------------+
| innodb_temp_data_file_path | ibtmp1:12M:autoextend |
+----------------------------+-----------------------+

临时表空间的几点说明:

1、临时表空间不像普通InnoDB表空间那样,不支持裸设备(raw device)。

2、临时表空间使用动态的表空间ID,因此每次重启时都会变化(每次重启时,都会重新初始化临时表空间文件)。

3、当选项设置错误或其他原因(权限不足等原因)无法创建临时表空间时,mysqld实例也无法启动。

4、临时表空间中存储这非压缩的InnoDB临时表,如果是压缩的InnoDB临时表,则需要单独存储在各自的表空间文件中,文件存放在 tmpdir(/tmp)目录下。

5、临时表元数据存储在INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO视图中。

建议

临时表使用的几点建议:

1、设置innodb_temp_data_file_path选项,设定文件最大上限(innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:200M),超过上限时,需要生成临时表的SQL无法被执行(一般这种SQL效率也比较低,可借此机会进行优化)。

2、检查INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO,找到最大的临时表对应的线程,kill之即可释放,但ibtmp1文件则不能释放(除非重启)。

3、择机重启实例,释放ibtmp1文件,和ibdata1不同,ibtmp1重启时会被重新初始化而ibdata1则不可以。

4、定期检查运行时长超过N秒(比如N=300)的SQL,考虑干掉,避免垃圾SQL长时间运行影响业务。

例子

临时表测试案例:

你可以往CREATE TEMPORARY TABLE里插入数据,ibtmp1会持续增长。另外你也可以产生一个union查询的慢SQL,MySQL 5.7起,执行UNION ALL不再产生临时表(除非需要额外排序)。

select * from sbtest1 union select * from sbtest2;

查看ibtmp1文件会持续增长。
du -sh /var/lib/mysql/ibtmp1
7.8G    /var/lib/mysql/ibtmp1

详细说明:
附:临时表测试案例
表DDL

CREATE TEMPORARY TABLE `tmp1` (
  `id` int(10) unsigned NOT NULL DEFAULT '0',
  `name` varchar(50) NOT NULL DEFAULT '',
  `aid` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `nid` int(11) unsigned GENERATED ALWAYS AS ((`id` + 1)) VIRTUAL NOT NULL,
  `nnid` int(11) unsigned GENERATED ALWAYS AS ((`id` + 1)) STORED NOT NULL,
  PRIMARY KEY (`aid`),
  KEY `name` (`name`),
  KEY `id` (`id`),
  KEY `nid` (`nid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
原表大小只有 120MB,从这个表直接 INSERTSELECT 导数据到tmp1表。

-rw-r-----  1 yejr  imysql   120M Apr 14 10:52 /data/mysql/test/sid.ibd
生成临时表(去掉虚拟列,临时表不支持虚拟列,然后写入数据),还更大了(我也不解,以后有机会再追查原因)。

-rw-r-----  1 yejr  imysql   140M Jun 25 09:55 /Users/yejinrong/mydata/ibtmp1
查看临时表元数据信息

yejr@imysql.com [test]>select * from 
 INFORMATION_SCHEMA.INNODB_TEMP_TABLE_INFO\G
*********************** 1. row ***********************
            TABLE_ID: 405
                NAME: #sql14032_300000005_3
              N_COLS: 6
               SPACE: 421
PER_TABLE_TABLESPACE: FALSE
       IS_COMPRESSED: FALSE
再删除索引,结果,又更大了

-rw-r-----  1 yejr  imysql   204M Jun 25 09:57 /data/mysql/ibtmp1
第二次测试删除索引后,变成了200M(因为第二次测试时,我设置了临时表最大200M)

innodb_temp_data_file_path = ibtmp1:12M:autoextend:max:200M
-rw-r-----  1 yejr  imysql   200M Jun 25 10:15 /data/mysql/ibtmp1
执行一个会产生临时表的慢SQL。
注:MySQL 5.7起,执行UNION ALL不再产生临时表(除非需要额外排序)。

yejr@imysql.com [test]>explain select * from tmp1 union 
  select id,name,aid from sid\G
*************************** 1. row ***************************
           id: 1
  select_type: PRIMARY
        table: tmp1
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 3986232
     filtered: 100.00
        Extra: NULL
*************************** 2. row ***************************
           id: 2
  select_type: UNION
        table: sid
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 802682
     filtered: 100.00
        Extra: NULL
*************************** 3. row ***************************
           id: NULL
  select_type: UNION RESULT
        table: <union1,2>
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: NULL
     filtered: NULL
        Extra: Using temporary
文件涨到588M还没结束,我直接给卡了

-rw-r-----  1 yejr  imysql   588M Jun 25 10:07 /data/mysql/ibtmp1
第二次测试时,设置了临时表空间文件最大200M,再执行会报错:

yejr@imysql.com [test]>select * from tmp1 union 
 select id,name,aid from sid;
ERROR 1114 (HY000): The table '/var/folders/bv/j4tjn6k54dj5jh1tl8yn6_y00000gn/T/#sql14032_5_8' is full

有时执行SQL请求时会产生临时表,极端情况下,可能导致临时表空间文件暴涨,有案例中最高涨到快300G,比以前遇到的ibdata1文件暴涨还要猛。所以对于临时表空间的使用也是一定要多注意的。

MySQL 5.7起,开始采用独立的临时表空间(和独立的undo表空间不是一回事哟),命名ibtmp1文件,初始化12M,且默认无上限。
选项 innodb_temp_data_file_path 可配置临时表空间相关参数。
innodb_temp_data_file_path = ibtmp1:12M:autoextend

建议给临时表的大小设置个上限

本文说明,主要技术内容来自互联网技术大佬的分享,还有一些自我的加工(仅仅起到注释说明的作用)。如有相关疑问,请留言,将确认之后,执行侵权必删

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值