上节课没讲完的一个案例

优化 limit 分页

在很多应用场景中我们需要将数据进行分页,一般会使用limit加上偏移量的方法实现,同时加上合适的orderby 的子句,如果这种方式有索引的帮助,效率通常不错,否则的化需要进行大量的文件排序操作,还有一种情况,当偏移量非常大的时候,前面的大部分数据都会被抛弃,这样的代价太高。
要优化这种查询的话,要么是在页面中限制分页的数量,要么优化大偏移量的性能

利用子查询,优化一个 limit 分页

例如,原语句:

select * from rental limit 1000000,5;

利用子查询,优化后的语句:(是因为走了索引)

select * from rental a join(select rental_id from rental limit 1000000,5) b on a.rental_id=b.rental_id;

优化union查询

mysql总是通过创建并填充临时表的方式来执行union查询,因此很多优化策略在union查询中都没法很好的使用。经常需要手工的将where、limit、order by等子句下推到各个子查询中,以便优化器可以充分利用这些条件进行优化

行转列可以使用unoin快速实现:

推荐使用用户自定义变量

用户自定义变量是一个容易被遗忘的mysql特性,但是如果能够用好,在某些场景下可以写出非常高效的查询语句,在查询中混合使用过程化和关系话逻辑的时候,自定义变量会非常有用。
用户自定义变量是一个用来存储内容的临时容器,在连接mysql的整个过程中都存在。

自定义变量的使用案例

分区表

对于用户而言,分区表是一个独立的逻辑表,但是底层是由多个物理子表组成。分区表对于用户而言是一个完全封装底层实现的黑盒子,对用户而言是透明的,从文件系统中可以看到多个使用#分隔命名的表文件。
mysql在创建表时使用partition by子句定义每个分区存放的数据,在执行查询的时候,优化器会根据分区定义过滤那些没有我们需要数据的分区,这样查询就无须扫描所有分区。
分区的主要目的是将数据安好一个较粗的力度分在不同的表中,这样可以将相关的数据存放在一起。

分区表的应用场景

分区表的限制

分区表的底层原理

​ 分区表由多个相关的底层表实现,这个底层表也是由句柄对象标识,我们可以直接访问各个分区。存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引知识在各个底层表上各自加上一个完全相同的索引。从存储引擎的角度来看,底层表和普通表没有任何不同,存储引擎也无须知道这是一个普通表还是一个分区表的一部分。

​ 分区表的操作按照以下的操作逻辑进行:

select查询

​ 当查询一个分区表的时候,分区层先打开并锁住所有的底层表,优化器先判断是否可以过滤部分分区,然后再调用对应的存储引擎接口访问各个分区的数据

insert操作

​ 当写入一条记录的时候,分区层先打开并锁住所有的底层表,然后确定哪个分区接受这条记录,再将记录写入对应底层表

delete操作

​ 当删除一条记录时,分区层先打开并锁住所有的底层表,然后确定数据对应的分区,最后对相应底层表进行删除操作

update操作

​ 当更新一条记录时,分区层先打开并锁住所有的底层表,mysql先确定需要更新的记录再哪个分区,然后取出数据并更新,再判断更新后的数据应该再哪个分区,最后对底层表进行写入操作,并对源数据所在的底层表进行删除操作

有些操作是支持过滤的,例如,当删除一条记录时,MySQL需要先找到这条记录,如果where条件恰好和分区表达式匹配,就可以将所有不包含这条记录的分区都过滤掉,这对update同样有效。如果是insert操作,则本身就是只命中一个分区,其他分区都会被过滤掉。mysql先确定这条记录属于哪个分区,再将记录写入对应得曾分区表,无须对任何其他分区进行操作

​ 虽然每个操作都会“先打开并锁住所有的底层表”,但这并不是说分区表在处理过程中是锁住全表的,如果存储引擎能够自己实现行级锁,例如innodb,则会在分区层释放对应表锁

分区表的类型

这个可以直接到MySQL官网上去看,解释和例子都给的比较清晰。

如何使用分区表

如果需要从非常大的表中查询出某一段时间的记录,而这张表中包含很多年的历史数据,数据是按照时间排序的,此时应该如何查询数据呢?
因为数据量巨大,肯定不能在每次查询的时候都扫描全表。考虑到索引在空间和维护上的消耗,也不希望使用索引,即使使用索引,会发现会产生大量的碎片,还会产生大量的随机IO,但是当数据量超大的时候,索引也就无法起作用了,此时可以考虑使用分区来进行解决

全量扫描数据,不要任何索引
使用简单的分区方式存放表,不要任何索引,根据分区规则大致定位需要的数据为止,通过使用where条件将需要的数据限制在少数分区中,这种策略适用于以正常的方式访问大量数据

