ef mysql性能_EF性能分析(一):动态SQL性能差.从OrderBy开始分析

1. 问题背景

在我的力推下,部门业务开发转向ABP,其中ORM采用的是EntityFrameworkCore. 然而,在数据查询方面,出现了重大的性能问题...

请看代码:

//在一个百万数据量的表中分页获取十条数据居然花了180ms左右,简直不能忍。

var entityList = await query

.PageBy(input)

//这是个字符串:MonthCode desc

.OrderBy(input.Sorting)

.ToListAsync();

这是很常见的Abp示例项目中的CURD中的常规代码,被大量使用...

2.分析问题

2.1 遇到问题先猜,提高查问题效率

开始我平淡的猜测...

a. 整段代码平淡无奇,【但是OrderBy的出现】直接解决了任意字段排序的问题,简直解放双手,要知道百万数据在前端排序是不可能的。 b. 【问题只能被转移,不能被消灭】 --我的编程思想 c. 所以,问题初步定在Orderby上。

2.2 猜到问题代码,继续猜可能的原因

a. 按下F12查看函数签名:

OrderBy(this IQueryable source, ParsingConfig config, string ordering, params object[] args);

b. 开始感性的猜测,只需要一路F12即可。

1. IOderQueryable继承自IQueryable

2. IQueryable由:Type(类型),Expression(表达式树),Provider(表达式树的解析器)组成

3. Expression的构建需要涉及到元数据反射创建。

4. 反射!!!元数据!!!所以真相可能是:从SQL字符串,动态生成Expression!然后,创建IOrderQueryable,最后由EF的Provider解析出SQL,而不是简单的直接作为Orderby字符串拼接...

2.3 猜个大概了,开始证明它吧!

2.3.1 查看OrderBy源码

我用ILSpy反编译工具

public static IOrderedQueryable OrderBy(this IQueryable source, ParsingConfig config, string ordering, params object[] args)

{

Check.NotNull(source, "source");

Check.NotEmpty(ordering, "ordering");

ParameterExpression[] parameters = new ParameterExpression[]

{

ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty)

};

//果真String转Expression,还没有缓存,是快不起来了...

IEnumerable arg_48_0 = new ExpressionParser(parameters, ordering, args, config).ParseOrdering(false);

Expression expression = source.Expression;

foreach (DynamicOrdering current in arg_48_0)

{

expression = Expression.Call(typeof(Queryable), current.MethodName, new Type[]

{

source.ElementType,

current.Selector.Type

}, new Expression[]

{

expression,

Expression.Quote(Expression.Lambda(current.Selector, parameters))

});

}

Expression expression2 = DynamicQueryableExtensions.OptimizeExpression(expression);

return (IOrderedQueryable)source.Provider.CreateQuery(expression2);

}

2.3.2 初步结论:

为什么是初步结论呢,,,因为EF还有个二次缓存机制不是...热启动怎么也这么慢,是不是得查它

所以:OrderBy的时候,是由字符串,反射生成表达式树后,创建Queryable,交给EF做后续处理!所以,性能是快不起来的,这里性能大概就消耗了80ms左右!

2.3.3 开始查EntityFramework的缓存机制

其实这个阶段不用查...因为OrderBy每次都会执行生成Expression的过程,所以性能稳稳有问题,但是我真的好奇...

EFCore执行查询的源码

public virtual TResult Execute(Expression query)

{

Check.NotNull(query, nameof(query));

var queryContext = _queryContextFactory.Create();

query = ExtractParameters(query, queryContext, _logger);

//获取缓存的地方

var compiledQuery

= _compiledQueryCache

//这个query就是他的key.

.GetOrAddQuery(

_compiledQueryCacheKeyGenerator.GenerateCacheKey(query, async: false),

() => CompileQueryCore(query, _queryModelGenerator, _database, _logger, _contextType));

return compiledQuery(queryContext);

}

看一下query的结构

12e34dbf7fd492b4fa98d7fdf0e4c2ff.png

结论:EF的缓存机制是用生成后的表达式树做的Key哦,但是生成表达式树的那段代码的性能损耗是无法避免的了!所以!!!慎用!!!动态SQL!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值