C#大型电商项目优化(二)——嫌弃EF与抛弃EF
上一篇博文中讲述了使用EF开发电商项目的代码基础篇,提到EF后,一语激起千层浪。不少园友纷纷表示:EF不适合增长速度飞快的互联网项目,EF只适合企业级应用等等。
也有部分高手提到了分布式,确实,性能优化从数据库出发,初期就加索引,然后垂直拆分,水平拆分,读写分离,甚至是分布式事务,阳春白雪,格局很高。然而笔者希望通过渐进的过程来优化这个项目,我们缩小格局,从细节查看不同方案的优劣。
之前提过,使用EF最主要的原因是项目时间紧迫,EF搭建速度快,熟悉的同事也多,使用方便。这个决策确实帮助我们挺过了初期的难关。在业务量增长的过程中,一些问题也逐渐暴露出来,我们开始针对问题做优化。
问题1:部分请求响应缓慢,影响用户体验。
使用EF做数据的增删改查,一些不规范代码也会拖慢程序效率,笔者在上一篇中已经提过。某些请求中可能包含多次数据查询与更新,如果这些细小的问题都以低效运行,那这个请求确实会很慢。然而在EF的框架下优化它,也未必能收到明显成效。以更新商品销量为例,我们上方案与代码:
方案1
先查出某一条数据,然后填入新算出的销量,再更新数据库,代码如下:
//先查出数据,再更新
var productSKU = unitOfWork.ProductSku.GetByID(dtos[].Items[].SKUId);
productSKU.SalesCount = productSKU.SalesCount + dtos[].Items[].Quantity;
unitOfWork.ProductSku.Update(productSKU);
unitOfWork.Submit();
其响应时间如图:
方案2
采用IQuryable,之前提到过,这种查询方式不会真的将全部数据加载到内存,代码如下:
//2采用IQueryable查询更新
var productSKU1 = unitOfWork.ProductSku.Get(p => p.Id == dtos[].Items[].SKUId);
foreach (var item in productSKU1)
{
item.SalesCount = item.SalesCount + dtos[].Items[].Quantity;
unitOfWork.ProductSku.Update(item);
}
unitOfWork.Submit();
其响应时间如图:
方案3
直接使用SQL,简单粗暴,代码如下:
//3直接使用sql更新
string updateSql = @"update ProductSKU set SalesCount=SalesCount+" + dtos[].Items[].Quantity + " where Id='" + dtos[].Items[].SKUId.ToString() + "'";
unitOfWork.ProductSku.ExecuteSqlCommand(updateSql);
unitOfWork.Submit();
其响应时间如图:
我们来分析下这三种方案:
方案1简单易懂,将数据查出来,更新后再塞回数据库,逻辑清晰,代码更清晰。可是将数据取出来,加载到内存,再对内存里的数据进行修改,最后生成SQL送回数据库执行,这套走下来,好像兜了一大圈,其实我们只想更新个销量。
方案2显得好了些,使用了我们在前一篇中讲到的理念,不将数据整个加载到内存,只到用时才加载,通过这种方式更新,其实生成的SQL和直接执行SQL差不太多了,但是我们可以发现:方案2和方案1时间差不多。在笔者预期中,方案2应该是比方案1好的,至于时间差不多的原因,应该是方案2用了一次循环,第二次循环时不符合条件,然后跳出循环,造成了些许时间浪费。如果是批量更新销量,方案2必然比方案1优秀很多。
方案3自然是简单粗暴,一条SQL搞定,毫不拖泥带水,其效果也是喜人的,几乎比方案1快1倍!
之前有园友提到索引的问题,其实我厂有专职DBA,不仅为数据量较大的表添加了聚集索引和非聚集索引,还写了定时任务去更新索引。所以索引这块我们不深究。
早起追逐开发效率阶段,我们可能将方案1优化为方案2,然后继续做新功能开发。但是现在我们有更多的选择,我们也有更多的时间去深究用户体验与系统效率了,那么自然的,我们开始嫌弃EF了。但是开发到这个程度,再去更换框架似乎不合适。而且大道至简,如果能用SQL搞定,那必是极好的。于是乎,我们开始对关键部分业务代码做重构,替换为原生SQL。这似乎是抛弃EF的开端。
问题2:部分数据量很大的表需要分表,EF难以维系
EF是ORM框架,映射数据库对象,然而同一数据库的一张表被拆分为两张以上,EF似乎没法映射了,两张表字段完全一致,难道再写个Model?而且分表是个动态的过程,也许一年分一次,也许一个月分一次,而且可能是定时任务去执行的,总不能一分表就改代码。自此,我们和EF的矛盾激化了。
园子里有关于数据库拆分的博客,我们所要改的只有数据访问层,最好不要动业务层。而且我们上文也提到:用原生SQL。那么我们就用原生SQL重写数据访问。
这里举个例子,比如我们拆分订单表,按年拆分,通常用户只会查看当年的订单,所以主表的查询次数肯定比其他分出的表要多,如果有用户要查往年的订单,我们再将查询范围扩大。按照这样的理念,我们开始重写数据访问层,并按照以下要点执行:
1.使用原生SQL
2.添加日期参数,如果日期超出主表的范围,则开始连接查询
3.查询中也可以添加其他条件,将参数作为对象填入SQL
4.抽象出一个生成SQL的公共方法,方便大家调用
拼装SQL是一件极其考验基本功的事,这个公共方法是我厂一位大师级的数据专家抽象出来的,大家也可以按照这个思路尝试下,一个简化版是很容易做出来的。
至此,项目中重要的业务功能已经和EF脱离关系了,我们也欣喜地收获了SQL带来的效率。而其他功能模块中,没有高并发场景或并不常用的应用,我们并没有做重构。
尽管本文标题是嫌弃,但如果是企业级应用,需要兼顾开发效率,且没有互联网模式下的业务量激增状况,笔者仍然推荐使用EF。
C#大型电商项目优化(二)——嫌弃EF与抛弃EF的更多相关文章
- C#大型电商项目优化(三)——扩展性与支付
上一篇文章引来不少非议,笔者并非对EF有看法,而是针对不同的业务场景和框架背景,挑选不同的方案.每个方案都有其优势劣势,挑选最快速,最简单的方案,是笔者的初衷. 看评论也是学习的过程,然而有些只做评价 ...
- C# 大型电商项目性能优化(一)
经过几个月的忙碌,我厂最近的电商平台项目终于上线,期间遇到的问题以及解决方案,也可以拿来和大家多做交流了. 我厂的项目大多采用C#.net,使用逐渐发展并流行起来的EF(Entity Framewor ...
- Spark大型电商项目实战-及其改良之番外(1)-将spark前端页面效果高效拷贝至博客
Spark大型电商项目实战-及其改良这个系列的时间轴展示图一直在变....1-3篇是用图直接表示时间轴,用一段简陋的html代码表示时间表.第4篇开始才是用比较完整的前端效果,能移动.缩放时间轴,鼠标 ...
- vue大型电商项目尚品汇(前台篇)day01
学完vue2还是决定先做一个比较经典,也比较大的项目来练练手好一点,vue3的知识不用那么着急,先把vue2用熟练了,vue3随时都能学. 这个项目确实很经典包含了登录注册.购物车电商网站该有的都有, ...
- Spark大型电商项目实战-及其改良(2) RDD优化效果不稳定的真正原因
首先看没有map join的第2任务: 时间线如下 接着是对应id的算子计算时间表 Stage Id Description Submitted Duration Tasks: Succeeded/T ...
- Spark大型电商项目实战-及其改良(1) 比对sparkSQL和纯RDD实现的结果
代码存在码云:https://coding.net/u/funcfans/p/sparkProject/git 代码主要学习https://blog.csdn.net/u012318074/artic ...
- vue大型电商项目尚品汇(前台篇)day02
现在正式回归,开始好好做项目了,正好这一个项目也开始慢慢的开始起色了,前面的准备工作都做的差不多了. 而且我现在也开始慢慢了解到了一些项目才开始需要的一些什么东西了,vuex.router这些都是必备 ...
- vue大型电商项目尚品汇(前台篇)day04
这几天一直都在做项目,只是没有上传上来,即将把前台项目完结了.现在开始更新整个前台的部分 一.面包屑处理 1.分类操作 点击三级联动进入搜索产生面包屑,直接取参数中的name即可 点击x怎么干掉这个面 ...
- vue大型电商项目尚品汇(前台篇)day05
紧急更新第二弹,然后就剩下最后一弹,也就是整个前台的项目 一.购物车 1.加入购物车(新知识点) 加入到购物车是需要接口操作的,因为我们需要将用户的加入到购物车的保存到服务器数据库,你的账号后面才会在 ...
随机推荐
- html5手机移动端三级联动城市选择器
//我的地址 var area1 = new LArea(); area1.init({ 'trigger': '#demo1', //触发选择控件的文本框,同时选择完毕后name属性输出到该位置 ' ...
- JMeter—后置处理器(十)
参考<全栈性能测试修炼宝典JMeter实战>第六章 JMeter 元件详解中第五节后置处理器后置处理器是用来处理采样器发送的请求后得到的响应数据 一.Debug PostProcessor ...
- log4net写入日志到sqlserver数据库
1.添加log4Net配置文件log4net.config 配置文件属性设置为: 配置如下: <?xml version="1.0" encoding="utf-8 ...
- CentOS乱码解决方法
linux 中文显示乱码解决办法, 其实是有多种情况的, 有一部分是由于终端默认的设置造成的 vi /etc/sysconfig/i18n 将内容改为LANG="zh_CN.GB18030& ...
- 【PAT】1083 是否存在相等的差(20 分)
//这题不是我耍流氓,实在太简单,只能直接贴代码了,凑个数 #include<stdio.h> int aaa[10005]={0}; int main(){ int N;scanf(&q ...
- rows的参数
ds.Tables[0].Rows[0][0].ToString()中的rows后边的那俩参数分别代表什么 第一个0表示行的索引(如果是0就表示第一行,1表示第二行……)第二个0表示列的索引(如果是0 ...
- 4.5Python数据类型(5)之列表类型
返回总目录 目录: 1.列表的定义 2.列表的常规操作 3.列表的额外操作 (一)列表的定义: 列表的定义 [var1, var2, --, var n ] # (1)列表的定义 [var1, var ...
- matlab数字图像处理 入门基础
本代码基于张铮主编的<数字图像处理与机器视觉>一书. 源图片:lena A = imread ('lena.jpg'); %读入图像lena.jpg,赋给变量A %imwrite(A,'l ...
- 利用原生JS实现网页1920banner图滚动效果
内容描述:随着PC设备硬件性能的进步和分辨率的不断提高,现在主流网站逐渐开始采用1920banner图,为适应这一趋势,博主设计了1920banner图的滚动效果,代码利用了原生JS实现了1920ba ...
- day10,11-Python 基本数据类型介绍之数字与字符串(看看就好)
数字:int #字符串转换整型 a = "123" print(type(a),a) b = int(a) print(type(b),b) b = b + 1000 print( ...