最近的一些想法和总结
最终放弃了做一个python版的简易数据库,而是最终转向了C++实现;没有指针计算做这种事情简直就是隔靴搔痒。当然,两个星期是不可能了,两个月都快有了吧。这个过程中产生了一些想法。
首先是关于C和C++的选择,最终是主要用C,局部实现中偶尔用一些C++。这有两方面考虑:首先从外部来讲,C的接口是最通用的二进制接口;其次从内部来讲,这一块实际上没什么和方法论相关的东西,指针和数据布局同时既是最大的抽象,也是最具体的细节。事实上也很难发现mongo的代码因为用了对象而比任何其它类似项目的代码漂亮。
这个和mongo作者们的选择形成了一定的反差。就我估计他们作出采用C++的决定恐怕只是“顺理成章”,而没有更多的考虑。对于我来说,我的想法是做一组地层操作数据的库(目标和他们不同),那么就要尽量排除任何语言特性带来的特殊性才合适。由于C++的特性一旦使用很容易四处扩散,所以能够局部使用C++的地方就变得比较有限。(当然Template(不算STL)仍毫不例外的派上了用场,只是目前比较轻量更像宏的替代品)
但这个过程中,有一些是跟洗澡水一块倒掉的婴儿。比如借助析构函数进行资源管理,这是C++的一个可以说最大的特点和优点,因为我对“采用什么”过于谨慎,并未采用,而是goto配合集中式的资源释放代码来做的最传统的做法(在linux代码里可以经常看见)。但这种做法仍然没有C++的自动化析构来的好,这就涉及到另外一个婴儿,就是异常。
若我不能保证不产生异常(这一点并不容易)、或者不能保证处理了全部的异常情况,资源释放代码就不能保证一定得到执行。另一个地方就是,把自己的错误处理与标准C错误的处理结合起来是不那么“无缝”的;而返回错误码的形式,无论怎么做,也没异常来的痛快和方便。C++式的异常管理中属于糖的部分尝起来很甜,而甜的发苦的场景却相对较少。
不过,由于这是算法密集型应用,关键部分的代码并不算很长,等到算法完整实现了,再在这两方面(资源管理、异常处理)做一下升级,换成C++的方式估计也不困难。关键是不能等以后再升级,不及时换成更可靠的方式的话,算法的逻辑一旦忘光了,将来再回头弄,时间成本一定非常高。
另一个选择我却轻率的使用了mongo的决策:即使用mmap去操作文件;这真是楞头和菜鸟才会选择的方案。mmap本身虽然和操作系统的资源管理结合比较好,但这个方案却存在巨大的缺陷(不论mmap本身的限制)。因为数据持久中间件中一个很主要的逻辑就是何时、何地、如何与硬盘打交道;这一块被隔开了,产品最初比较简化的版本虽然更好做了,但进一步拓展功能、引入更复杂的逻辑时,即便不说面临无法逾越障碍,也会使得能选择方案变少。
如果说数据库这类东西有什么业务逻辑的话,那么这等于是因为最小的技术选型影响了业务本身。这就好比“网上商城因为选择了Java而不得不改变送货形式”一样荒谬可笑,但由于mmap不提供细粒度接口,确实可能产生这样的影响。这一块最后肯定要改,时机应该是引入更复杂的“业务”如日志等功能之前。再那之前换成自己的映射管理层都不会有很大的工作量(刨除管理层本身工作量)。
预期的形式应该是部分借用mmap优点作为辅助工具,真正用于持久化的操作由自己的管理层完成。mmap还能用到的一些特性,比如共享内存、临时数据存放,还有特殊功能的实现等等,都可以采用;但也仅限于此。开始采用mmap的时候,并不是因为盲目相信mongo的选择,而是因为个人认为大不了自己实现一个类似mmap的驱动,有相同的接口,但有更多的其它控制方式。但这个方案面临的风险并未进行评估:会不会连锁反应把更多的逻辑带入内核层?
最后是一些题外话。C++ 0x引入lambda真TM可以说是鸡肋。关键是只有stateless的lambda才可以被(像函数指针那样)来回传递使用(而且VC2010还不支持这个)。这说实话是没法子的事情。因为缺乏自动化的内存管理,C++的运行时没法决定何时释放占用的资源。如果说一个lamda引用的全部资源可以通过delete lambda1来释放,那么两个lambda同时引用的资源至少需要一个引用计数了,或者极其复杂的用户自己控制的管理办法。
在目前这种情况下,引入lambda虽然不能下结论说一个草率的决定,但并非一个合适的机会。0x的草案里本来有可选的内存管理选项,最后被大大的简化了。将来这一块完备了,真正可用的lambda、作为能够安全的携带闭包的糖,才能变得甜起来。可是考虑到C++委员会要考虑的那么多事情,这恐怕又得等10年以后了哎哎。想到这个真有做个GCC扩展的冲动,不过,别再给自己添事了,唉。
回头说到实现自己的数据底层,这个事情本身到不缺乏理由。最明显的理由是Mongo的算法实在太一般,依赖于这样一个“优秀”开源产品实在是心有不甘。虽然自己实现也不能一步到位,最初在功能性上肯定还不如人家的、性能上也未必超得过,但未来升级扩展的可能性却完全独立的掌握在自己手里。嗯正好这块还有些不一般、别人也没实现的东西,否则真不如用人家的算了。
另外一方面,如上面提到,我的目标是一个灵活的、可以以低成本重新组装其中元素的的一个库,而不是一个盒装产品。试图去覆盖从嵌入式到云计算的各种需求对于单一产品而言显然是不合适的,但是对于其中功能拆的比较分散的库就完全不同了。