-
Notifications
You must be signed in to change notification settings - Fork 8
数据库与模块化
这部分其实我不是特别满意,但目前没有特别好的idea,欢迎大大给tips!
这个问题之所以成为问题,是因为其实我真正使用数据库是从NoSQL开始的。虽然我longlong ago想学php和mysql,但由于没有找到好的资料而放弃了(老实说现在很庆幸当初时间花在HTML和CSS上面,而不是被叫做全身都坏掉的
php)。不过幸运的是,我对数据库有了直观的概念:其实就是数据仓库。
然后我开始真正使用数据库是写oj的时候。我那时候用MongoDB和flask来搭一个oj,而且核心的部分其实已经搞好了,为了逃离oj无穷无尽的表格,我刻意回避表格,用DIV+CSS来。虽然最后的下场还是发现表格其实是oj展现资料的最好方式,而且还是回到了表格。以及最后我觉得整个oj根本没有经过充分考虑,越来越渣,没有价值,到最后全部放弃。
但我对NoSQL还是相当有好感的,太方便了,而且如果是效能不敏感的场合,其实会大大提高开放效率。以至于我当初实现的时候第一反应是Zodb或者MongoDB之类的。那时候我很激动地认为,NoSQL将会干掉SQL,我们的计划是长远的计划。
但在和gcc交流的过程中,我的想法改变了。猛然发现自己太嫩了……我的信件里提到:
sql乃至mysql我感觉都是前途未卜的东西(虽然nosql也是)……这个我是尽量偷懒, 能简单的就简单……然后用oodb比sql舒服太多太多了……而简单意味着容易扩展和开 发快速……
而gcc大大回复说:
mysql是业界的标准,已经不存在前途一说了,以前我也抵制,并尽量使用NoSQL类的 简单key/value DB,理由是sql语言太复杂,或者说潜意识里抵制大众化与古老的东 西,但后来我反而去研究其实现,学习其进化与io折衷。Anyway,SQL/NoSQL两者会 根据用途不同而生存下去。当然,像django那样做好class到db的ORM,数据库用什 么都无关紧要。
其实我那时候还不懂什么是ORM,也不理解gcc说的用途不同
。但我显然发现了其实我堕入了gcc说的潜意识里抵制大众化与古老的东西
。至此,我就发现自己的考虑其实不是那么充分了。
数据库选择的终结来自cypress
目前argo算了一下至少有223万个帖子,精华区也是200多万,邮件有100多万。。。 oodb未必扛得住,但是可以写一些obj封装mysql的接口,类似ORM那样,逻辑层可以 不用理会数据层的具体实现。 目前数据库部分我设计好文章列表和版面表,然后准备写ORM。
至此,我已经接受SQL了。而且开始好好去琢磨SQL……
如果说一定要SQL的话,pythoner很多时候都会选择ORM——把SQL伪装成NoSQL的家伙。
但写的过程中我发现问题很大。我们的ORM其实没有经过仔细的设计,基本就是一个名字一个类,很乱,搞到最后我常常忘记某个接口再哪里。而且我发现我们的orm其实不是纯粹的orm……一般的orm都是直接面对数据库,比如查询、插入对应这类的,而我们的其实很少这方面的重用,反而是把一个个的函数,比如get_post
,add_post
,add_user
这类的函数和class绑在一起。
在经过无数次的重构,参考了django的orm和SQLAlchemy后,最后的结论是,放弃orm了。为了重用,所写的orm会越来越像django的orm或者SQLAlchemy,但功能和效能上却又不如,这导致其实我们为了重用和效率而搞的orm根本没有意义。另外一方面,orm往往是内部
用的,而现在其实是在写很多的接口(为了重用底层,下面会提到),几乎不可能把orm暴露出来。但实际上,orm对重用的作用其实又不大。
于是,orm的方案推掉。我们的model是为了实现模块化 ,而不是快感。
这里就要说一下我们的抽象的层次:
web | telnet
ORM(Model)
MySQL | Memcache
首先底层是MySQL和Memcache(话说那时候都没发现cypress大大已经把memcache考虑进去了)。然后是ORM,也就是后来说的用于模块化的层。最后是telnet和web共用Model的接口。
现在问题在哪里呢?MySQL和Memcache是已经写好的,我们其实就是用就可以了,没有太多的设计上面的问题。而web和telnet调用Model层的接口,也就是其实对数据存读这一块,也没有太多的设计问题。于是问题聚焦在Model上面。事实上,我们那时候没有意识到这个是个问题……导致我为此几乎花了一个星期。
Model有多复杂呢?经过十几年的发展,其实数据的存储变得很混乱和不对称。比如,需要add_board
,add_section
,add_post
,add_user
,然后还有edit_board
,edit_section
.... 这些其实还好,因为可以看成是 add_*
和edit_*
。但我们的board还有各种各样的权限,读发帖限制,比如我们有校内版块,另外还有我们的精髓十大等等。此外,为了更好地发挥效能,我们还得做cache ……
在Model层,我倒觉得重用不是很重用了。因为Model层就是给上面的web和telnet重用的,而下面又是底层的SQL和cache 。保持直观,甚至是硬编码我都可以接受。但一定要模块清晰,哪个接口在哪里,是什么样子马上能找到或者猜对。事实上,可以重用的add_*
怎么硬也就是四个不同的update(当然最后还是有很多重用了)
在多伦纠结以后,确定了现在的模块化方案。
- 一个model表示一系列的函数接口的封装
- 一个model对应一些基本的功能,一般地,对应与一张表的操作。
- 传入参数,传出字典(而不是包装后的类)。即全部操作直接对应model里面的方法。
- model最后统一在manager里面实例化,需要被调用的model绑定到manager里面。
- 一些高层的功能另外用model来揉合(调用胶水层)
比如,现在我们有:
- UserInfo(对应用户表)
- Board(对应讨论区表的操作)
- Section(对应于讨论区分区)
- Auth(通过UserInfo来实现认证)
- Online(在线人数、版块在线人数,用户状态等)
而在manager中,1是不公开的,而2,4,5可以调用,另外4会调用到1。酱紫,我们对接口应该丢到哪里就很容易确定了。首先,我们是对什么来操作,或者是什么功能。
然后丢进去。然后是模块化和层次非常鲜明。比如以后我们需要搞站内信系统了,那就加个Mail的Model然后全部接口丢进去。
注意到auth的地位非常特殊。比如add_post,简单地看,就是插一条数据到数据库,但其实不是。在插入数据库后,我们还得看看是不是要参与十大排名啦,更新一下这个author的最近文章啦,如果有@,还得去Message模块发一下通知。
我们的方案是提供一个公共的胶水Model,底层的Model不变,然后有需要就修改胶水层。而auth显然就是这样的一个胶水层。一般地,也只有胶水层会公开。
像上面的例子,Mail的接口加完后,再到胶水层里面加一下,这样就可以很好地控制好每个Model。而胶水层无论怎么变化,总有写好的底层在被调用。
再比如以后要开放API了,就稿多一个胶水层/用另外的manager就好。
另外, = = manager不是安全的,最后还是很容易直接对数据库操作,需要用代码的人直觉。按照python的理念,作为一句逃避我们没有办法解决这个问题,“坏的习惯应该避免而不是杜绝”。另外我们写代码的人不会很多也不会恶意性地破坏。
最后其实我觉得这样子还是会有许多不足,但现在没有很好的idea,就先凑合着吧。任何的设计都有局限,我们的目前的做法可能不是很好的做法,但至少能满足我们的需求。如果有更好的idea,欢迎拍砖。
现在,我们用 MySQL来做主要的数据库存储,用redis来做辅助和cache 。而Model方面已经搞定了UserInfo、Board、Section,都是赤裸裸的接口,从名字就可以看出作用甚至参数的那种接口。等telnet搞好了,意味着全部可用的Model都搞好了,而web端纯粹就是理清逻辑以后调用各种API和搞定外观即可。
而这些接口,都是我对着telnet下面的功能键说明
,一个个地加的。chaofeng对设置快捷键十分简单,所以上,从技术层面上可以放心不会对习惯有太多的影响。
我们的计划是在6月出demo,然后是测试。而web端的新版本是在8月,telnet测试了两个月后才能上线。那时候将做数据迁移工作。注意到model的sql设计是cypress开始的,而他其实参与了phpbbs的开放,而且了解原来的实现,也就是数据的迁移不用担心。但我担心我会由于军训、期末各种原因而跳票 = =
数据迁移的话,估计是用C或者python或者其他写好迁移程序,一次性的那种就好了。不过这个程序估计也得写好一会。然后就是扫啊扫,导入数据库。
有意思的是,在实现我们的电子公告版的时候,我在chaofeng里面加了一个Animation ,先定义好每一帧的内容,然后可以‘播放’ 。为此我打趣说,在数据库迁移的时候我们搞个大电影,一登陆进来就煽情賺眼泪,然后后台慢慢同步数据。但cypress说数据迁移就是各种扫.DIR扫文件,整个过程就十分钟,只有这十分钟的内容没有同步。但……我不是很相信他。