当前位置:

首页 > 系统应用 > 千万级数据深分页查询SQL性能优化实践|京东云技术团队

千万级数据深分页查询SQL性能优化实践|京东云技术团队

一、系统介绍和问题描述如何在Mysql中进行上亿数据的遍历查询呢?先来说说咱们的系统主角——关注系统。它主要负责维护京东用户和业务对象之间的关注关系,还能对外提供各种关系查询,像查询用户的关注商品或店铺列表,以及查询用户是否关注了某个商品或店铺等。但最近有个新需求,得提供查询关注对象的粉丝列表接口功

一、系统介绍和问题描述

如何在Mysql中进行上亿数据的遍历查询呢?先来说说咱们的系统主角——关注系统。它主要负责维护京东用户和业务对象之间的关注关系,还能对外提供各种关系查询,像查询用户的关注商品或店铺列表,以及查询用户是否关注了某个商品或店铺等。但最近有个新需求,得提供查询关注对象的粉丝列表接口功能。这功能的难点在于,关注对象的粉丝数量实在太多了,不少店铺的粉丝都有千万级别,有些大V的粉丝甚至能达到上亿级别。而这些粉丝列表数据现在都存放在Mysql库中,是通过业务对象ID进行分库分表的,所有粉丝列表数据分布在16个分片的256张表里。同时,为了方便查询粉丝列表,同一个业务对象的所有粉丝都会被路由到同一张表中,这样每个表的数据量都能达到2亿多。

千万级数据深分页查询SQL性能优化实践|京东云技术团队

二、解决问题的思路和方法

数据库表结构示例如下:

CREATE TABLE follow_fans_[0-255]
(
id bigint(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
biz_content VARCHAR(50) DEFAULT NULL COMMENT '业务对象ID',
source VARCHAR(50) DEFAULT NULL COMMENT '来源',
pin VARCHAR(50) DEFAULT NULL COMMENT '用户pin',
ext VARCHAR(5000) DEFAULT NULL COMMENT '扩展信息',
status TINYINT(2) DEFAULT 1 COMMENT '状态,0是失效,1是正常',
created_time DATETIME DEFAULT NULL COMMENT '创建时间',
modified_time DATETIME DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY(id),
UNIQUE INDEX uniq_biz_content_pin (biz_content, pin)
)
ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT = '关注粉丝表';

Limit实现

由于同一个业务对象的所有粉丝都保存到一张数据库表中,对于分页查询列表接口,首先想到的就是用limit实现,对于粉丝数量很少的关注对象,查询接口性能还不错。但是随着关注对象的粉丝数量越来越多,接口查询性能就会越来越慢。后来经过接口压测,当业务对象粉丝列表数量达到几十万级别的时候,查询页码数量越大,查询耗时越多。limit深分页为什么会变慢?这就和sql的执行计划有关了,limit语句会先扫描offset+n行,然后再丢弃掉前offset行,返回后n行数据。也就是说limit 100000,10,就会扫描100010行,而limit 0,10,只扫描10行。查询 sql 示例如下:

select  id,biz_content,pin FROM follow_fans_1 where biz_content = #{bizContent} order by id desc limit 10, 10;
  • 方案优点:实现简单,支持跳页查询。
  • 方案缺点:数据量变大时,随着查询页码的深入,查询性能越来越差。

标签记录法

Limit深分页问题的本质原因就是:偏移量(offset)越大,mysql就会扫描越多的行,然后再抛弃掉,这样就导致查询性能的下降。所以我们可以采用标签记录法,就是标记一下上次查询到哪一条了,下次再来查的时候,从该条开始往下扫描。具体做法方式是,查询粉丝列表中按照自增主键ID倒序查询,查询结果中返回主键ID,然后查询入参中增加maxId参数,该参数需要透传上一次请求粉丝列表中最后一条记录主键ID,第一次查询时可以为空,但是需要查询下一页时就必传。最后根据查询时返回的行数是否等于 10 来判断整个查询是否可以结束。优化后的查询sql参考如下:

select id,biz_content,pin FROM follow_fans_1 where biz_content = #{bizContent} and id < #{lastId} order by id desc limit 10;
  • 方案优点:避免了数据量变大时,页码查询深入的性能下降问题;经过接口压测,千万级数据量时,前 N-1页查询耗时可以控制在几十毫秒内。
  • 方案缺点:只能支持按照页码顺序查询,不支持跳页,而且仅能保证前 N-1 页的查询性能;如果最后一页的表中行数量不满 10 条时,引擎不知道何时终止查询,只能遍历全表,所以当表中数据量很大时,还是会出现超时情况。

区间限制法

标签记录法最后一页查询超时就是因为不知道何时终止查询,所以我们可以提供一个区间限制范围来告诉引擎查询到此结束。

查询sql再次优化后参考如下:

select id,biz_content,pin FROM follow_fans_1 where biz_content = #{bizContent} and id < #{lastId} and id >={minId} order by id desc limit 10;

由于查询时需要带上 minId 参数,所以在执行查询粉丝列表之前,我们就需要先把 minId 查询出来,查询 sql 参考如下:

select min(id) from follow_fans_1 where biz_content = #{bizContent}

由于表中数据量太大,每个表中总数据量都是上亿级别,导致第一步查询 minId就直接超时了,根本没有机会去执行第二步。但是考虑到上一个查询方案只有最后一页才会查询超时,前N-1页查询根本用不到 minId 作为区间限制。所以当表中数据量很大时,通常从第一页到最后一页查询之间会存在一定的时间差。我们就可以正好去利用这个时间差去异步查询minId,然后将查询出来的minId存储到缓存中,考虑到这个 minId 可能会被删除,可以设置一定的过期时间。最后优化后的查询流程如下:

  1. 调用查询粉丝列表方法时首先查询缓存minId;
  2. 如果缓存minId 为空,则创建异步任务去执行select min(id) 查询表中的 minId,然后回写缓存,该异步任务执行时间可能会很长,可以单独设置超时时间。
  3. 如果缓存minId不为空,则在查询sql中拼接查询条件id >={minId},从而保证查询最后一页时不会超时。

但是在上述方案中,如果表中的数据量达到上亿级别时,第二步的异步获取minId任务还是会存在超时的风险,从而导致查询最后一页粉丝列表出现超时。所以我们又引入了离线数据计算任务,通过在大数据平台离线计算获取每个biz_content下的minId,然后将计算结果minId推送到缓存中。为了保证minId能够及时更新,我们可以自由设置该离线任务的执行周期,比如每周执行一次。通过大数据平台的离线计算minId,从而大大减少了在查询粉丝列表时执行 select min(id)的业务数据库压力。只有当缓存没有命中的时候才去执行 select min(id),通常这些缓存没有命中的 minId 也都是一些被离线任务遗漏的少量数据,不会影响接口的整体查询性能。

  • 方案优点:避免了数据量变大时,页码查询深入的性能下降问题;经过接口压测,千万级数据量时,从第一页到最后一页都控制在几十毫秒内。
  • 方案缺点:只能支持按照页码顺序和主键ID倒序查询,不支持跳页查询,并且还需要依赖大数据平台离线计算和额外的缓存来存储 minId。

三、对SQL优化治理的思考

通过对以上三种方案的探索实践,发现每一种方案都有自己的优缺点和它的适用场景,我们不能脱离实际业务场景去谈方案的好坏。所以我们要结合实际的业务环境以及表中数据量的大小去综合考虑、权衡利弊,然后找到更适合的技术方案。以下是总结的几条SQL优化建议:

查询条件一定要有索引

索引主要分为两大类,聚簇索引和非聚簇索引,可以通过 explain 查看 sql 执行计划判断查询是否使用了索引。

聚簇索引 (clustered index):聚簇索引的叶子节点存储行记录,InnoDB必须要有且只有一个聚簇索引:

  1. 如果表定义了主键,则主键索引就是聚簇索引;
  2. 如果没有定义主键,则第一个非空的唯一索引列是聚簇索引;
  3. 如果没有唯一索引,则创建一个隐藏的row-id列作为聚簇索引。主键索引查询非常快,可以直接定位行记录。

非聚簇索引 (secondary index):InnoDB非聚簇索引的叶子节点存储的是行记录的主键值,而MyISAM叶子节点存储的是行指针。 通常情况下,需要先遍历非聚簇索引获得聚簇索引的主键ID,然后在遍历聚簇索引获取对应行记录。

正确使用索引,防止索引失效

可以参考以下几点索引原则:

  1. 最左前缀匹配原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如 a=1 and b=2 and c>3 and d=4 ,如果建立了(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a、b、d的顺序可以任意调整。
  2. =和in可以乱序,比如 a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮助优化成索引可以识别的形式。
  3. 尽量选择区分度高德列作为索引,区分度公式count(distinct col)/count(*),表示字段不重复的比例。
  4. 索引列不能使用函数或参与计算,不能进行类型转换,否则索引会失效。
  5. 尽量扩展索引,不要新建索引。

减少查询字段,避免回表查询

回表查询,简单来说,就是先定位主键值,再依据主键值去定位行记录,这一过程需要扫描两遍索引。那怎么解决呢?其实很简单,只要在一颗索引树上就能获取SQL所需要的所有列数据,就无需回表查询了,这样速度自然更快。我们可以把要查询的字段,建立到联合索引里去,这就是所谓的索引覆盖。当查询sql进行explain解析时,如果Extra字段显示为Using Index,那就说明触发了索引覆盖。反之,如果没有触发索引覆盖,发生了回表查询,Extra字段就会显示为Using Index condition。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
系统应用 性能优化
相关文章 更多
StartAllBack开机启动设置在哪里及设置方法
StartAllBack开机启动设置在哪里及设置方法

找不到StartAllBack开机启动选项?本文详解如何在Windows设置、任务管理器及启动文件夹中定位StartAllBack启动项,并验证其是否随系统自动运行,解决重启后界面恢复默认的问题。

windows显示语言为什么改不了中文及解决方法
windows显示语言为什么改不了中文及解决方法

遇到Windows界面无法切换为中文的情况?本文详解如何区分输入法与显示语言,排查单一语言版本限制,正确安装中文语言包并解决下载失败问题,助您快速恢复中文界面。

Windows10安装过程分区删除重建正确步骤
Windows10安装过程分区删除重建正确步骤

Windows 10 安装过程中遇到分区混乱或报错?本教程指导你如何在备份数据后,正确识别目标磁盘,匹配 UEFI/GPT 启动模式,并通过安装程序自动重建系统分区,确保全新安装顺利启动。

在线安装 - 1panel 文档 Linux安装步骤详解
在线安装 - 1panel 文档 Linux安装步骤详解

详解 1Panel 在 Linux 服务器上的在线安装步骤。涵盖环境检查、SSH 终端操作、官方脚本执行、Docker 依赖处理及浏览器访问配置,助您快速搭建运维管理面板。

Windows 10家庭版怎么升级到专业版?步骤与成本说明
Windows 10家庭版怎么升级到专业版?步骤与成本说明

想知道如何将 Windows 10 家庭版升级到专业版?本文详解通过微软商店购买和输入产品密钥两种官方升级方法,说明数据保留情况、成本计算及升级后如何验证 BitLocker 和远程桌面等功能,助您安全获取专业版权限。

win10自动更新突然恢复启用是什么原因
win10自动更新突然恢复启用是什么原因

Windows 10 自动更新突然重新启用怎么办?本文详解暂停35天期限结束、误点继续更新、重启后任务续跑及公司域策略覆盖等常见原因,提供查看更新历史、设置活动时间和正确使用疑难解答的操作指南,帮助用户在支持结束前安全管理系统更新。

Media Creation Tool下载失败或速度慢的解决方法
Media Creation Tool下载失败或速度慢的解决方法

解决 Media Creation Tool 在下载 Windows 镜像时速度慢、中断或报错的问题。本文按操作顺序指导用户检查网络连接、系统权限、磁盘空间及 U 盘状态,确保顺利制作可启动安装介质。

普通用户选Windows 10家庭版还是专业版?适用场景分析
普通用户选Windows 10家庭版还是专业版?适用场景分析

不确定该选Windows 10家庭版还是专业版?本文解析两者在远程连接、磁盘加密和虚拟化等方面的实际差异,帮你避开性能误区,根据真实使用场景做出最具性价比的选择。

win10断电重启后自动继续更新要怎么阻止
win10断电重启后自动继续更新要怎么阻止

Win10断电重启后总是自动继续更新怎么办?别强行关机,先等恢复完成。教你用暂停更新、设置活动时间和按流量计费连接,减少更新对工作的打扰。注意Win10支持已终止,长期不更新有风险。

win10能不能单独关闭功能更新不关闭安全补丁
win10能不能单独关闭功能更新不关闭安全补丁

解析Windows 10功能更新与质量更新的区别。说明个人家庭版无法永久单独关闭功能更新,专业版可通过组策略延迟功能升级。提醒用户关注Win10支持终止时间,避免误用禁用服务导致安全风险。

查看更多
精品专题 更多
装机必备
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows
Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

macOS软件
macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

Mac软件 更多
photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。

灵活计算器
灵活计算器
macOS/iOS/Android

灵活计算器是一款笔记式算数应用,支持实时计算、动态关联和云端同步功能。记录、整理和输出之间的过渡会更自然,适合长期写作、做笔记或持续沉淀个人内容。

WINDOWS 更多
3dmax(3ds max)
3dmax(3ds max)
Windows

Autodesk 3ds Max 是一款专业的三维建模、动画与渲染软件,广泛应用于建筑可视化、游戏开发、影视动画、广告设计和产品展示等领域。

photoshop
photoshop
Windows、macOS 、 iPad

Photoshop 2026 是 Adobe 推出的专业图像处理与视觉设计软件,支持 Windows、macOS 和 iPad 等平台,广泛应用于摄影修图、电商设计、平面海报、数字绘画及视觉合成等创作场景。

Blender
Blender
Windows、macOS 和 Linux

Blender 是一款免费开源、跨平台的专业 3D 创作软件,集建模、动画、渲染、视频编辑与视觉合成等功能于一体,广泛应用于影视动画、游戏设计和建筑可视化等领域。软件支持 Cycles 物理渲染器与 Eevee 实时渲染引擎,并提供多边形建模、骨骼绑定、物理模拟等专业工具。Blender 兼容 Windows、macOS 和 Linux 系统,安装包轻巧、运行流畅,依托活跃的全球开发者社区持续更新,是从初学者到专业创作者都值得选择的正版 3D 创作工具。