敝公司在使用Tokyo Tyrant这个高性能的key-value DB,总的来说,性能相当不错,不过在数量达到50M之后有明显的瓶颈,以至于我们3000的TPS都有点困难了。究其原因,是因为我们用的是hash db,文件大了以后,任何一个写操作都在硬盘不同地点,于是就是对硬盘的完全的随机写,在我们的RAID10上,也只能做到100多IOPS,可想而知,效率是多么的低下了。

其实最直观的解决方案就是换SSD硬盘,比如INTEL的MLC SSD硬盘号称可以达到8500 writes/second。不过这个可能需要一些沟通和批准的工作,而且对于Engineering最有意思的就是在现有的条件下,实现最大的可能,所以,我们可以做点什么呢?

  • 不立刻写入硬盘,而是类似Cassandra和Redis那样,只写入redo log(ulog),就返回。另外在后台有一个线程,定期将dirty data写入硬盘。这个写入应该是顺序写入的,以避免随机写的问题——我们的硬盘顺序写可以有80M/s,还算不错了。
  • 这也就意味着要缓存不少的数据,为了高效,所有的写数据都应该被缓存——很可能会被读,不过现在内存应该也不是大问题,而且我们的TT跑了一段时间之后,对硬盘的读就没有了——全部被操作系统缓存了,所以读不是什么问题。
  • 写入线程要考虑如何和其他的线程一起工作——它不能锁住所有的线程,所以考虑是不是把数据库分段?可以参考以下redis之类的?
  • 删除的处理,删除了数据之后就留下空穴,那么必须要有一个defragment的线程。更新等同于删除+新增。
  • 如果该进程crash了,重启的时候要replay redo log,这个跟上一点有关系,因为可能不能得到一个完整的某一个时刻的snapshot。
  • 要能支持多台机器的master-master复制,master-slave比较麻烦,failover之后还要手动切换。应该是都是没有任何区别的master,因为他们的数据都是相同的。我们的数据特点是,量大,不过每条数据都不大,100字节足够,所以并不是很需要consistent hash。

为什么不用Redis?Redis要求所有的数据都在内存里,对于我们公司的应用来说,有时候有点困难——想象如果有1亿条记录,光hash的bucket(64 bit)就要0.8G,用mmap还是靠谱一点。最好是常用的在内存里,不常用的就待在硬盘上就好了。我们的数据大概一半以上都很不常用。

Cassandra也试了一下,不过似乎有更多的问题,它的读效率实在太低, 因为需要从硬盘load sstable,这个就是一个随机读的问题的,再用Java实现,读的TPS只有不到100. 解决方案就是使用Key cache和 Row cache,不过那又是把很多数据load在内存里,而且warm up似乎很慢。(为什么会比Tokyo慢?)