“让家里的骡啊、马啊这些大牲口都歇了吧!”
让大象歇着吧,让蚂蚁来干大象的工作!
用嵌入式数据库实现大型数据库的功能,这感觉就像是把大象辞了,让蚂蚁来接大象的工作。听起来不可思议,但这就是我要做的工作——在应用服务器上用SQLITE替换Oracle一类的商业数据库。这个Idea很疯狂,不知道是哪个疯子想出来的。
这是一个非常平淡无奇的项目,如果数据存储用的是Oracle而不是SQLITE的话。和往常一样,我负责了数据库相关的开发。和往常一样,我用1天时间完成了所有的工作,然后开始休息,看着别人干活。一切看起来都是那么美好,我剩下的工作就是在心情好的时候做些优化,只要在他们的工作完成前完成就可以了。
在项目开始一个礼拜后,问题出来了。SQLITE在Window平台上以多线程运行时非常不稳定,经常出现数据库死锁问题。查了官网的资料,冒似不可能有这种问题。也就是说,没有提供解决方案。剩下的一个月的时间里,就开始研究死锁问题,郁闷了一个月,终于又可以休息了。
休息了两天,前台应用的人开始吵架了:一个说数据写到库里了,一个看不到数据。吵到了我这里,确认大家的应用都没有问题,但是确实出现了非常诡异的数据丢失问题:大批量的写入数据,大批量的丢失数据,而且不报任何错误。DeadLine已经来临了,郁闷ing。
下载了JDBC源代码,研读ing,研读ing,测试ing,测试ing,源代码中多这种情况似乎早有估计,已经作了预防性质的特殊处理,但是,但是,“线程是灾难”。多线程并发,总是会出现些不可思议的问题。
描述一下现象吧:
起500个线程,每个线程都做大量的随机读&&写数据库操作。测试程序运行的同时用二进制工具sqlite3用SQL语句查询数据库中的记录状态。在开始阶段,通过线程写入数据库的数据都可以通过sqlite3在命令行下读出来。在线程运行后期,写入的数据可以被其它线程读出来,但是用sqlite3读不到。数据库临时交换文件不断增长,直到不再变化。在运行了一段时间以后,数据储存文件和临时交换文件都不再更新,此时线程中反馈的信息显示数据还在被大量的写入数据库。
在最后一个线程结束前,都可以在线程中通过Select查出其它线程写入的数据,没有发现数据丢失现象。比如,期待所有线程总共往数据库中写入2000条数据,在最后一次查询时还可以看到数据库中有2000条新增数据。
当所有线程都运行结束时,用sqlite3察看数据,大量的数据都被丢失了!飞了!没有任何出错提示!
仔细分析了线程运行的过程,发现有一个线程曾经报了一个无法提交数据库的错,往后就再也没有任何出错提示了。分析了源代码,估计是由于:默认状态下,SQLITE运行在自动提交模式下。但是,在完成SQL语句的执行后,并没有像标准的JDBC那样直接把数据写入到数据存储文件中。如果在insert操作进行的中途,有一个select操作到来,并且出现异常,就可能导致刚才insert的数据被丢失。针对这种情况,SQLITE作了一些特殊的处理,理论上可以避免数据丢失问题的出现(源代码的注释里有说明)。但是,在多线程情况下,这种实现机制还是有问题,仍然有可能出现数据丢失的问题。
拍着脑袋想了n久,试验了一种解决方案,似乎有点歪门邪道的方法,不过好歹是解决了丢失数据的问题。至于是不是还会带来其它的问题,那就等端午假期结束以后再说吧。
觉得自己太天才了,呵呵!