索引数据,并分离热点
如果数据有明显的热点,而且除了这部分数据,其他数据很少被访问到,那么可以将这部分热点数据单独放在一个分区中,让这个分区的数据能够有机会都缓存在内存中,这样查询就可以只访问一个很小的分区表,能够使用索引,也能够有效的使用缓存

在使用分区表的时候需要注意的问题

范围分区示例

范围分区表的分区方式是:每个分区都包含行数据且分区的表达式在给定的范围内,分区的范围应该是连续的且不能重叠,可以使用values less than运算符来定义。

​ 1、创建普通的表

CREATE TABLE employees (id INT NOT NULL,fname VARCHAR(30),lname VARCHAR(30),hired DATE NOT NULL DEFAULT '1970-01-01',separated DATE NOT NULL DEFAULT '9999-12-31',job_code INT NOT NULL,store_id INT NOT NULL
);

​ 2、创建带分区的表,下面建表的语句是按照 store_id 来进行分区的,指定了4个分区

CREATE TABLE employees (id INT NOT NULL,fname VARCHAR(30),lname VARCHAR(30),hired DATE NOT NULL DEFAULT '1970-01-01',separated DATE NOT NULL DEFAULT '9999-12-31',job_code INT NOT NULL,store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (PARTITION p0 VALUES LESS THAN (6),PARTITION p1 VALUES LESS THAN (11),PARTITION p2 VALUES LESS THAN (16),PARTITION p3 VALUES LESS THAN (21)
);
--在当前的建表语句中可以看到,store_id的值在1-5的在p0分区,6-10的在p1分区,11-15的在p3分区,16-20的在p4分区,但是如果插入超过20的值就会报错,因为mysql不知道将数据放在哪个分区

3、可以使用less than maxvalue来避免此种情况

CREATE TABLE employees (id INT NOT NULL,fname VARCHAR(30),lname VARCHAR(30),hired DATE NOT NULL DEFAULT '1970-01-01',separated DATE NOT NULL DEFAULT '9999-12-31',job_code INT NOT NULL,store_id INT NOT NULL
)
PARTITION BY RANGE (store_id) (PARTITION p0 VALUES LESS THAN (6),PARTITION p1 VALUES LESS THAN (11),PARTITION p2 VALUES LESS THAN (16),PARTITION p3 VALUES LESS THAN MAXVALUE
);
--maxvalue表示始终大于等于最大可能整数值的整数值

4、可以使用相同的方式根据员工的职务代码对表进行分区

CREATE TABLE employees (id INT NOT NULL,fname VARCHAR(30),lname VARCHAR(30),hired DATE NOT NULL DEFAULT '1970-01-01',separated DATE NOT NULL DEFAULT '9999-12-31',job_code INT NOT NULL,store_id INT NOT NULL
)
PARTITION BY RANGE (job_code) (PARTITION p0 VALUES LESS THAN (100),PARTITION p1 VALUES LESS THAN (1000),PARTITION p2 VALUES LESS THAN (10000)
);

5、可以使用date类型进行分区:如虚妄根据每个员工离开公司的年份进行划分,如year(separated)

CREATE TABLE employees (id INT NOT NULL,fname VARCHAR(30),lname VARCHAR(30),hired DATE NOT NULL DEFAULT '1970-01-01',separated DATE NOT NULL DEFAULT '9999-12-31',job_code INT,store_id INT
)
PARTITION BY RANGE ( YEAR(separated) ) (PARTITION p0 VALUES LESS THAN (1991),PARTITION p1 VALUES LESS THAN (1996),PARTITION p2 VALUES LESS THAN (2001),PARTITION p3 VALUES LESS THAN MAXVALUE
);

​ 6、可以使用函数根据range的值来对表进行分区,如timestampunix_timestamp()

CREATE TABLE quarterly_report_status (report_id INT NOT NULL,report_status VARCHAR(20) NOT NULL,report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),PARTITION p9 VALUES LESS THAN (MAXVALUE)
);
--timestamp不允许使用任何其他涉及值的表达式

基于时间间隔的分区方案,在mysql5.7中,可以基于范围或事件间隔实现分区方案,有两种选择

1、基于范围的分区,对于分区表达式,可以使用操作函数基于date、time、或者datatime列来返回一个整数值

