Java与大型系统构建

本篇纯属扯淡,观看可能引起不适。

最近用Java写了一个DBMS,主要参照的是MIT6.830的lab实现。使用的是Java语言,感兴趣的可以去我的git repo查看。

跟我稍微熟一些的人都知道我之前是一个不折不扣的Java黑,不管是我的朋友圈还是知乎也好,每次出现Java我总是喜欢上去踩一脚。现在发现自己的行为其实是太不成熟了,纵然Java其作为一门语言有着这样或者那样的缺点,但是其能够在短短二十年内就拥有那么多的应用,说明必然有其过人之处。

Java感受

易于把控的面向对象

一个大型的软件系统,说明白一些就是一个状态机,随着时间的流逝不停的变换状态,只不过这个状态机的状态数有点多甚至是UNcountable的:)OOP就提供了一个非常好的抽象手段,将状态保存在对象内部,这样的话可以随时随地对这样的内部状态进行修改。相比之下函数是语言,如果没办法将状态封装起来甚至是无法保存状态的话,对程序员的要求就大大提高了。

Interface 好

不管是Java还是Golang,最近用interface总有一种将静态语言用成了动态语言的快感。只要规定好了调用的方法的接口,就可以在实现了这些接口的对象之间相互转换。当然,在使用interface的时候要尽可能对接口进行详细规定,否则的话类型检查的意义就会越来越trival,会将本来可以在编译期间就能检查到的错误推迟到了运行时,这对于之后的debug无疑增加了难度。这也预示着,对于安全和开发效率之间总是存在着tradeoff的。

实用的泛型

虽然类型擦除已经被吐槽了一万遍,但是不可否认Java的泛型还是非常实用的:)

易于操控的并发

对这方面涉猎不多,但是对比C和Java的多线程就知道,Java的并发写起来是很容易操控的,不像C,写着写着就不知道自己飞到哪里去了。

其实说白了,Java作为一个工业语言是十分成功的(且不论其是否优秀),严谨甚至有些呆板的语法,尽一切可能为实用服务,避免“银弹”的出现,以及易于控制的OOP和抽象层次,使得各种机构可以产生大量的Java程序员,并且公司能够控制软件的规模和可维护性,不至于前人的代码后人无法维护。

系统构建

Never doubt your machine

编译器没错,系统没有错,JVM没有错,错的永远是你的代码:)

只用接口而不直接去更改变量

其实我觉得,面向对象最好的方式还是想Smalltalk那样采用消息传递的方法。在Java中,最好的方法还是只调用成员的方法而不去直接修改成员内部的变量。因为一旦你去直接修改了成员内部的变量,这样的修改对于成员来说是不可控的,不可控就容易出事情。最好的办法就是要干什么都直接调用方法。毕竟内部实现只要靠谱,就会减少大量bug。

算法对于系统设计的重要性

之前一直觉得,在实际工程中,对于算法的重要性并没有那么大。但是在这里并不是这样的。实现一个LockManager的时候,为了检测是否有死锁,用到了图论中的算法(检测是否有环)。在自己设计的系统中很可能是没有轮子给你用的,算法的重要性也体现出来了。

Log

做完这个DBMS之后开始研究一些分布式协议的问题,发现在系统中,log是个非常重要的部分。不管是DBMS还是raft Paxos 这样的分布式系统,log都是其中的一个核心部件。log其实用途很容易理解,就像我刚刚说的那样,把整个系统理解成状态机,那么log记录的就是状态机至今为止所工作的步骤,每当client发过来一个请求时,在系统要把这个请求写入之前,必须在log中保存这记录,以防止系统直接crash了没有记录。并且在DBMS这种带有缓存的系统中,如果一个页面在写入硬盘前就crash了,那么我们可以通过log来恢复。那么又有一个问题,crash了的系统重启后必然是丢失了初始状态,只能从硬盘中读取可能是N轮之前的状态。那么我们应该从哪里开始恢复呢?这里就使用了checkpoint,每当从缓存中flush的时候,就先加个checkpoint表明位置,之后直接从最近的checkpoint开始恢复就行。

Ivan Yang 29 February 2016
blog comments powered by Disqus