记一次ORM的权衡和取舍

2023-11-06 12:08
文章标签 一次 orm 权衡 取舍

本文主要是介绍记一次ORM的权衡和取舍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

面对ORM的选型,有些人是根据自己熟悉程度来评判,有些人是根据他人的推荐来抉择,有些人觉得都差不多,随便了。当自己要真正做选择的时候,以上的这些依据都无法真正说服自己,因为不同的业务需求,不同的团队构成都会造成选型的差异,而且特别大,这里谈一谈自己的选型。

  • 我所在的公司是做互联网产品,对性能有着极致的要求

  • 后台人数也不算多,尽量人尽其用,技术水平呈梯度分布

  • 因为产品需要长期维护,所以对代码质量要求非常高,必须做评审和单元测试

  • 对技术的可维护性,可扩展性要求很高,因为资源有限;

  • 因为资源有限,所以在生产力和性能之间需要做一个平衡

难点

  我们知道没有完美的技术,鱼和熊掌无法兼得,所以我们必须择优录取,这里的复杂度在于你要对所选择的技术优缺点有一个360度的环评,最好能展示各自优缺点对比一览图,最后依据数据来证明你的选择对的,是对团队和业务负责的。

现状

  不知道你有没有呆过这样的团队:

  • 选什么技术从不讨论,大伙各自闷声干,等干出来你才知道原来对方是用的IBatis.NET。

  • 你是后面入职的,你只能在前人的基础上做维护,尽管你觉得这个技术不是最优的,但是你也无法了解当初为什么做这样的选择。

  • 在入职后,没有规范的技术培训和规范,你一边Google一边编码一边骂娘。

  所以对需求的分析和选型在前期特别的重要,没有对比的选择,感觉活得有点不明不白,你无法说服自己,更无法说服团队和领导。

分析

  根据互联网业务特点和团队结构,我们的选择有几个重要的关键词:

  • 高性能

  • 易使用

  • 可扩展

  高性能就不用说了,互联网产品,毫秒必争!

  易用性和易维护相似,希望能在后续运维过程中不要给团队造成维护的困难,同时也遵循简单原则,高级的东西都有简单的特点。

  可扩展面对的是产品的变更,很难想象高性能,易使用但是扩展性很差的产品,这种瑕疵会造成代码的臃肿和腐朽。

  所以,这个权衡的标准就出来了,性能、易用、扩展

  这里的备选方案由于精力和时间的关系,根据经验和评论只罗列EF Core、Dapper、SmartSql三种,另外一个原因是这三种设计理念完全是不一样的,差异性很大。另外有些人也会偏爱NHibernate Core(和EF雷同,不做考虑)或者SqlSuger等,不在本文讨论范围。

备选方案1:EF Core

优点

  • 强类型带来维护的安全感,一旦数据库有修改或者字段变更,编译带来的BUG提示,可用极大提升维护效率。

  • EF Core的Code first和自动迁移功能,对面向DDD的设计十分友好,对DB迁移的高效带来的体验也非常棒。

  • 领域优先的设计理念,在和业务人员沟通的过程中,优势也相对明显。

  • 喜欢写SQL的同学,不要忘记EF本身兼容原生脚本,包括存储过程,不过不是优先的选择。

  • 支持多种数据库。

缺点

  • 入门容易,精通比较难,其知识体系有点复杂,学习曲线会比较陡峭!

  • 生成的SQL需要调试和跟踪,面对多表联合查询,性能就不用说了。

  • 需要借助性能检测工具比如MiniProfiler来进行性能分析和监控。

  • “我想好了Sql怎么写,然后再来写Linq,完了可能还要再查看一下Linq输出的Sql是什么样的“。这是非常糟糕的体验。

适用场景

  在性能和生产力之间可以做很好的平衡,比如企业管理系统、个人站点或者外包项目等。

  对EF Core来说如果用的好,性能是完全可以做到非常高的,虽然不是极致的效果,但是在开发效率和性能之间可以做一个很好的平衡。

备选方案2:Dapper

  Dapper是.NET的一款轻量级ORM工具(GitHub),也可称为简单对象映射器。在速度方面拥有微型ORM之王的称号。它是半自动的,也就是说实体类和SQL语句都要自己写,但它提供自动对象映射。是通过对IDbConnection接口的扩展来操作数据库的。

