最近在項目中做緩存的性能優(yōu)化,對熱點代碼/TP時間進行分析之后.發(fā)現(xiàn)這些性能不理想的請求,都是cache的value比較大導致的.所以自然想到的就是精簡字段.并且對數據進行壓縮存儲.這篇文章就談談其中對日期的壓縮處理.
字符串
最常規(guī)的日期存儲方式應該就是字符串.如"20171029",那這樣的字符串到底占用多大空間呢.我們可以讓代碼告訴我們.
public static void main(String[] args) {
String date = "20171029";
byte[] dateBytes = date.getBytes();
System.out.println(dateBytes.length);
}
運行結果是: 8
我們可以看到,使用字符串存儲的日期.要占用8個字節(jié).如果一個數據包里大部分都是日期.那1KB的數據包只能存放128個日期.當日期很多的時候,會占用很多空間.
Long
了解Unix時間的都知道.我們可以用一個long類型來存儲日期.Java里long類型占用的空間也是8byte.和上面的字符串是一樣的.
Int
int在Java中占用4Byte.所代表的數字范圍是 -2^32 ~ 2^31-1.用來存儲日期完全夠用.所以我們完全可以使用int類型來存儲時間.這樣下來.我們用1KB的數據包就能存放256個日期了.
到這里看起來已經很好了.但是我們發(fā)現(xiàn).2^31-1 = 2147483647 遠遠大于99991231.也就是說這個int的前面很多個bit是空閑的.那我們有沒有可能繼續(xù)壓縮?
Mysql如何存儲日期
因為使用int存儲日期肯定是肯定不是最優(yōu)的.那如何知道更優(yōu)的方案呢.那就是去借鑒其他人的方案.那么數據庫一定是一個很好的選擇.因為對于數據庫來說,能存小,一定不會存大.經過查詢資料.Mysql是使用3個byte來存儲日期的.具體的公式是
yyyy * 32 * 16 + mm * 32 + dd
具體成二進制表示為:yyyyyyyy yyyyyyym mmmddddd
這個公式是什么意思呢.
- 日的范圍是1~31.我們使用5個bit就足夠存儲.
- 月的范圍是1~12.我們使用4個bit就足夠存儲.
- 對于3個byte的24位.還剩下15位.可以存儲2^15-1 = 32767個數據.用來當年已經足夠了.
Unix時間戳 + Mysql存儲方式 = 2Byte
看了Mysql的存儲方案,不難發(fā)現(xiàn),使用3Byte確實不錯.但是我們能不能做的更好,只用2個Byte.
我們可以看下.對于日和月的存儲已經沒有壓縮空間了.如果使用2個byte.則還有7個bit供我們使用.
2^7=128.如果直接用來存年份.肯定是不夠的.但是Unix時間戳還記得嗎.它是從1970年1月1日到現(xiàn)在的毫秒差.這個相對的思路如果加到我們的設計里呢.
我們這127不用來做絕對的時間,而是作為相對的時間存儲.那參照日期如果采用2017年.則可以存儲到2017 + 127 = 2144.對于一般的業(yè)務系統(tǒng).128年的偏移,足夠我們使用了.
使用二進制來表示則日期格式為:yyyyyyym mmmddddd
而使用這種2Byte類型的存儲.則1KB的數據包則可以存儲512個日期.相比一開始的128.多了3倍.
總結
你可能需要知道,2M的數據和2K的數據.在網絡傳輸中有什么區(qū)別.
才能理解這篇文章為什么會要對這種看似沒什么用字符串存儲進行優(yōu)化.
不過沒關系.多了解一下,總沒有錯.