zoukankan      html  css  js  c++  java
  • 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<IQueryable>(source, "source");
    	Check.NotEmpty(ordering, "ordering");
    	ParameterExpression[] parameters = new ParameterExpression[]
    	{
    		ParameterExpressionHelper.CreateParameterExpression(source.ElementType, string.Empty)
    	};
    	//果真String转Expression,还没有缓存,是快不起来了...
    	IEnumerable<DynamicOrdering> 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<TResult>(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<TResult>(query, _queryModelGenerator, _database, _logger, _contextType));
    
                return compiledQuery(queryContext);
            }
    
    

    看一下query的结构

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

  • 相关阅读:
    PowerDesigner导出word表结构
    Java
    Java
    Java
    Java
    Java
    Java
    Java
    Java
    Java
  • 原文地址:https://www.cnblogs.com/Imaigne/p/10573767.html
Copyright © 2011-2022 走看看