CREATE TABLE members (firstname VARCHAR(25) NOT NULL,lastname VARCHAR(25) NOT NULL,username VARCHAR(16) NOT NULL,email VARCHAR(35),joined DATE NOT NULL
)
PARTITION BY RANGE( YEAR(joined) ) (PARTITION p0 VALUES LESS THAN (1960),PARTITION p1 VALUES LESS THAN (1970),PARTITION p2 VALUES LESS THAN (1980),PARTITION p3 VALUES LESS THAN (1990),PARTITION p4 VALUES LESS THAN MAXVALUE
);CREATE TABLE quarterly_report_status (report_id INT NOT NULL,report_status VARCHAR(20) NOT NULL,report_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
PARTITION BY RANGE ( UNIX_TIMESTAMP(report_updated) ) (PARTITION p0 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-01-01 00:00:00') ),PARTITION p1 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-04-01 00:00:00') ),PARTITION p2 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-07-01 00:00:00') ),PARTITION p3 VALUES LESS THAN ( UNIX_TIMESTAMP('2008-10-01 00:00:00') ),PARTITION p4 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-01-01 00:00:00') ),PARTITION p5 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-04-01 00:00:00') ),PARTITION p6 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-07-01 00:00:00') ),PARTITION p7 VALUES LESS THAN ( UNIX_TIMESTAMP('2009-10-01 00:00:00') ),PARTITION p8 VALUES LESS THAN ( UNIX_TIMESTAMP('2010-01-01 00:00:00') ),PARTITION p9 VALUES LESS THAN (MAXVALUE)
);

2、基于范围列的分区,使用date或者datatime列作为分区列

CREATE TABLE members (firstname VARCHAR(25) NOT NULL,lastname VARCHAR(25) NOT NULL,username VARCHAR(16) NOT NULL,email VARCHAR(35),joined DATE NOT NULL
)
PARTITION BY RANGE COLUMNS(joined) (PARTITION p0 VALUES LESS THAN ('1960-01-01'),PARTITION p1 VALUES LESS THAN ('1970-01-01'),PARTITION p2 VALUES LESS THAN ('1980-01-01'),PARTITION p3 VALUES LESS THAN ('1990-01-01'),PARTITION p4 VALUES LESS THAN MAXVALUE
);
真实案例:
#不分区的表
CREATE TABLE no_part_tab
(id INT DEFAULT NULL,
remark VARCHAR(50) DEFAULT NULL,
d_date DATE DEFAULT NULL
)ENGINE=MYISAM;
#分区的表
CREATE TABLE part_tab
(id INT DEFAULT NULL,
remark VARCHAR(50) DEFAULT NULL,
d_date DATE DEFAULT NULL
)ENGINE=MYISAM
PARTITION BY RANGE(YEAR(d_date))(
PARTITION p0 VALUES LESS THAN(1995),
PARTITION p1 VALUES LESS THAN(1996),
PARTITION p2 VALUES LESS THAN(1997),
PARTITION p3 VALUES LESS THAN(1998),
PARTITION p4 VALUES LESS THAN(1999),
PARTITION p5 VALUES LESS THAN(2000),
PARTITION p6 VALUES LESS THAN(2001),
PARTITION p7 VALUES LESS THAN(2002),
PARTITION p8 VALUES LESS THAN(2003),
PARTITION p9 VALUES LESS THAN(2004),
PARTITION p10 VALUES LESS THAN maxvalue);
#插入未分区表记录
DROP PROCEDURE IF EXISTS no_load_part;DELIMITER//
CREATE PROCEDURE no_load_part()
BEGINDECLARE i INT;SET i =1;WHILE i<80001DOINSERT INTO no_part_tab VALUES(i,'no',ADDDATE('1995-01-01',(RAND(i)*36520) MOD 3652));SET i=i+1;END WHILE;
END//
DELIMITER ;CALL no_load_part;
#插入分区表记录
DROP PROCEDURE IF EXISTS load_part;DELIMITER&&
CREATE PROCEDURE load_part()
BEGINDECLARE i INT;SET i=1;WHILE i<80001DOINSERT INTO part_tab VALUES(i,'partition',ADDDATE('1995-01-01',(RAND(i)*36520) MOD 3652));SET i=i+1;END WHILE;
END&&
DELIMITER ;CALL load_part;

其他

有个疑问,文件描述符 fd 的个数受限于这个 open files 吗?如果是这样的话,每一个 socket 都要开启一个 fd 对吧,那在使用网络 IO 的时候,开启的 socket 的总个数是不是也要受到这个 open files 的限制?

实战:查看分区文件的存储方式


可以看到产生的分区文件:

MySQL调优(六):分区设计,分区优化案例相关推荐

  1. mybatis与mysql调优_MySQL + mybatis的SQL优化方案

    sql优化方案: 1.添加索引,在条件参数,关联参数上建立参数, 2.字段优化,需要什么字段查什么字段 3.模糊查询尽量使用: select * from tableName a where a.na ...

  2. 【JVM 学习笔记 05】:JVM性能调优工具的使用和优化案例

    [JVM 学习笔记 05]:JVM性能调优工具的使用 1. 使用 jstat(命令行工具) 查看线上系统的JVM运行状况 1.1 常用命令 1.2 使用技巧 1.2.1 随着系统运行,每秒钟会在年轻代 ...

  3. MySQL性能调优与架构设计——第11章 常用存储引擎优化

    第11章 常用存储引擎优化 前言: MySQL 提供的非常丰富的存储引擎种类供大家选择,有多种选择固然是好事,但是需要我们理解掌握的知识也会增加很多.每一种存储引擎都有各自的特长,也都存在一定的短处. ...

  4. MySQL 调优/优化的 101 个建议!

    转载自 MySQL 调优/优化的 101 个建议! MySQL是一个强大的开源数据库.随着MySQL上的应用越来越多,MySQL逐渐遇到了瓶颈.这里提供 101 条优化 MySQL 的建议.有些技巧适 ...

  5. mysql性能调优 高可用_MySQL性能调优与架构设计——第 17 章 高可用设计之思路及方案...

    第 17 章 高可用设计之思路及方案 前言: 数据库系统是一个应用系统的核心部分,要想系统整体可用性得到保证,数据库系统就不能出现任何问题.对于一个企业级的系统来说,数据库系统的可用性尤为重要.数据库 ...

  6. 性能优化专题 - MySql 性能优化 - 04 - MySql调优

    目录导航 前言 Undo-log与Redo-log 案例 当前读.快照读 Redo Log的落盘配置 MySQL配置优化 MySQL服务器参数类型 快速定位MySql配置文件 MySQL内存参数配置 ...

  7. 转】MYSQL性能调优与架构设计之select count(*)的思考

    原博文出自于: http://blog.fens.me/category/%E6%95%B0%E6%8D%AE%E5%BA%93/page/5/ 感谢! Posted: Feb 7, 2013 Tag ...

  8. MySQL性能调优与架构设计——第4章 MySQL安全管理

    第4章 MySQL安全管理 前言 对于任何一个企业来说,其数据库系统中所保存数据的安全性无疑是非常重要的,尤其是公司的有些商业数据,可能数据就是公司的根本,失去了数据的安全性,可能就是失去了公司的一切 ...

  9. MySQL性能调优与架构设计——第5章 备份与恢复

    第5章 备份与恢复 前言 数据库的备份与恢复一直都是 DBA 工作中最为重要的部分之一,也是基本工作之一.任何正式环境的数据库都必须有完整的备份计划和恢复测试,本章内容将主要介绍 MySQL数据库的备 ...

  10. MySQL执行计划(MySQL调优的重要利器)

    文章目录 看完本篇文章你能学到什么? 一.MySQL执行计划 1.1 id字段 1.2 select_type 字段 1.3 table 字段 1.4 partitions 字段 1.5 type字段 ...

最新文章

  1. 从强制卸载Office到强制安装WPS
  2. Centos 7下搭建WordPress
  3. 1.10 编程基础之简单排序 06 整数奇偶排序 python
  4. 信安精品课:第4章网络安全体系与网络安全模型精讲笔记
  5. 【转】 opengl编程学习笔记(三)(2D绘图)
  6. 『ORACLE』Oracle GoldenGate搭建(11g)
  7. 寒冬之下,被cai的那些人到底去哪了?
  8. Ubuntu下查看cuda版本的两种方法
  9. ftp服务器复制文件命令,FTP服务器的Copy命令的使用
  10. 深度学习基础之三分钟轻松搞明白tensor到底是个啥!看不懂的话我倒立洗头~~
  11. php测试页面打开速度,在JS中如何测试目标网站的打开响应速度
  12. Automatically manage signing
  13. MP模型、单层感知器、多层感知器的理解
  14. 远程网络监视(rmon)与简单网络管理协议(snmp)之间是什么关系
  15. 小米2s解决充电过热的方法
  16. w7如何关闭计算机防火墙,win7怎么关闭防火墙 win7自带防火墙启用或关闭方法
  17. 数字电路基础知识——格雷码和二进制码的转换的算法和Verilog实现
  18. 在线文本编辑器(一)—FreeTextBox
  19. 郭天祥ARM9架构嵌入式linux培训视频教程
  20. 数加加众包深耕AI第8年,苹果加码人工智能和机器学习

热门文章

  1. HDU - 5451 Best Solver(循环群+矩阵快速幂)
  2. 多线程读取同一个文件_前端进阶:多线程Web Workers的工作原理及使用场景
  3. python安装后无法运行任何软件_为啥我按照python安装教程,总说无法启动此程序,因为计算机中丢失?...
  4. input()与raw_input()
  5. [luogu4027] [NOI2007]货币兑换
  6. Python_文件_日记管理系统
  7. 22.循环控制.rs
  8. nginx系列之四:web服务器
  9. Lua 调试(Debug)
  10. 用特征码秒杀各程序语言按钮事件