优点

  • 轻量,只有一个文件

  • 性能高,Dapper的速度接近与IDataReader,取列表的数据超过了DataTable。

  • 支持多种数据库。Dapper可以在所有Ado.net Providers下工作,包括sqlite, sqlce, firebird, oracle, MySQL, PostgreSQL and SQL Server

  • 使用Dapper可以自动进行对象映射,通过Emit反射IDataReader的序列队列,来快速的得到和产生对象

缺点

  • 代码里边充斥着 SQL 和各种判断分支,这些将会使代码维护难以阅读和维护,更谈不上Linq的优雅。

  • 和EF相比,手写SQL 当修改表结构不易发现bug。

  • 习惯了EF后再来使用Dapper,会很难适应那种没有了强类型的安全感。

适用场景

  对性能有着极限的追求,同时能写一手很好的SQL(对数据库能达到DBA的水准更好),怀念SQL的感觉,习惯SQL的体验的同学。

  在可维护这块有做单元测试,很好的规避后期维护的困难。

备选方案3:SmartSql

  为什么这边没有选择NHibernate-Core?是因为这家伙和EF Core太像了,在人气上来看,完全没有必要去做选型,当然如果你的团队有NHibernate情节,对NH玩得风生水起例外。

  为什么是SmartSql而不是MyBatis?主要是Mybatis在跨平台上找到不到开源方案,几乎没有更新,更不用说跨平台了。而且SmartSql的设计理念就是借鉴的Mybatis,同时又增加了不少强大的功能,比如支持缓存、CQRS等干货,看官宣SmartSql就一句话:

MyBatis .NET Core+ Cache(Memory | Redis) + R/W Splitting +Dynamic Repository + Diagnostics 

优点

  • 因为SQL自撸,所以性能和Dapper不相上下,非常的高。

  • SmartSql 借鉴了 MyBatis 的思想,使用 XML 来管理 SQL ,并且提供了若干个筛选器标签来消除代码层面的各种 if/else 的判断分支。

  • SmartSql将管理你的 SQL ,并且通过筛选标签来维护本来你在代码层面的各种条件判断,使你的代码更加优美,你再也不用看到到处充斥的SQL了,对代码优雅有着极限追求的人会有点受不了。

  • 支持多种数据库

缺点

  • 易排查:排查性和维护性对新人来说,个人感觉不是十分友好,写SQL会考验你的细心。

  • 使用 XML 来管理 SQL个人觉得是优点也是缺点,因为代码优雅了,但是有些人并不是很感冒这种方式,特别是在XML里面的if/else的逻辑判断,不亲切。

  • 稳定性有待提升,虽然官宣对Dapper有很好的提升,但是从人气来看,成熟度需要进一步观察。

适用场景

  喜欢Dapper的性能,但是不喜欢到处充斥的SQL脚本,追求极致优雅,同时又对SmartSql的特性和效率特别欣赏。但是从人气和成熟度来看,如果对源码没有很好的掌控能力,碰到坑就不好搞了。

1.4备选方案评估和选择

  在评估和最终选型的时候,建议做360度环评,架构人员、开发人员、运维人员、测试人员不妨都请过来参与一下。

  架构人员首先给出自己的备选方案,然后举行备选方案评估会议,再根据会议结论修改备选方案文档。

  有些团队主管或者叫技术经理一人就包办了需求分析、方案选型等工作,虽然这种方式效率很高,但是对团队开发的推进和向上汇报其实是很不利的。首先团队会觉得你大包大揽,黑箱操作;领导会觉得你做事没有章法和原则,万一你离职了,你留下的后遗症和黑锅需要你来背,也许你会觉得我都离职了,关我上面事!

  我觉得你的技术牌子首先就砸了,你的影响力和同事的相处能力也消减了,你的分享能力和做事风格也就丢分了,这些无形的资产会在将来某一个时刻带你带来晦气。

640?wx_fmt=png

  由于精力有限,我没有对这些ORM进行很好的压测和提供压测数据,所以提供的是网友的压测结果,由于压测和环境配置以及框架本身版本息息相关,所以这里的数据仅供参考:

640?wx_fmt=jpeg

(图片来源)

结论:Dapper+扩展

  根据人气、性能和易用性,我选择了Dapper。EF Core的性能和精通的门槛是我抛弃它的原因,而SmartSql尽管设计理念是我的最爱,但他的人气和坑是我担心的点。因为无法鱼和熊掌兼得,所以只能根据自己的情况进行取舍,但是你无法开怀,因为你要包容Dapper带给你的不足,而这些不足,我个人选择单元测试来弥补;同时对Dapper的进一步封装和优化也是接下来很重要的工作:比如AOP拦截代替到处都在的Transaction等等,正式的工作才刚刚开始……

