我在数据层的抽象上走了一段比较长的弯路。简单总结一下:
刚开始做简单的项目时,用简单的ActiveRecord就已经很合适了,比如CodeIgniter自带的。但是项目变大之后,特别是业务实体之间的关联变多之后马上就出现了问题,为了降低各个实体间的耦合,你需要将各种操作再抽象出一层。举个例子:你做了一个简单的用户系统,对用户的增删改查只需要对“用户类”调用相应的操作就行了。后来你增加了一个文件类,每个用户可以拥有多个文件,删除用户时需要同时删除相应的文件。为了能使删除的用户的这个操作复用,同时防止其他操作删除用户时没有删除相应文件夹,于是你不得不再对用户的删除做一层封装。我曾经试图让各种实体在构建的时候就说明与其他实体的关系,这样写业务逻辑的时候就能尽量少的受打扰。后来发现这也是错误的,因为把关系写到数据层以后,遇到特殊的逻辑就很难操作了。还是刚才那个例子:如果有一天我增加了一个分享功能,同时规定删除用户时不删除他已经分享的文件,这时该怎么办?当然有些其他绕的方法可以实现,比如分享文件的时候就复制文件到另一个特殊的用户账户下,用户读取的分享文件其实都是这个特殊用户账户的。但是系统大了之后,这种特殊方法就会多得烦人。后来在我写climbPHP时,才算是找到了一个较为合理的方法。就是系统应该以具体的业务逻辑来分模块,数据实体是从属于业务逻辑模块的。模块间的联系是通过抛出和监听事件实现的。数据实体之间不再有直接的关联。还是以刚才的背景来举例子:系统分为用户、文件、分享三个模块,用户模块接受正常的删除指令,成功后向外抛出事件。文件模块监听用户删除事件,同时删除相应的文件。分享模块也监听用户删除事件,监听到之后获取用户除了分享出去的文件以外可删除的文件,然后阻断原来的用户删除事件,改为抛出可删除文件的事件。这样的好处在于,每一个模块开发完成之后都可以完全封闭。当系统关闭共享模块时,原有系统不需要任何改动,仍然可用。不过这样做的前提在于需要有一个足够强大的事件派发中心。参见。
对ORM我没有用到过真正的项目中,一是因为有些业务逻辑太复杂,调研的ORM都不能满足性能上的需求。二是因为语法太难看,不想用。学Django的时候发现Django的数据模型层很成熟,具体特点记录如下:
1.对关联实体查询的优化。
这一部分应该是最影响数据库性能的。Django在查询有关联实体的实体时,默认时先查询主实体,然后根据关联项依次查询对关联实体。如果你查询的主实体是多个,那么会依次对每一个再去数据库查询相关联的实体。为了优化,Django提供了一个prefetch_related方法,能一次性查询所有相关联的实体,然后通过python组装。对于一对一的关联,Django还提供了selecte_related方法,这个方法使用的join来查询。
除了在查询方法上加速,Django还支持“关联对象”的延迟加载,也就是当访问关联的对象时才再次访问数据库。这一点我特别注意了一下,因为最近读了CSDN范凯的关于ORM的总结,他说实际项目中数据库的瓶颈在于IO而不是并发。将复杂的查询拆分成简单的数据库查询可以极大地提高缓存利用率,降低磁盘IO。但是Django延迟载入关联实体在实际情况下性能如何我没有考证过。
这里值得注意的是,Django在实现数据模型层的时候应该是用到了python语言本身扩展自己语法的特性。因为它直接使用了python的切片语法来实现sql的limit操作。如 users.objects.all()[0:5],在Django文档中说明了all()操作会造成数据库访问,如果没有对语法进行扩展的话,那么切片时岂不是已经对数据库进行了全表查询?
2.对查询结果的数据缓存。
对普通的数据查询来说,Djang没有提供特别的缓存方式,需要用户手工用变量来缓存上次查询的结果。对于关联数据,如果执行了select_related()或者prefetch_related(),那么通过self来读取关联实体时读取的是缓存。
3.模型的继承。
Django提供了三种继承方式,php的Doctrine也实现了类似的方式。三种继承分别是:“继承自抽象父类”,“继承自普通数据模型”,“继承变成代理”。具体意义分别是:“抽象父类只包括通用属性或者方法,本身不能生成表”;“继承后的模型对自己的特有字段生成新表”;“继承后的模型不生成新表,只是一个可以复写源模型行为的代理”。
总结:
1.关于ORM感觉已经没有太多值得讨论的了,请看CSDN范凯的 《WEB的缓存设计模式》。
2.数据层以及持久化的具体实现其实不需要纠结,目前的ORM已经足够强大,真正纠结的是业务逻辑造成的数据模型关联。也是我开篇一堆废话所说的。
3.Django数据模型层对python本身特性的使用让我很感兴趣,这在php中是没有的。下一篇准备研究python扩展自身的能力。
4.NOSQL将如何改变框架中的数据模型层?我认为没有太大改变,如上所说,数据库如何变化说到底只是数据如何持久化。框架始终关注的是如何更好的表现业务逻辑。