cache_timing_db基于 Redis 的简单时序数据库实现
cache_timing_db是基于Redis的一个时序数据库实现,支持高效的查询时间分布的数据,提供数据的加载器和查询器,能够实现自动化的数据加载和高性能的查询。
背景
在我负责的一个系统中,有一个数据的同步表,每天都会同步几万的数据,一定时间下来,表已经累积了几千万的数据,这些数据实际上是十万个站址的电量统计,平常没用到觉得没什么,后面来了一个实时性较强的需求,需要通过站址号、电表号、地址编码以及时间来查询一个范围的数据,即使加了索引查询效率依然没有达到实时的要求,后来发现系统的Redis内存申请的还算大,就打算用Redis来缓存这部分数据。主要考虑数据在Redis如何存储、如何高效的加载进Redis以及如何查询等问题,因此开发了cache_timing_db。
设计思路
数据特点 :数据有个特点,就是一个站址号下有很多条数据,这些数据是按照天来分布的,因此可以考虑一个站址的数据就放到一个Redis的数据结构里面。
存储实现 :使用Redis的列表来实现,用户通过配置一个起始的日期时间以及一个单位偏移时间就能实现列表不同的下标存储不同时间的数据,查找时只需要知道起始时间和单位偏移就可以计算出要查找的数据的下标。当然有缺点,比如日期范围内有很多时间点没有数据就会造成存储的浪费,因为即使没有数据也需要Redis开辟一个空节点,空节点也会占用一定的内存,因此这种数据结构设计不能存储过长时间的数据。如下图:这就是一个站址号对应的每天同步的数据,我把这样的一个列表称之为一个“桶”。
序列化方式 :采用的是Protostuff作为序列化方式,相比JSON和JDK能够更加节省内存。
加载模块实现 :由于采用定时任务加载的方式,因此使用我的另一个框架AutoJob,支持错误重试,日志记录等。加载主要分为几个步骤:
首先就是初始化桶,Redis的lset命令必须要求下标在列表的长度范围内,所以必须先给桶初始化n个节点,都不放数据,初始化桶需要保证桶原来就不存在,因为通过管道的方式初始化,所以如果桶本来存在会让桶的长度变成几倍设置初始化长度。为了保证高效性,这里使用的是布隆过滤器来进行过滤,如果布隆过滤器说桶存在则可能存在,如果说不存在则一定不存在,这也会导致一个问题:在使用管道存数据时可能有的桶没有被创建,此时会出现异常,因此此时才去扫描Redis,查找不存在的桶然后创建,并且重新load这一批数据,这样布隆过滤器可以过滤出大部分(预估配置合理的情况下)key。
其次就是保存数据,保存数据主要就是利用配置的起始时间和单位偏移来计算下标,然后放入列表。