mysql 单表百万级记录查询分页优化

insert select (制造百万条记录)

在开始百万级数据的查询之前,自己先动手制造百万级的记录来供我们使用,使用的方法是insert select方法

INSERT 一般用来给表插入一个指定列值的行。但是,INSERT 还存在另一种形式,可以利用它将一条SELECT 语句的结果插入表中。这就是所谓的INSERT SELECT, 顾名思义,它是有一条INSERT语句和一条SELECT语句组成的。

现在,有一个warning_reparied表,有2447条记录,如下:

mysql> select count(*) from warning_repaired;
+----------+
| count(*) |
+----------+
|     2447 |
+----------+
1 row in set (0.00 sec)

mysql>

使用这个warning_repaired表创建出一个百万级数量的表:

首先,创建一个新表warning_repaired1,

mysql>  CREATE TABLE `warning_repaired1` (
    ->   `id` int(11) NOT NULL AUTO_INCREMENT,
    ->   `device_moid` varchar(36) NOT NULL,
    ->   `device_name` varchar(128) DEFAULT NULL,
    ->   `device_type` varchar(36) DEFAULT NULL,
    ->   `device_ip` varchar(128) DEFAULT NULL,
    ->   `warning_type` enum(‘0‘,‘1‘,‘2‘) NOT NULL,
    ->   `domain_moid` varchar(36) NOT NULL,
    ->   `domain_name` varchar(128) DEFAULT NULL,
    ->   `code` smallint(6) NOT NULL,
    ->   `level` varchar(16) NOT NULL,
    ->   `description` varchar(128) DEFAULT NULL,
    ->   `start_time` datetime NOT NULL,
    ->   `resolve_time` datetime NOT NULL,
    ->   PRIMARY KEY (`id`),
    ->   UNIQUE KEY `id` (`id`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=4895 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.39 sec)

mysql> select count(*) from warning_repaired1; 
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from warning_repaired;
+----------+
| count(*) |
+----------+
|     2447 |
+----------+
1 row in set (0.00 sec)

mysql>

其次,使用insert select语句插入把warning_repaired中的记录插入到warning_repaired1表中:

mysql>  insert into warning_repaired1(device_moid, device_name, device_type, device_ip, warning_type,
 domain_moid, domain_name, code, level, description, start_time, resolve_time) 
select device_moid, device_name, device_type, device_ip, warning_type, domain_moid, 
domain_name, code, level, description, start_time, resolve_time from warning_repaired;
Query OK, 2447 rows affected (1.07 sec)
Records: 2447  Duplicates: 0  Warnings: 0

mysql> select count(*) from warning_repaired;
+----------+
| count(*) |
+----------+
|     2447 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from warning_repaired1;
+----------+
| count(*) |
+----------+
|     2447 |
+----------+
1 row in set (0.00 sec)

插入成功后,把INSERT SELECT语句用的查询表也改为warning_repaired1,如下:

 insert into warning_repaired1(device_moid, device_name, device_type, device_ip, warning_type, 
 domain_moid, domain_name, code, level, description, start_time, resolve_time) 
 select device_moid, device_name, device_type, device_ip, warning_type, domain_moid, 
 domain_name, code, level, description, start_time, resolve_time from warning_repaired1;

这样多运行几次(记录指数级的增长)就可以很快的制造出百万条的记录了。




最常见MYSQL 最基本的分页方式limit

mysql> select count(*) from warning_repaired;
+----------+
| count(*) |
+----------+
|     2447 |
+----------+
1 row in set (0.00 sec)

mysql> select count(*) from warning_repaired5;
+----------+
| count(*) |
+----------+
|  5990256 |
+----------+
1 row in set (10.11 sec)

mysql> select code,level,description from warning_repaired5 limit 1000,2;
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
2 rows in set (0.00 sec)

mysql> select code,level,description from warning_repaired5 limit 10000,2;
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
2 rows in set (0.05 sec)

mysql> select code,level,description from warning_repaired5 limit 100000,2;
+------+----------+------------------------------------------------------+
| code | level    | description                                          |
+------+----------+------------------------------------------------------+
| 2003 | critical | 服务器内存5分钟内平均使用率超过阈值                  |
| 2019 | critical | 网卡的吞吐量超阈值                                   |
+------+----------+------------------------------------------------------+
2 rows in set (0.26 sec)

mysql> select code,level,description from warning_repaired5 limit 1000000,2;
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
2 rows in set (1.56 sec)

mysql> select code,level,description from warning_repaired5 limit 5000000,2; 
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
2 rows in set (7.15 sec)

mysql>

在不超过100万条记录时,可以看出花费的时间还是比较少。所以在中小数量的情况下,这样的SQL足够用了,唯一需要注意的问题就是确保使用了索引。但是随着数据量的增加,页数会越来越多,在数据慢慢增长的过程中,可能出现limit 5000000,2这样的情况,limit 5000000,2的意思是扫描满足条件的l5000002行,扔掉前面的5000000行,返回最后的2行,问题就在这里,如果limit 5000000,2,需要扫描5000002行,在一个高并发的应用里,每次查询需要扫描超过500w行,性能肯定大打折扣。

这种方式有几个不足: 较大的偏移(OFFSET)会增加结果集,小比例的低效分页足够产生磁盘I/O瓶颈,需要扫描的行多。

简单的解决办法: 不显示记录总数,没用户在乎这个数字;不让用户访问页数比较大的记录,重定向他们;避免count(*),不显示总数,让用户通过"下一页"来翻页,缓存总数;单独统计总数,在插入和删除时递增/递减。

mysql> select code,level,description from warning_repaired5 limit 5000000,2;                  
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
2 rows in set (2.98 sec)

mysql> select code,level,description from warning_repaired5 order by id desc limit 5000000,2;                   
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
2 rows in set (8.04 sec)

从上面可以看出再加了order by id desc后,花费的时间又增长了。



第二种就是分表,计算HASH值,这儿不做介绍。



第三种:偏移

mysql> select code,level,description from warning_repaired5 order by id desc limit 5000000,20;                                                                        
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
……
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
20 rows in set (4.77 sec)

mysql> select code,level,description from warning_repaired5 where id <=( select id from warning_repaired5 order by id desc limit 5000000,1) order by id desc limit 20;
+------+----------+----------------+
| code | level    | description    |
+------+----------+----------------+
| 1006 | critical | 注册GK失败     |
| 1006 | critical | 注册GK失败     |
……
| 1006 | critical | 注册GK失败     |
+------+----------+----------------+
20 rows in set (4.26 sec)

可以查出时间相对第一种少了一点。

整体来说在面对百万级数据的时候如果使用上面第三种方法来优化,系统性能上是能够得到很好的提升,在遇到复杂的查询时也尽量简化,减少运算量。 同时也尽量多的使用内存缓存,有条件的可以考虑分表、分库、阵列之类的大型解决方案了。

参考文章:

http://www.lvtao.net/database/mysql_page_limit.html

http://my.oschina.net/u/1540325/blog/477126

时间: 2024-10-18 17:18:46

mysql 单表百万级记录查询分页优化的相关文章

Mysql数据库百万级记录查询分页优化

很多的朋友在面试中会遇到这样的问题,也有很多的项目在运营一段时间后也会遇到MYSQL查询中变慢的一些瓶颈,今天这儿简单的介绍下我常用的几种查询分页的方法,我所知道的也无非就是索引.分表.子查询偏移,所以要是有什么不对或有更好的方法,欢迎大家留言讨论. 效率分析关键词:explain + SQL语句 一,最常见MYSQL最基本的分页方式limit: select * from `table` order by id desc limit 0, 20 在中小数据量的情况下,这样的SQL足够用了,唯一

MySQL单表百万数据记录分页性能优化

原文地址:http://www.cnblogs.com/lyroge/p/3837886.html MySQL单表百万数据记录分页性能优化 背景: 自己的一个网站,由于单表的数据记录高达了一百万条,造成数据访问很慢,Google分析的后台经常报告超时,尤其是页码大的页面更是慢的不行. 测试环境: 先让我们熟悉下基本的sql语句,来查看下我们将要测试表的基本信息 use infomation_schemaSELECT * FROM TABLES WHERE TABLE_SCHEMA = 'dbna

MySQL 单表百万数据记录分页性能优化

来源:一颗卤蛋 链接:http://www.cnblogs.com/lyroge/p/3837886.html 背景: 自己的一个网站,由于单表的数据记录高达了一百万条,造成数据访问很慢,Google分析的后台经常报告超时,尤其是页码大的页面更是慢的不行. 测试环境: 先让我们熟悉下基本的sql语句,来查看下我们将要测试表的基本信息 use infomation_schemaSELECT * FROM TABLES WHERE TABLE_SCHEMA = ‘dbname’ AND TABLE_

MySQL单表百万数据记录分页性能优化,转载

背景: 自己的一个网站,由于单表的数据记录高达了一百万条,造成数据访问很慢,Google分析的后台经常报告超时,尤其是页码大的页面更是慢的不行. 测试环境: 先让我们熟悉下基本的sql语句,来查看下我们将要测试表的基本信息 use infomation_schemaSELECT * FROM TABLES WHERE TABLE_SCHEMA = 'dbname' AND TABLE_NAME = 'product' 查询结果: 从上图中我们可以看到表的基本信息: 表行数:866633平均每行的

MySQL单表多字段模糊查询解决方法

在最近的一个项目需要实现在单表中对多字段进行多个关键字的模糊查询,但这数个关键字并不一定都存在于某个字段 例如现有table表,其中有title,tag,description三个字段,分别记录一条资料的标题,标签和介绍.然后根据用户输入的查询请求,将输入的字串通过空格分割为多个关键字,再在这三个字段中查询包含这些关键字的记录. 可目前遇到的问题是,这些关键字是可能存在于三个字段中的任意一个或者多个,但又要求三个字段必须包含所有的关键词.如果分别对每个字段进行模糊匹配,是没法实现所需的要求,由此

单表60亿记录等大数据场景的MySQL优化和运维之道

此文是根据杨尚刚在[QCON高可用架构群]中,针对MySQL在单表海量记录等场景下,业界广泛关注的MySQL问题的经验分享整理而成,转发请注明出处. 杨尚刚,美图公司数据库高级DBA,负责美图后端数据存储平台建设和架构设计.前新浪高级数据库工程师,负责新浪微博核心数据库架构改造优化,以及数据库相关的服务器存储选型设计. 前言 MySQL数据库大家应该都很熟悉,而且随着前几年的阿里的去IOE,MySQL逐渐引起更多人的重视. MySQL历史 1979年,Monty Widenius写了最初的版本,

单表60亿记录等大数据场景的MySQL优化和运维之道 | 高可用架构(转)

转自http://www.php1.cn/Content/DanBiao_60_YiJiLuDengDaShuJuChangJingDe_MySQL_YouHuaHeYunWeiZhiDao_%7C_GaoKeYongJiaGou.html, 更多详细资料请参看原文 此文是根据杨尚刚在[QCON高可用架构群]中,针对MySQL在单表海量记录等场景下,业界广泛关注的MySQL问题的经验分享整理而成,转发请注明出处. 杨尚刚,美图公司数据库高级DBA,负责美图后端数据存储平台建设和架构设计.前新浪高

[转载] 单表60亿记录等大数据场景的MySQL优化和运维之道 | 高可用架构

原文: http://mp.weixin.qq.com/s?__biz=MzAwMDU1MTE1OQ==&mid=209406532&idx=1&sn=2e9b0cc02bdd4a02f7fd81fb2a7d78e3&scene=1&key=0acd51d81cb052bce4ec2a825666e97fe7d6e1072fb7d813361771645e9403309eb1af025691162c663b60ea990c3781&ascene=0&

【转】单表60亿记录等大数据场景的MySQL优化和运维之道 | 高可用架构

此文是根据杨尚刚在[QCON高可用架构群]中,针对MySQL在单表海量记录等场景下,业界广泛关注的MySQL问题的经验分享整理而成,转发请注明出处. 杨尚刚,美图公司数据库高级DBA,负责美图后端数据存储平台建设和架构设计.前新浪高级数据库工程师,负责新浪微博核心数据库架构改造优化,以及数据库相关的服务器存储选型设计. 前言 MySQL数据库大家应该都很熟悉,而且随着前几年的阿里的去IOE,MySQL逐渐引起更多人的重视. MySQL历史 1979年,Monty Widenius写了最初的版本,