原文地址:https://www.cnblogs.com/jackyfei/p/orm.html

 

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 
640?wx_fmt=jpeg

这篇关于记一次ORM的权衡和取舍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/356520

相关文章

Go 在orm中使用反射

作为静态语言,golang 稍显笨拙,还好 go 的标准包reflect(反射)包弥补了这点不足,它提供了一系列强大的 API,能够根据执行过程中对象的类型来改变程序控制流。本文将通过设计并实现一个简易的 mysql orm 来学习它,要求读者了解mysql基本知识,并且跟我一样至少已经接触 golang 两到三个月。 orm 这个概念相信同学们都非常熟悉,尤其是写过rails的同学,对acti

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位

flume系列之:记录一次flume agent进程被异常oom kill -9的原因定位 一、背景二、定位问题三、解决方法 一、背景 flume系列之:定位flume没有关闭某个时间点生成的tmp文件的原因,并制定解决方案在博主上面这篇文章的基础上,在机器内存、cpu资源、flume agent资源都足够的情况下,flume agent又出现了tmp文件无法关闭的情况 二、

jmeter之仅一次控制器

仅一次控制器作用: 不管线程组设置多少次循环,它下面的组件都只会执行一次 Tips:很多情况下需要登录才能访问其他接口,比如:商品列表、添加商品到购物车、购物车列表等,在多场景下,登录只需要1次,我们期望的是重复执行登陆后面的接口来做压测,这就和事务相关,例如 事务1: 登录—>添加购物车 事务2: 登录—>购物车列表 事务3: 登录—>商品列表—>添加购物车 … 一、仅一次控制器案例 在

一次生产环境大量CLOSE_WAIT导致服务无法访问的定位过程

1.症状 生产环境的一个服务突然无法访问,服务的交互过程如下所示: 所有的请求都是通过网关进入,之后分发到后端服务。 现在的情况是用户服务无法访问商旅服务,网关有大量java.net.SocketTimeoutException: Read timed out报错日志,商旅服务也不断有日志打印,大多是回调和定时任务日志,所以故障点在网关和商旅服务,大概率是商旅服务无法访问导致网关超时。 后

关于一次速度优化的往事

来自:hfghfghfg, 时间:2003-11-13 16:32, ID:2292221你最初的代码 Button1 34540毫秒 5638毫秒  Button2 我的代码 这个不是重点,重点是这个  来自:hfghfghfg, 时间:2003-11-13 16:54, ID:22923085528毫秒 不会吧,我是赛杨1.1G  128M内存  w2000, delphi6  128M

一次关于生产环境服务无故宕机的排查过程

故事的开始 这个故事是在一年之前,当时我们的系统运行在客户的k8s环境上。然后很神奇的是每个月底我们都会服务宕机,当然我们开启了多个实例。当时的容器线条就像心跳图一样(或许有些描述的不太准确,我没有找到当时那个像心电图一样的容器资源监控图)。 第一次的排查 当时我们还是很有信心去解决这个问题的。由于每个月的月底都是业务使用的高峰时段,也就是说,从表象上来看,qps一高,容器就挂。 业务日

第二十二章 rust数据库使用:sea-orm详解

注意 本系列文章已升级、转移至我的自建站点中,本章原文为:rust数据库使用:sea-orm详解 目录 注意一、前言二、项目管理三、迁移文件四、实体文件五、业务使用 一、前言 只要开发稍微大型一点的项目,数据库都是离不开的。 rust目前并没有特别成熟的数据库框架,sea-orm这个框架是我目前所看到的成熟度最高的一个,并且仍在积极开发中。 所以本文将以sea-orm框

记一次knife4j文档请求异常 SyntaxError: Unexpected token ‘<‘, ... is not valid JSON

knife4j页面报错问题定位 前几天开发新接口,开发完成后想使用knife4j测试一下接口功能,突然发现访问页面报错提示:knife4j文档请求异常,但之前运行还是正常的,想想会不会与升级依赖有关系,启动其他微服务发现文档接口访问正常,排除因依赖版本升级导致在线API文档无法使用情况,还是和本服务新增接口有关系。 定位问题 首先f12打开调试台,重新刷新页面,看到console有报错提示

记一次项目启动报错问题

今天遇到了一个问题,困扰了我几个小时,虽然最后是一个小问题导致的。记录下,也算一个解决问题的方法。   前提:调用webservice时引用 <dependency><groupId>org.codehaus.xfire</groupId><artifactId>xfire-all</artifactId><version>1.2.6</version></dependency>