分表情况下的分页如何优化

首先还是要给自己的开原框架打个广告 sharding-core 针对efcore 2+版本的分表组件,首先我们来快速回顾下目前市面上分表下针对分页常见的集中解决方案

分表解决方案

解决方案 skip<=100 skip<10000 skip>10000 优点 缺点
内存分表 速度快O(n),n=skip*分表数 速度快O(n),n=skip*分表数,内存暴涨 O(n),n=skip*分表数,内存爆炸,速度越来越慢 实现简单,支持分库 skip过大内存暴涨
union all 速度快 速度一般 死慢死慢的 实现简单 仅支持同库,不好优化,索引会失效
流式分表 速度快O(n),n=skip 速度快O(n),n=skip O(n),n=skip 速度越来越慢 支持分库 实现复杂

1.内存分页

顾名思义就是将各个表的结果集合并到内存中进行排序后分页

2.union all

使用的是数据库本身的聚合操作,用过匿名表来实现和操作当前表一样无感知

3.流式分表

和名字一样就是通过next来一次一次获取,和datareader类似只有在next后才可以获取到客户端

通过上面的简单对照我们可以清楚地发现,其实我们可以选择的基本上就内存分表和流式分表而已,又以为内存分表的限制其实最优解就是流式分表。

上篇文章我们简单的介绍了流式分表这次我们在针对流式分表的原理进行介绍,并且提出针对流式分表下的分页“最优解”。

流式分表原理

我们先简单的假设一个场景,我们有一个订单表,针对订单表我们进行了分表,根据订单的创建时间按月分表。

如果我们执行 select * from order limit 100,2

内存分页

在这种情况下如果我们需要分页跳过前 100条记录获取第101-102条记录,现在如果内存分表情况下我们该如何操作

流式分页

上述就是内存排序的实现,通过上图发现我们需要获取102*3条数据,并且进行排序后获取第101和102条数据,所以说上述表格里已经体现了内存分表的优劣
那么如果是流式分页我们是如何操作的呢

简单解释下这张图,右边为数据库在数据库外面的分别是next了一次的数据,其他数据都是在数据库里面只是结果集有了但是结果还不没有取到client,

通过100次next后我们可以取到真实的数据所以对于任何分页都是只需要O(n)的时间复杂度,其中n=skip+take就是跳过多少条和获取多少条

注意:不要以为next了100次就是查询了100次数据库,结果集生成后就不会再查询数据库里,next可以理解为是对结果集的客户端获取。

sharding-core的优化

至此流式分表获取数据的原理基本上就是这样,针对这种情况下我们该如何进行对分页数据进行优化,因为上图数据库模块内部的区域是未知的也就是说我们是不知道索引“1”后面的索引“2”和其他语句下的当前索引大小情况,我们只知道索引“1”和索引“2”在本张表里面的排序情况,

针对这种情况我们应该是没办法进行程序的优化了,可以理解为目前情况下已经是最优解了。但是如果我们仔细一想可以发现事情并不简单

大家能看懂吗我们只需要让程序的获取方式按顺序那么就可以保证性能最佳 O(1),所以针对时间分表或者顺序分表的情况下我们一般情况下使用时间倒序或者顺序,那么就可以告诉程序如何排序,又可以得知,在对应顺序的情况下每张表都是顺序的又因为只要保证如下就可以了

有些朋友可能会有疑问,为什么order by id也可以这样,其实order by id是不可以这样的,但是如果你这样又会怎么样?难道数据库用它最优解排序返回是正确,程序用最优解排序返回就不是正确了?

sharding-core的优化升阶

可能有些喷友认为优化到这里就是差不多了但是其实sharding-core针对优化还不止如此,

因为这种排序需要让程序知道以某种情况排序可以按表顺序排序达到性能最优,但是如果我是Id取模或者范围就会导致这个排序仅仅只适合id排序如果需要按别的来排序就没办法了还是得走流式分表.

那么该如何优化呢还是一样我们忽略了分页是2步操作

这种排序仅仅需要的是第一存在order by 第二告诉系统skip多少后需要启用反排,并且该情况适用于任何的分表规则id取模或者别的其他情况都是可以支持的

你以为sharding-core的优化结束了吗?

sharding-core已经实现了以上所有的解决方案,并且已经在实现第三种优化,就是极不规则情况下的分页,具体就是当表查询坐落到3张表后其中2张表或者1张表的count极少的情况下直接取到内存然后剩余的1张表可以直接通过skip+take获取数据后内存排序,

因为时间原因目前还没实现后续会针对这个情况进行实现。

以上就是我为大家带来的理论和干货,

具体的理论听得爽了干货我再发一遍吧 sharding-core

sharding-core如何启用高性能分页

高性能分页

