Unity 容器性能优于直接数据访问 - 一个很大的不同

Posted

技术标签:

【中文标题】Unity 容器性能优于直接数据访问 - 一个很大的不同【英文标题】:Unity container performance over direct data access - a big difference 【发布时间】:2018-03-14 06:38:00 【问题描述】:

我有一个包含大约 30 个项目的解决方案,其中大多数使用 Microsoft Unity 作为容器。

对于这个测试,我在不同区域和不同网络中使用远程 Azure SQL 数据库,所以我预计会有延迟响应,但不会影响这个测试。

让我们使用 Unity 计算数据访问时间,使用 DbContext 计算直接数据访问时间,以下是平均计算时间(以毫秒为单位):

Unity Container
8749
5757
7225
7072
7256
8791
7016
7465
8449
10741
7852.1 (average)

DbContext
3599
2239
2902
2378
1898
1682
1692
1522
2773
2054
2273.9 (average)

因此,使用统一容器访问数据需要 7852.1(平均)毫秒,同时使用 DbContext 访问数据需要 2273.9(平均)毫秒。这是很大的性能瓶颈,你不觉得吗?

让我分享一些代码 sn-ps,这将展示我如何在项目中使用 Unity。

项目中的 Unity 配置如下所示:

public class UnityConfig
    
        private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
        
            var container = new UnityContainer();
            RegisterTypes(container);
            return container;
        );

        public static IUnityContainer GetConfiguredContainer()
        
            return container.Value;
        

        public static void RegisterTypes(IUnityContainer container)
        
            //// Repositories
            container.RegisterType<ICartRepository, CartRepository>();
            // .... total 50 repositories registrations ....

            //// Services
            container.RegisterType<ICartService, CartService>();
            // .... total 72 services registrations ....
        
    


public static class UnityWebActivator

    public static void Start()
    
        var container = UnityConfig.GetConfiguredContainer();
        FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
        FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));
        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        // TODO: Uncomment if you want to use PerRequestLifetimeManager
        // Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
    

    public static void Shutdown()
    
        var container = UnityConfig.GetConfiguredContainer();
        container.Dispose();
    

以下是与 Unity 一起使用的示例存储库和示例服务:

public interface ICartRepository

    Cart Get(string id);
    IEnumerable<Cart> GetAll();
    // more codes


public class CartRepository : ICartRepository

    [Dependency]
    public ApplicationData db  get; set; 

    public Cart Get(string id)
    
        return db.Carts.AsNoTracking().Where(i => i.Id == id && i.IsDeleted == 0).FirstOrDefault();
    

    public IEnumerable<Cart> GetAll()
    
        return db.Carts.AsNoTracking().Where(i => i.IsDeleted == 0);
    
    // more codes


public interface ICartService

    Cart Get(string id, string orgid);
    IEnumerable<Cart> GetAll(string orgid);
    // more codes


public class CartService : ICartService

    private ICartRepository cartRepository;

    public CartService(ICartRepository _cartRepository)
    
        cartRepository = _cartRepository;
    

    public Cart Get(string id, string orgid)
    
        return cartRepository.GetAll().Where(i => i.OrganizationId == orgid && i.Id == id).FirstOrDefault();
    

    public IEnumerable<Cart> GetAll(string orgid)
    
        return cartRepository.GetAll().Where(i => i.OrganizationId == orgid);
    
    // more codes

在我使用它们的项目中:

public class HomeController : Controller

    private ICartService cartService;

    public HomeController(ICartService _cartService)
    
        cartService = _cartService;
    

    public ActionResult Index()
    
        // through unity
        var item = cartService.Get("id", "org_id");

        // direct DbContext
        ApplicationData data = new ApplicationData();
        var item1 = data.Carts.AsNoTracking().Where(i => i.Id == "id" && i.OrganizationId == "org_id").FirstOrDefault();
        // more code

        return View();
    

这就是我们在应用程序中使用的全部内容。您发现有什么可以更改以提高性能的吗?

【问题讨论】:

您能添加用于生成测量值的代码 sn-p 吗? 我认为您没有提供“直接 DbContext”方法的代码,所以我们不知道您在比较什么。此外,缺少cartRepository.GetAll() 实现。 代码已更新,包含您需要的所有详细信息 【参考方案1】:

原因是您比较的两种方式完全不同,而不是因为 Unity。第一种方法是:

var item = cartService.Get("id", "org_id");

实现为

return cartRepository.GetAll().Where(i => i.OrganizationId == orgid && i.Id == id).FirstOrDefault()

GetAll 在哪里:

public IEnumerable<Cart> GetAll()

    return db.Carts.AsNoTracking().Where(i => i.IsDeleted == 0);

因为GetAll 返回类型是IEnumerable&lt;Cart&gt; - cartRepository.GetAll().Where(...)不会 过滤掉数据库中的购物车。相反,整个Cart 表被像select * from Cart where IsDeleted = 0 这样的SQL 查询拉入内存。然后Where 被执行并在内存中找到目标购物车(按组织和ID)。当然,这是非常低效的(因为它将整个表从远程数据库传输到您的机器)并且比另一个方法花费更多的时间:

data.Carts.AsNoTracking().Where(i => i.Id == "id" && i.OrganizationId == "org_id").FirstOrDefault();

这会产生您期望的 SQL,例如 select top 1 * from Cart where IsDeleted = 0 and Id = @id and OrganizationId = @org_id,所有过滤都发生在数据库中,然后通过网络仅传输一行。

要修复 - 将您的 GetAll(和其他类似方法)更改为返回 IQueryable

public IQueryable<Cart> GetAll()

    return db.Carts.AsNoTracking().Where(i => i.IsDeleted == 0);

【讨论】:

从 IEnumerable 转换为 IQueryable 是可行的方法,感谢您的帮助。

以上是关于Unity 容器性能优于直接数据访问 - 一个很大的不同的主要内容,如果未能解决你的问题,请参考以下文章

一种优于gzip的压缩方式Brotli

JVM详解——直接内存

ArrayDeque详解

11_直接内存

JVM 专题十三:运行时数据区直接内存

JVM之直接内存