最近在我的常识星球中,有个小同伴问了这样一个疑问:百万商品分页查问接口,如何保障接口的性能?
这就须要对该分页查问接口做优化了。
这篇文章从9个方面跟大家一同聊聊分页查问接口优化的一些小技巧,宿愿对你会有所协助。
关于分页查问接口,假设没有不凡要求,我们可以在输入参数中,给一些自动值。
这样可以减少数据范畴,防止每次都count一切数据的状况。
关于商品查问,这种业务场景,我们可以自动查问今日上架形态的商品列表。
例如:
select * from productwhere edit_date>='2023-02-20' and edit_date<'2023-02-21' and status=1
假设每天有变卦的商品数量不多,经过这两个自动条件,就能过滤掉绝大局部数据,让分页查问接口的性能优化不少。
分页查问接口通常状况下,须要接纳两个参数:pageNo(即:页码)和pageSize(即:每页大小)。
假设分页查问接口的调用端,没有传pageNo自动值是1,假设没有传pageSize也可以给一个自动值10或许20。
不太倡导pageSize传入过大的值,会间接影响接口性能。
在前端有个下拉控件,可以选用每页的大小,选用范畴是:10、20、50、100。
前端自动选用的每页大小为10。
不过在实践业务场景中,要依据产品需求而且,这里只是一个参考值。
有时刻,我们的分页查问接口的查问结果,须要join多张表能力查出数据。
比如在查问商品信息时,须要依据商品称号、单位、品牌、分类等信息查问数据。
这时刻写一条sql可以查出想要的数据,比如上方这样的:
selectp.id,p.product_name,u.unit_name,b.brand_name,c.category_namefrom product pinner join unit u on p.unit_id = u.idinner join brand b on p.brand_id = b.idinner join category c on p.category_id = c.idwhere p.name='测试商品'limit 0,20;
经常使用product表去join了unit、brand和category这三张表。
其实product表中有unit_id、brand_id和category_id三个字段。
我们可以先查出这三个字段,失掉分页的数据增加范畴,之后再经过主键id汇合去查问额外的数据。
我们可以把sql改成这样:
selectp.id,p.product_id,u.unit_id,b.brand_id,c.category_idfrom productwherelimit 0,20;
这个例子中,分页查问之后,我们失掉到的商品列表其实只需20条数据。
再依据20条数据中的id汇合,失掉其余的称号,例如:
select id,namefrom unitwhere id in (1,2,3);
而后在程序中填充其余称号。
伪代码如下:
List<Product> productList = productMapper.search(searchEntity);List<Long> unitIdList = productList.stream().map(Product::getUnitId).distinct().collect(Collectors.toList());List<Unit> unitList = UnitMapper.queryUnitByIdList(unitIdList);for(Product product: productList) {Optional<Unit> optional = unitList.stream().filter(x->x.getId().equals(product.getId())).findAny();if(optional.isPersent()) {product.setUnitName(optional.get().getName());}}
这样就能有效的增加join表的数量,可以必定的水平上优化查问接口的性能。
分页查问接口性能发生了疑问,最间接最极速的优化方法是:优化索引。
由于优化索引不须要修正代码,只需回归测试一下就行,改变老本是最小的。
我们须要经常使用explain关键字,查问一下消费环境分页查问接口的执行方案。
看看有没有创立索引,创立的索引能否正当,或许索引失效了没。
索引不是创立越多越好,也不是创立越少越好,我们须要依据实践状况,到消费环境测试一下sql的耗时状况,而后选择如何创立或优化索引。
倡导优先创立联结索引。
有时刻我们的业务场景很复杂,有很多查问sql,须要创立多个索引。
在分页查问接口中依据不同的输入参数,最终的查问sql语句,MySQL依据必定的抽样算法,却选用了不同的索引。
不知道你有没有遇到过,某个查问接口,原本性能是没疑问的,但一旦输入某些参数,接口照应期间就十分长。
这时刻假设你此时用explain关键字,检查该查问sql执行方案,会发现如今走的索引,跟之前不一样,并且驱动表也不一样。
之前不时都是用表a驱动表b,走的索引c。
此时用的表b驱动表a,走的索引d。
为了处置Mysql选错索引的疑问,最经常出现的手腕是经常使用force_index关键字,在代码中指定走的索引称号。
但假设在代码中硬编码了,前面一旦索引称号修正了,或许索引被删除了,程序或许会间接报错。
这时该怎样办呢?
答:我们可以经常使用straight_join替代inner join。
straight_join会通知Mysql用左边的表驱动左边的表,能改表优化器关于联表查问的执行顺序。
之前的查问sql如下:
select p.id from product pinner join warehouse w on p.id=w.product_id;...
我们用它将之前的查问sql启动优化:
select p.id from product pstraight_join warehouse w on p.id=w.product_id;...
随着期间的推移,我们的系统用户越来越多,发生的数据也越来越多。
单表曾经抵达了几千万。
这时刻分页查问接口性能急剧降低,我们不能不做分表处置了。
做便捷的分表战略是将历史数据归档,比如:在主表中只保管最近三个月的数据,三个月前的数据,保障到历史表中。
我们的分页查问接口,自动从主表中查问数据,可以将数据范畴增加很多。
假设有不凡的需求,再从历史表中查问数据,最近三个月的数据,是用户关注度最高的数据。
在分页查问接口中,须要在sql中经常使用count关键字查问总记载数。
目前count有上方几种用法:
那么它们有什么区别呢?
由此,最后count的性能从高到低是:
所以,其实count(*)是最快的。
有些时刻,join的表真实太多,没法去掉多余的join,该怎样办呢?
答:可以将数据保管到ClickHouse。
ClickHouse是基于列存储的数据库,不允许事务,查问性能十分高,号称查问十几亿的数据,能够秒级前往。
为了防止对业务代码的嵌入性,可以经常使用Canal监听Mysql的binlog日志。当product表有数据新增时,须要同时查问出单位、品牌和分类的数据,生成一个新的结果集,保管到ClickHouse当中。
查问数据时,从ClickHouse当中查问,这样经常使用count(*)的查问效率能够优化N倍。
其实假设查问条件十分多,经常使用ClickHouse也不是特意适合,这时刻可以改成ElasticSearch,不过它跟Mysql一样,存在深分页疑问。
有时刻,分页查问接口性能差,是由于用户并发量过去了。
在系统的初期,还没有多少用户量,读数据恳求和写数据恳求,都是访问的同一个数据库,该模式成功起来便捷、老本低。
刚开局分页查问接口性能没啥疑问。
但随着用户量的增长,用户的读数据恳求和写数据恳求都显著增多。
我们都知道数据库衔接有限,普通是性能的闲暇衔接数是100-1000之间。假设多余1000的恳求,就只能期待,就或许会发生接口超时的状况。
因此,我们有必要做数据库的读写分别。写数据恳求访问主库,读数据恳求访问从库,从库的数据经过binlog从主库同步过去。
依据不同的用户量,可以做一主一从,一主两从,或一主多从。
数据库读写分别之后,能够优化查问接口的性能。