记一次ORM的权衡和取舍
面对ORM的选型,有些人是根据自己熟悉程度来评判,有些人是根据他人的推荐来抉择,有些人觉得都差不多,随便了。当自己要真正做选择的时候,以上的这些依据都无法真正说服自己,因为不同的业务需求,不同的团队构成都会造成选型的差异,而且特别大,这里谈一谈自己的选型。
1.1需求背景介绍
- 我所在的公司是做互联网产品,对性能有着极致的要求;
- 后台人数也不算多,尽量人尽其用,技术水平呈梯度分布;
- 因为产品需要长期维护,所以对代码质量要求非常高,必须做评审和单元测试;
- 对技术的可维护性,可扩展性要求很高,因为资源有限;
- 因为资源有限,所以在生产力和性能之间需要做一个平衡;
1.2需求分析
我们知道没有完美的技术,鱼和熊掌无法兼得,所以我们必须择优录取,这里的复杂度在于你要对所选择的技术优缺点有一个360度的环评,最好能展示各自优缺点对比一览图,最后依据数据来证明你的选择对的,是对团队和业务负责的。
不知道你有没有呆过这样的团队:
- 选什么技术从不讨论,大伙各自闷声干,等干出来你才知道原来对方是用的IBatis.NET。
- 你是后面入职的,你只能在前人的基础上做维护,尽管你觉得这个技术不是最优的,但是你也无法了解当初为什么做这样的选择。
- 在入职后,没有规范的技术培训和规范,你一边Google一边编码一边骂娘。
所以对需求的分析和选型在前期特别的重要,没有对比的选择,感觉活得有点不明不白,你无法说服自己,更无法说服团队和领导。
根据互联网业务特点和团队结构,我们的选择有几个重要的关键词:
- 高性能
- 易使用
- 可扩展
高性能就不用说了,互联网产品,毫秒必争!
易用性和易维护相似,希望能在后续运维过程中不要给团队造成维护的困难,同时也遵循简单原则,高级的东西都有简单的特点。
可扩展面对的是产品的变更,很难想象高性能,易使用但是扩展性很差的产品,这种瑕疵会造成代码的臃肿和腐朽。
所以,这个权衡的标准就出来了,性能、易用、扩展。
1.3备选方案
这里的备选方案由于精力和时间的关系,根据经验和评论只罗列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度环评,架构人员、开发人员、运维人员、测试人员不妨都请过来参与一下。
架构人员首先给出自己的备选方案,然后举行备选方案评估会议,再根据会议结论修改备选方案文档。
有些团队主管或者叫技术经理一人就包办了需求分析、方案选型等工作,虽然这种方式效率很高,但是对团队开发的推进和向上汇报其实是很不利的。首先团队会觉得你大包大揽,黑箱操作;领导会觉得你做事没有章法和原则,万一你离职了,你留下的后遗症和黑锅需要你来背,也许你会觉得我都离职了,关我上面事!
我觉得你的技术牌子首先就砸了,你的影响力和同事的相处能力也消减了,你的分享能力和做事风格也就丢分了,这些无形的资产会在将来某一个时刻带你带来晦气。
由于精力有限,我没有对这些ORM进行很好的压测和提供压测数据,所以提供的是网友的压测结果,由于压测和环境配置以及框架本身版本息息相关,所以这里的数据仅供参考:
(图片来源)
结论:Dapper+扩展
根据人气、性能和易用性,我选择了Dapper。EF Core的性能和精通的门槛是我抛弃它的原因,而SmartSql尽管设计理念是我的最爱,但他的人气和坑是我担心的点。因为无法鱼和熊掌兼得,所以只能根据自己的情况进行取舍,但是你无法开怀,因为你要包容Dapper带给你的不足,而这些不足,我个人选择单元测试来弥补;同时对Dapper的进一步封装和优化也是接下来很重要的工作:比如AOP拦截代替到处都在的Transaction等等,正式的工作才刚刚开始……