sharding-core本身使用流式处理获取数据在普通情况下和单表的差距基本没有,但是在分页跳过X页后,性能会随着X的增大而减小O(n)

目前该框架已经实现了一套高性能分页可以根据用户配置,实现分页功能。

支持版本x.2.0.16+

1.如何开启分页配置 比如我们针对用户月新表进行分页配置,先实现IPaginationConfiguration<>接口,该接口是分页配置接口


public class SysUserSalaryPaginationConfiguration:IPaginationConfiguration<SysUserSalary>
{
public void Configure(PaginationBuilder<SysUserSalary> builder)
{
builder.PaginationSequence(o => o.Id)
.UseTailCompare(Comparer<string>.Default)
.UseQueryMatch(PaginationMatchEnum.Owner | PaginationMatchEnum.Named | PaginationMatchEnum.PrimaryMatch);
builder.PaginationSequence(o => o.DateOfMonth)
.UseQueryMatch(PaginationMatchEnum.Owner | PaginationMatchEnum.Named | PaginationMatchEnum.PrimaryMatch).UseAppendIfOrderNone(10);
builder.PaginationSequence(o => o.Salary)
.UseQueryMatch(PaginationMatchEnum.Owner | PaginationMatchEnum.Named | PaginationMatchEnum.PrimaryMatch).UseAppendIfOrderNone();
builder.ConfigReverseShardingPage(0.5d,10000L);
}
}

2.添加配置

在对应的用户月薪路由中添加配置

        public override IPaginationConfiguration<SysUserSalary> CreatePaginationConfiguration()
{
return new SysUserSalaryPaginationConfiguration();
}

3.Configure内部为什么意思?

  1. builder.PaginationSequence(o => o.Id) 配置当分页orderby 字段为Id时那么分表所对应的表结构为顺序,顺序的规则通过UseTailCompare来设置,其中string为表tail,

    具体什么意思就是说如果本次分页设计3张表分别是table1,table2,table3,如果我没配置id的情况下那么需要查询3张表然后分别进行流式聚合,如果我配置了id的情况下,如果本次sql查询带上了id作为order by字段

    那么就不需要分别查询3张表,可以直接查询table1如果table1的count大于你要跳过的页数,假设分页查询先查询多少条,table1:100条,table2:200条,table3:300条

    如果你要跳过90条获取10条原先的时间就是O(100)现在的时间就是O(10)因为table1跳过了90条还剩余10条;
  2. UseQueryMatch是什么意思,这个就是表示你要匹配的规则,是必须是当前这个类下的属性还是说只需要排序名称一样即可,因为有可能select new{}匿名对象类型就会不一样,PrimaryMatch表示是否只需要第一个主要的

    orderby匹配上就行了,UseAppendIfOrderNone表示是否需要开启在没有对应order查询条件的前提下添加本属性排序,这样可以保证顺序排序性能最优
  3. builder.ConfigReverseShardingPage 表示是否需要启用反向排序,因为正向排序在skip过多后会导致需要跳过的数据过多,尤其是最后几页,如果开启其实最后几页就是前几页的反向排序,其中第一个参数表示跳过的因子,就是说

    skip必须大于分页总total*该因子(0-1的double),第二个参数表示最少需要total多少条必须同时满足两个条件才会开启(必须大于500),并且反向排序优先级低于顺序排序,

    4.如何使用
var shardingPageResultAsync = await _defaultTableDbContext.Set<SysUserMod>().OrderBy(o=>o.Age).ToShardingPageAsync(pageIndex, pageSize);

注意:如果你是按时间排序无论何种排序建议开启并且加上时间顺序排序,如果你是取模或者自定义分表,建议将Id作为顺序排序,如果没有特殊情况请使用id排序并且加上反向排序作为性能优化

测试

首先我们使用 EFCore.BulkExtensions

针对数据进行创建

一共近295.5w数据耗时24.2秒其中解析表路由耗时3.4秒,插入到本地20.8秒,实际300w订单肯定要比这个时间长因为测试原因所以创建的订单表字段比较少

再不起用高性能分表的情况下我们看下

流式分页



基本在skip 1w后还是可以保持在500ms,skip2w后虽然内存波动不大但是基本上耗时也有显著增加那么如果开启了高性能分表呢

高性能分页







直接爆杀有没有

如果需要使用请在nuget安装ShardingCore记得勾选预览版本哦安装最新版

最后的最后

如果本文章对您有帮助请点下推荐,如果本框架对您有帮助请点下start,Thanks♪(・ω・)ノ github sharding-core

