谈谈对日期的压缩存储

最近在项目中做缓存的性能优化,对热点代码/TP时间进行分析之后.发现这些性能不理想的请求,都是cache的value比较大导致的.所以自然想到的就是精简字段.并且对数据进行压缩存储.这篇文章就谈谈其中对日期的压缩处理.

字符串

最常规的日期存储方式应该就是字符串.如"20171029",那这样的字符串到底占用多大空间呢.我们可以让代码告诉我们.

    public static void main(String[] args) {
        String date = "20171029";
        byte[] dateBytes = date.getBytes();
        System.out.println(dateBytes.length);
    }

运行结果是: 8

我们可以看到,使用字符串存储的日期.要占用8个字节.如果一个数据包里大部分都是日期.那1KB的数据包只能存放128个日期.当日期很多的时候,会占用很多空间.

Long

了解Unix时间的都知道.我们可以用一个long类型来存储日期.Java里long类型占用的空间也是8byte.和上面的字符串是一样的.

Int

int在Java中占用4Byte.所代表的数字范围是 -2^32 ~ 2^31-1.用来存储日期完全够用.所以我们完全可以使用int类型来存储时间.这样下来.我们用1KB的数据包就能存放256个日期了.

到这里看起来已经很好了.但是我们发现.2^31-1 = 2147483647 远远大于99991231.也就是说这个int的前面很多个bit是空闲的.那我们有没有可能继续压缩?

Mysql如何存储日期

因为使用int存储日期肯定是肯定不是最优的.那如何知道更优的方案呢.那就是去借鉴其他人的方案.那么数据库一定是一个很好的选择.因为对于数据库来说,能存小,一定不会存大.经过查询资料.Mysql是使用3个byte来存储日期的.具体的公式是

yyyy * 32 * 16 + mm * 32 + dd

具体成二进制表示为:yyyyyyyy yyyyyyym mmmddddd

这个公式是什么意思呢.

  1. 日的范围是1~31.我们使用5个bit就足够存储.
  2. 月的范围是1~12.我们使用4个bit就足够存储.
  3. 对于3个byte的24位.还剩下15位.可以存储2^15-1 = 32767个数据.用来当年已经足够了.

Unix时间戳 + Mysql存储方式 = 2Byte

看了Mysql的存储方案,不难发现,使用3Byte确实不错.但是我们能不能做的更好,只用2个Byte.
我们可以看下.对于日和月的存储已经没有压缩空间了.如果使用2个byte.则还有7个bit供我们使用.

2^7=128.如果直接用来存年份.肯定是不够的.但是Unix时间戳还记得吗.它是从1970年1月1日到现在的毫秒差.这个相对的思路如果加到我们的设计里呢.

我们这127不用来做绝对的时间,而是作为相对的时间存储.那参照日期如果采用2017年.则可以存储到2017 + 127 = 2144.对于一般的业务系统.128年的偏移,足够我们使用了.

使用二进制来表示则日期格式为:yyyyyyym mmmddddd

而使用这种2Byte类型的存储.则1KB的数据包则可以存储512个日期.相比一开始的128.多了3倍.

总结

你可能需要知道,2M的数据和2K的数据.在网络传输中有什么区别.
才能理解这篇文章为什么会要对这种看似没什么用字符串存储进行优化.
不过没关系.多了解一下,总没有错.

推荐阅读更多精彩内容