1。问题复现在实际的软件系统开发过程中,随着使用的用户群体越来越多,表数据也会随着时间的推移不断增加,单表的数据量会越来越大。以订单表为例,如果日订单量在4万左右,那么月订单量在120万以上,年订单量在1400万以上。数据量会越来越大,订单数据的查询也不会像最初那么简单快捷。如果查询的关键字段没有被索引,将直接影响用户体验,甚至影响服务的正常运行!我以电子商务系统的客户表为例。数据库是Mysql,数据量100万以上。我将详细介绍分页查询下不同阶段的查询效率(订单表的情况类似,只是它的数据量比客户表大)。接下来,我们一起来测试一下。每次查询customer表,最多返回100条数据。在不同的起始条件下,数据库查询的性能是不同的。当起点为0时,仅需时间:18毫秒。当起点在1000时,只需要时间:23毫秒。当起点在10000时,只需要时间:54毫秒。当起点为100000时,仅耗时:268ms当起点为500000时,仅耗时:1.16s当起点为1000000时,仅耗时:2.35s可以看出非常显然,随着起点变大,分页查询的效率呈指数级下降。当起点在100万以上时,单表百万级数据量,查询时间基本以秒为单位。实际上,查询时间超过1秒的SQL称为慢SQL。一些公司的运维团队可能会有更严格的要求。比如在我公司,如果SQL的执行时间超过0.2s,又称为慢SQL,必须在有限的时间内尽快优化,否则可能会影响服务的正常运行和用户体验。千万级的单表数据查询,我只是用了分页查询,起点是10000000,我也截图给大家看看查询耗时结果:39秒!从来没有接触过如此海量数据的同学可能会对这个查询结果有些吃惊。其实这只是数据库层面的耗时,还不算后端服务的处理链接时间和返回前端的时间,以百万级别的单表查询为例。如果数据库查询需要1秒,那么经过后端数据封装处理,前端数据渲染处理,网络传输时间,如果没有异常的话,差不多在3到4秒之间,有的同学可能对此请求持续时间值不太敏感。根据互联网软件用户体验报告,当平均请求时间小于1秒时,用户体验最佳,此时的软件用户留存率也最高;2秒以内勉强通过,用户可以接受;超过3秒,体验会稍差;如果超过5秒,当前软件基本会被卸载。为了提高用户体验,一些公司会严格控制请求时长。当请求持续时间超过3秒时,会自动放弃请求,从而倒逼技术优化调整SQL语句的查询逻辑,甚至调整后端的整体架构,比如引入缓存中间件redis。搜索引擎elasticSearch等。回到本文要讨论的问题,当单表数据量达到百万级时,查询效率急剧下降。如何优化和改进呢?二、解决方案下面我们来看一下具体的解决方案。1、方案一:查询时只返回主键ID。我们继续回到上面介绍的customer表查询,将select*改为selectid,简化返回字段。让我们观察耗时的查询。当起点为100000时,仅需时间:73ms当起点为500000时,仅需时间:274ms当起点为1000000时,仅需时间:471ms可以明显看出通过简化返回的字段可以显着成倍地提高查询效率。实际操作思路是先通过分页查询满足条件的主键ID,再通过主键ID查询部分数据,可以显着提高查询效果。--先分页查询满足条件的主键idselectidfrombizuuserorderbyidlimit100000,10;--然后分页查询返回的id,批量查询数据select*frombizuuserwhereidin(1,2,3,4,.....);2。方案二:查询时,通过主键ID进行过滤。该方案的一个要求是主键ID必须是数字类型。实际思路是取上次查询结果的ID的最大值作为Filter条件,排序字段必须为主键ID,否则分页排序顺序会乱。查询100000到1000100区间的数据只需要18ms,查询1000000到1000100区间的数据只需要18ms,看清1000000到1000100区间的数据只需要18ms。事实证明,以主键ID作为过滤条件,查询性能非常稳定,基本可以在20ms以内返回。这个方案还是很可行的。如果当前业务不需要太多排序,可以采用这个方案,性能也很不错!但是,如果当前业务有排序需求,比如按客户最后修改时间、客户最后下单时间、客户最后下单金额等字段排序,那么上述[方案1]比[方案2]更有效。!2.方案三:使用elasticSearch作为搜索引擎。当数据量越来越大时,尤其是分库分表的数据库,通过主键ID过滤查询的效果可能会不尽如人意,比如订单数据的查询。这时候比较好的方案是将订单数据存储在elasticSearch中,通过elasticSearch实现快速分页和搜索,效果提升也很明显。关于elasticSearch的玩法,之前给大家介绍过具体的做法,这里就不多写书了。3.小结不知道大家有没有注意到,上面介绍的表主键ID都是数值型的。之所以使用数值类型作为主键,是因为数值类型的字段可以很好的排序。但是如果当前表的主键ID是字符串类型,比如uuid,就没有办法实现这种排序功能,而且查找性能也很差。所以不推荐使用uuid作为主键ID。具体数值型主键ID的生成方案有很多种,比如自增、雪花算法等,都可以很好的满足我们的需求。