“ShardingCore”是如何针对分表下的分页进行优化的的更多相关文章

  1. efcore分表下"完美"实现

    ShardingCore 如何呈现"完美"分表 这篇文章是我针对efcore的分表的简单介绍,如果您有以下需求那么可以自己选择是否使用本框架,本框架将一直持续更新下去,并且免费开源 ...

  2. 分库分表下uuid的生成

    分库分表时一般有必要自定义生成uuid,大企业一般有自己的uuid生成服务,其他它的实现很简单.我们以订单号为例,组成可以是"业务标识号+年月日+当日自增数字格式化",如00012 ...

  3. efcore分表分库原理解析

    ShardingCore ShardingCore 易用.简单.高性能.普适性,是一款扩展针对efcore生态下的分表分库的扩展解决方案,支持efcore2+的所有版本,支持efcore2+的所有数据 ...

  4. 数据库分库分表和带来的唯一ID、分页查询问题的解决

    需求缘起(用一个公司的发展作为背景) 1.还是个小公司的时候,注册用户就20w,每天活跃用户1w,每天最大单表数据量就1000,然后高峰期每秒并发请求最多就10,此时一个16核32G的服务器,每秒请求 ...

  5. efcore使用ShardingCore实现分表分库下的多租户

    efcore使用ShardingCore实现分表分库下的多租户 介绍 本期主角:ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业 ...

  6. .Net 下高性能分表分库组件-连接模式原理

    ShardingCore ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业务代码入侵. Github Source Code 助 ...

  7. .Net下你不得不看的分表分库解决方案-多字段分片

    .Net下你不得不看的分表分库解决方案-多字段分片 介绍 本期主角:ShardingCore 一款ef-core下高性能.轻量级针对分表分库读写分离的解决方案,具有零依赖.零学习成本.零业务代码入侵 ...

  8. efcore在Saas系统下多租户零脚本分表分库读写分离解决方案

    efcore在Saas系统下多租户零脚本分表分库读写分离解决方案 ## 介绍 本文ShardinfCore版本x.6.0.20+ 本期主角: - [`ShardingCore`](https://gi ...

  9. 基于efcore的分表组件开源

    ShardingCore ShardingCore 是一个支持efcore 2.x 3.x 5.x的一个对于数据库分表的一个简易扩展, 目前该库暂未支持分库(未来会支持),仅支持分表,该项目的理念是让 ...

随机推荐

  1. vue 使用 lang="scss" 报错

    npm install sass-loader@7.3.1 --save-dev npm install node-sass --save npm install style-loader --sav ...

  2. lanm环境

    1,apache (1),安装apache # yum install httpd (2)查看apaceh版本 # httpd -v (3)查看linux版本 # cat /etc/centos-re ...

  3. java小程序之随机任务发布器

    需求 自定义任务 随机定时 随机排序 集合输出 搭建 简单项目,所以我使用java 的maven来搭建一个控制台程序,并且使用excel来记录任务 创建任务类(sub) package com.mic ...

  4. 大数据开发-Go-数组,切片

    new()和make的区别 二者看起来没什么区别,但是他们的行为不同,分别适用于不同的类型 new (T) 为每个新的类型 T 分配一片内存,初始化为 0 并且返回类型为 * T 的内存地址:这种方法 ...

  5. 大写字母与小写字母 相差 一个 空格 A = 65 a = 97 空格 = 32

    1 //大写字母与小写字母 相差 一个 空格 A = 65 a = 97 空格 = 32 2 3 #include <stdio.h> 4 int main() 5 { 6 7 char ...

  6. 内置函数 strlen

    1 //内置函数 strlen 2 //计算字符串的实际长度,不含字符串结束标准\0 3 4 #include<stdio.h> 5 #include<stdlib.h> 6 ...

  7. 2020互联网寒冬之下,作为一个Android老码农,是如何进入腾讯的?

    由于众所周知的原因,原生Android开发如今已经日渐凋敝,作为一个Android程序员,不仅要会Java,Kotlin,JavaScript,Css,Html,还要会Flutter,C++,FFmp ...

  8. .NET Core/.NET5/.NET6 开源项目汇总12:WPF组件库2

    系列目录     [已更新最新开发文章,点击查看详细] WPF(Windows Presentation Foundation)是微软推出的基于Windows 的用户界面框架,属于.NET Frame ...

  9. 『Java』List Set

    观前提醒:本文内容多为入门时的学习笔记,笔记内容有些混乱!!! | | | | | | | | | | | | 泛型只能是引用类型,不能是基本类型. 如果希望集合中存储的是基本类型数据,需要基本类型对 ...

  10. Java集合框架和数组的排序(转载)

    Java集合框架(*Collection)*和数组的排序 ​ 根据约定,在使用java编程的时候应尽可能的使用现有的类库,当然你也可以自己编写一个排序的方法,或者框架,但是有几个人能写得比JDK里的还 ...