返回顶部
首页 > 资讯 > 数据库 >浅谈MySQL 亿级数据分页的优化
  • 529
分享到

浅谈MySQL 亿级数据分页的优化

MySQL亿级数据分页MySQL分页优化 2022-05-27 11:05:39 529人浏览 八月长安
摘要

目录背景分析数据模拟1、创建两个表:员工表和部门表2、创建两个函数:生成随机字符串和随机编号3、编写存储过程,模拟500W的员工数据4、编写存储过程,模拟120的部门数据5、建立关键字段的索引,这边是跑完数据之后再建

目录
  • 背景
  • 分析
  • 数据模拟
    • 1、创建两个表:员工表和部门表
    • 2、创建两个函数:生成随机字符串和随机编号
    • 3、编写存储过程,模拟500W的员工数据
    • 4、编写存储过程,模拟120的部门数据
    • 5、建立关键字段的索引,这边是跑完数据之后再建索引,会导致建索引耗时长,但是跑数据就会快一些。
  • 测试
    • 执行结果
  • 解决方案
    • 1、使用索引覆盖+子查询优化
    • 2、起始位置重定义
    • 3、降级策略
  • 小结

    背景

    下班后愉快的坐在在回家的地铁上,心里想着周末的生活怎么安排。

    突然电话响了起来,一看是我们的一个开发同学,顿时紧张了起来,本周的版本已经发布过了,这时候打电话一般来说是线上出问题了。

    果然,沟通的情况是线上的一个查询数据的接口被疯狂的失去理智般的调用,这个操作直接导致线上的Mysql集群被拖慢了。
    好吧,这问题算是严重了,下了地铁匆匆赶到家,开电脑,跟同事把Pinpoint上的慢查询日志捞出来。看到一个很奇怪的查询,如下

    
    POST  domain/v1.0/module/method?order=condition&orderType=desc&offset=1800000&limit=500

    domain、module 和 method 都是化名,代表接口的域、模块和实例方法名,后面的offset和limit代表分页操作的偏移量和每页的数量,也就是说该同学是在 翻第(1800000/500+1=3601)页。初步捞了一下日志,发现 有8000多次这样调用。

    这太神奇了,而且我们页面上的分页单页数量也不是500,而是 25条每页,这个绝对不是人为的在功能页面上进行一页一页的翻页操作,而是数据被刷了(说明下,我们生产环境数据有1亿+)。详细对比日志发现,很多分页的时间是重叠的,对方应该是多线程调用。

    通过对鉴权的Token的分析,基本定位了请求是来自一个叫做apiAutotest的客户端程序在做这个操作,也定位了生成鉴权Token的账号来自一个QA的同学。立马打电话给同学,进行了沟通和处理。

    分析

    其实对于我们的mysql查询语句来说,整体效率还是可以的,该有的联表查询优化都有,该简略的查询内容也有,关键条件字段和排序字段该有的索引也都在,问题在于他一页一页的分页去查询,查到越后面的页数,扫描到的数据越多,也就越慢。
    我们在查看前几页的时候,发现速度非常快,比如 limit 200,25,瞬间就出来了。但是越往后,速度就越慢,特别是百万条之后,卡到不行,那这个是什么原理呢。先看一下我们翻页翻到后面时,查询的sql是怎样的:

    
    select * from t_name where c_name1='xxx' order by c_name2 limit 2000000,25;
    

    这种查询的慢,其实是因为limit后面的偏移量太大导致的。比如像上面的 limit 2000000,25 ,这个等同于数据库要扫描出 2000025条数据,然后再丢弃前面的 20000000条数据,返回剩下25条数据给用户,这种取法明显不合理。

    大家翻看《高性能MySQL》第六章:查询性能优化,对这个问题有过说明:

    分页操作通常会使用limit加上偏移量的办法实现,同时再加上合适的order by子句。但这会出现一个常见问题:当偏移量非常大的时候,它会导致MySQL扫描大量不需要的行然后再抛弃掉。

    数据模拟

    那好,了解了问题的原理,那就要试着解决它了。涉及数据敏感性,我们这边模拟一下这种情况,构造一些数据来做测试。

    1、创建两个表:员工表和部门表

    
    
    drop table if EXISTS dep;
    create table dep(
        id int unsigned primary key auto_increment,
        depno mediumint unsigned not null default 0,
        depname varchar(20) not null default "",
        memo varchar(200) not null default ""
    );
    
    
    drop table if EXISTS emp;
    create table emp(
        id int unsigned primary key auto_increment,
        empno mediumint unsigned not null default 0,
        empname varchar(20) not null default "",
        job varchar(9) not null default "",
        mgr mediumint unsigned not null default 0,
        hiredate datetime not null,
        sal decimal(7,2) not null,
        comn decimal(7,2) not null,
        depno mediumint unsigned not null default 0
    );
    
    

    2、创建两个函数:生成随机字符串和随机编号

    
    
    DELIMITER $
    drop FUNCTION if EXISTS rand_string;
    CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
    BEGIN
        DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmlopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        DECLARE return_str VARCHAR(255) DEFAULT '';
        DECLARE i INT DEFAULT 0;
        WHILE i < n DO
        SET return_str = CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1));
        SET i = i+1;
        END WHILE;
        RETURN return_str;
    END $
    DELIMITER;
    
    
    
    DELIMITER $
    drop FUNCTION if EXISTS rand_num;
    CREATE FUNCTION rand_num() RETURNS INT(5)
    BEGIN
        DECLARE i INT DEFAULT 0;
        SET i = FLOOR(100+RAND()*10);
        RETURN i;
    END $
    DELIMITER;
    
    

    3、编写存储过程,模拟500W的员工数据

    
    
    DELIMITER $
    drop PROCEDURE if EXISTS insert_emp;
    CREATE PROCEDURE insert_emp(IN START INT(10),IN max_num INT(10))
    BEGIN
        DECLARE i INT DEFAULT 0;
        
        SET autocommit = 0;
        REPEAT
        SET i = i + 1;
        INSERT INTO emp(empno,empname,job,mgr,hiredate,sal,comn,depno) VALUES ((START+i),rand_string(6),'SALEMAN',0001,now(),2000,400,rand_num());
        UNTIL i = max_num
        END REPEAT;
        COMMIT;
    END $
    DELIMITER;
    
    call insert_emp(0,5000000);
    

    4、编写存储过程,模拟120的部门数据

    
    
    DELIMITER $
    drop PROCEDURE if EXISTS insert_dept;
    CREATE PROCEDURE insert_dept(IN START INT(10),IN max_num INT(10))
    BEGIN
        DECLARE i INT DEFAULT 0;
        SET autocommit = 0;
        REPEAT
        SET i = i+1;
        INSERT  INTO dep( depno,depname,memo) VALUES((START+i),rand_string(10),rand_string(8));
        UNTIL i = max_num
        END REPEAT;
        COMMIT;
    END $
    DELIMITER;
    
    call insert_dept(1,120);
    

    5、建立关键字段的索引,这边是跑完数据之后再建索引,会导致建索引耗时长,但是跑数据就会快一些。

    
    
    CREATE INDEX idx_emp_id ON emp(id);
    CREATE INDEX idx_emp_depno ON emp(depno);
    CREATE INDEX idx_dep_depno ON dep(depno);
    

    测试

    测试数据

    
    
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno order by a.id desc limit 100,25;
    
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno order by a.id desc limit 4800000,25;
    

    执行结果

    [SQL]
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno order by a.id desc limit 100,25;
    受影响的行: 0
    时间: 0.001s
    [SQL]
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno order by a.id desc limit 4800000,25;
    受影响的行: 0
    时间: 12.275s

    因为扫描的数据多,所以这个明显不是一个量级上的耗时。

    解决方案

    1、使用索引覆盖+子查询优化

    因为我们有主键id,并且在上面建了索引,所以可以先在索引树中找到开始位置的 id值,再根据找到的id值查询行数据。

    
    
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id >= (select id from emp order by id limit 100,1)
    order by a.id limit 25;
    
    
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id >= (select id from emp order by id limit 4800000,1)
    order by a.id limit 25;
    
    

    执行结果

    执行效率相比之前有大幅的提升:
    [SQL]
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id >= (select id from emp order by id limit 100,1)
    order by a.id limit 25;
    受影响的行: 0
    时间: 0.106s

    [SQL]
    SELECT a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id >= (select id from emp order by id limit 4800000,1)
    order by a.id limit 25;
    受影响的行: 0
    时间: 1.541s

    2、起始位置重定义

    记住上次查找结果的主键位置,避免使用偏移量 offset

    
    
    SELECT a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id > 100 order by a.id limit 25;
    
    
    SELECT a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id > 4800000
    order by a.id limit 25;
    
    

    执行结果

    [SQL]
    SELECT a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id > 100 order by a.id limit 25;
    受影响的行: 0
    时间: 0.001s

    [SQL]
    SELECT a.id,a.empno,a.empname,a.job,a.sal,b.depno,b.depname
    from emp a left join dep b on a.depno = b.depno
    where a.id > 4800000
    order by a.id limit 25;
    受影响的行: 0
    时间: 0.000s

    这个效率是最好的,无论怎么分页,耗时基本都是一致的,因为他执行完条件之后,都只扫描了25条数据。

    但是有个问题,只适合一页一页的分页,这样才能记住前一个分页的最后Id。如果用户跳着分页就有问题了,比如刚刚刷完第25页,马上跳到35页,数据就会不对。

    这种的适合场景是类似百度搜索或者腾讯新闻那种滚轮往下拉,不断拉取不断加载的情况。这种延迟加载会保证数据不会跳跃着获取。

    3、降级策略

    看了网上一个阿里的dba同学分享的方案:配置limit的偏移量和获取数一个最大值,超过这个最大值,就返回空数据。
    因为他觉得超过这个值你已经不是在分页了,而是在刷数据了,如果确认要找数据,应该输入合适条件来缩小范围,而不是一页一页分页。
    这个跟我同事的想法大致一样:request的时候 如果offset大于某个数值就先返回一个4xx的错误。

    小结

    当晚我们应用上述第三个方案,对offset做一下限流,超过某个值,就返回空值。第二天使用第一种和第二种配合使用的方案对程序和数据库脚本进一步做了优化。

    合理来说做任何功能都应该考虑极端情况,设计容量都应该涵盖极端边界测试。

    另外,该有的限流、降级也应该考虑进去。比如工具线程调用,在短时间频率内8000次调用,可以使用计数服务判断并反馈用户调用过于频繁,直接给予断掉。

    到此这篇关于MySQL 亿级数据分页的优化的文章就介绍到这了,更多相关MySQL 亿级数据分页 内容请搜索自学编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持自学编程网!

    您可能感兴趣的文档:

    --结束END--

    本文标题: 浅谈MySQL 亿级数据分页的优化

    本文链接: https://lsjlt.com/news/10392.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • 浅谈MySQL 亿级数据分页的优化
      目录背景分析数据模拟1、创建两个表:员工表和部门表2、创建两个函数:生成随机字符串和随机编号3、编写存储过程,模拟500W的员工数据4、编写存储过程,模拟120的部门数据5、建立关键字段的索引,这边是跑完数据之后再建...
      99+
      2022-05-27
      MySQL 亿级数据分页 MySQL 分页优化
    • 如何使用分区处理MySQL的亿级数据优化
      mysql在查询上千万级数据的时候,通过索引可以解决大部分查询优化问题。但是在处理上亿数据的时候,索引就不那么友好了。 数据表(日志)是这样的: 表大小:1T,约24亿行; 表分区:按时间分区,每个...
      99+
      2022-05-11
      mysql 亿级处理数据 MySQL分区亿级数据
    • 浅谈mysql使用limit分页优化方案的实现
      Mysql limit分页语句用法 与Oracle和MS SqlServer相比,mysql的分页方法简单的让人想哭。 --语法: SELECT * FROM table LIMIT [offs...
      99+
      2024-04-02
    • 浅谈MySQL索引优化分析
      为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字段的意义。助你了解索引,分析索...
      99+
      2024-04-02
    • MySQL亿级数据数据库优化的方法是什么
      本篇内容主要讲解“MySQL亿级数据数据库优化的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“MySQL亿级数据数据库优化的方法是什么”吧!对MySQ...
      99+
      2024-04-02
    • MySQL 百万级数据分页查询优化
      方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺点: 全表扫描,速度会很慢 ...
      99+
      2020-02-11
      MySQL 百万级数据分页查询优化
    • MySQL百万级数据分页查询优化方案
      当需要从数据库查询的表有上万条记录的时候,一次性查询所有结果会变得很慢,特别是随着数据量的增加特别明显,这时需要使用分页查询。对于数据库分页查询,也有很多种方法和优化的点。下面简单说一下我知道的一些方法。 ...
      99+
      2024-04-02
    • 浅谈Mysql大数据分页查询解决方案
      目录1.简介2.分页插件使用3.sql测试与分析3.1 limit现象分析3.2 解决之道4 测试时走过的坑4.1 百万数据内容都一样4.2 写sql时,把"77"...
      99+
      2024-04-02
    • Mysql大数据量分页优化
      Mysql大数据量分页优化 参考文章: MySQL百万级数据量分页查询方法及其优化 MySQL分页查询优化     重点提一下: 在优化分页语句过程中,一定注意查询的语句添加排序字段,一定自己建表实践,...
      99+
      2021-09-09
      Mysql大数据量分页优化
    • SQL级别数据分页性能优化
      在SQL级别对数据分页性能进行优化可以通过以下几种方法来实现: 使用索引:在数据库表中创建合适的索引可以加快分页查询的速度。确保要查询的列上有合适的索引,可以减少查询的数据量,提高查询速度。 使用LIMIT和OFFSET:在SQL语...
      99+
      2024-08-03
      sql
    • 怎么浅谈数据库优化方案
      今天就跟大家聊聊有关怎么浅谈数据库优化方案,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。下面给大家分析了数据库优化方案,具体内容如下1. 利用表分区...
      99+
      2024-04-02
    • MySQL百万级数据大分页查询优化的实现
      目录一、MySQL分页起点越大查询速度越慢二、 limit大分页问题的性能优化方法(1)利用表的覆盖索引来加速分页查询(2)用上次分页的最大id优化三、MySQL百万数据快速生成3....
      99+
      2024-04-02
    • 浅谈MySQL分页Limit的性能问题
      MySQL的分页查询通常通过limit来实现。limit接收1或2个整数型参数,如果是2个参数,第一个是指定第一个返回记录行的偏移量,第二个是返回记录行的最大数目。初始记录行的偏移量是0。为了与Postgr...
      99+
      2024-04-02
    • 怎么样优化mysql千万级数据分页查询性能
      本文主要给大家简单讲讲怎么样优化mysql千万级数据分页查询性能,相关专业术语大家可以上网查查或者找一些相关书籍补充一下,这里就不涉猎了,我们就直奔主题吧,希望怎么样优化mysql千万级数据分页查询性能这篇...
      99+
      2024-04-02
    • Kettle--MySQL生产数据库千万、亿级数据量迁移方案及性能优化
      一、Kettle环境搭建 一、Windows主要用于可视化创建数据迁移用到的ktr转换脚本、kjb作业脚本,以及脚本需要配置的DB2信息、读写性能优化信息等,也可直接在客户端完成不同数据库之间的数据迁移。 测试库表及数据 (1)创建测试库表...
      99+
      2023-09-10
      数据库 linux 服务器
    • mysql数据库分页优化的方法有哪些
      MySQL数据库分页优化的方法主要有以下几种:1. 使用索引:在分页查询中,使用合适的索引可以大大提高查询效率。可以为经常进行分页查...
      99+
      2023-08-30
      mysql数据库
    • mysql大数据分页优化的方法有哪些
      MySQL大数据分页优化的方法有以下几种:1. 使用索引:对于大数据表,使用合适的索引可以大大提高分页查询的性能。可以根据实际情况选...
      99+
      2023-08-23
      mysql
    • mysql大数据分页优化的方法是什么
      在处理大数据分页时,可以通过以下方法来优化MySQL数据库的性能: 使用索引:为查询条件中经常使用的字段添加索引,可以加快查询速...
      99+
      2024-04-09
      mysql
    • MySQL Limit性能优化及分页数据性能优化详解
      MySQL Limit可以分段查询数据库数据,主要应用在分页上。虽然现在写的网站数据都是千条级别,一些小的的优化起的作用不大,但是开发就要做到极致,追求完美性能。下面记录一些limit性能优化方法。 Lim...
      99+
      2024-04-02
    • MySQL百万级数据量分页查询方法及其优化建议
      数据库SQL优化是老生常谈的问题,在面对百万级数据量的分页查询,又有什么好的优化建议呢?下面将列举了一些常用的方法,供大家参考学习! 方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用...
      99+
      2022-05-14
      MySQL 分页查询 MySQL 分页查询优化 MySQL 百万级数据分页查询
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作