数据结构压缩_将数据压缩到数据结构中
數據結構壓縮
這個故事是關于我們最近在Plumbr進行的容量優化任務。 一切始于將無害的要求添加到現有組合中。
您可能知道,Plumbr監視解決方案作為連接到服務器的Java代理分發。 只需少量添加即可跟蹤一段時間內所有已連接的代理,以便可以實時回答以下問題:
- 我們有多久沒有收到這個特定JVM的消息了?
- 另一個JVM的最后一次已知停機時間是什么?
當每個代理每秒發送一次心跳時,我們在服務器端需要做的就是跟蹤所有心跳。 由于每個心跳都附加有唯一的時間戳,因此幼稚的解決方案就像將所有心跳扔到Set或Map中一樣容易。 那么-簡單,完成,接下來,請?
但是,一些快速的數學運算表明,最初的想法可能行不通。 考慮到:
- 時間戳記的類型很長 ,需要8個字節才能容納
- 一年中有365 x 24 x 60 x 60 = 31,536,000秒
我們可以快速進行數學計算,發現單個JVM僅使用一年的原始數據就需要240MB 。 僅原始數據的大小就已經足夠嚇人了,但是當打包到HashSet時,結構的保留大小 爆炸到大約2GB ,而所有開銷的java.util.Collection API實現都隱藏在它們的腹部 。
幼稚的解決方案已經無法解決,我們需要一個替代方案。 最初我們不必走得很遠,因為在同一java.util包中,一個等待被發現的驚喜叫java.util.BitSet 。 根據該類的javadoc:
BitSet類實現一個按需增長的位向量。 位集合的每個分量都有一個布爾值。 BitSet的位由非負整數索引。 可以檢查,設置或清除各個索引位。
那么,如果我們將從代理獲取的心跳存儲為由心跳時間戳索引的布爾值,該怎么辦? Java中的時間戳表示為當前時間與UTC 1970年1月1日午夜之間的毫秒差。 知道這一點后,我們可以將UTC表示為2015年9月1日12:00 UTC,即數字1441108800。那么,如果當我們看到某個Agent在時間戳1441108800處向我們發送心跳信號時,我們會將索引1441108800的位置設置為true ,否則被保留為默認false ?
解決方案的問題隱藏在一個事實中,即BitSet中的位是用整數而不是long索引的。 為了繼續進行此解決方案,我們將需要一種將整數映射到long而不丟失任何信息的方法。 如果似乎不可能,那么讓我們回顧一下這樣一個事實,即需要一秒而不是一毫秒的精度。 知道了這一點,我們可以將索引縮小1,000倍,并以秒而不是毫秒的精度標記時間。
但是僅使用整數就可以表示多少秒? 顯然,Integer.MAX_VALUE足夠大,足以表示從1970年1月1日到19.01.2038的每一秒。 除了制造2038年的問題外,它還應該足夠好,對嗎?
不幸的是,正如我們對餐巾紙的計算所得出的那樣,一年的數據仍然需要約800MB的堆空間。 這是從最初的HashSet 2GB向正確方向邁出的一小步,但對于實際使用而言仍然太高了。
為了克服該問題,可能需要重新閱讀/重新考慮“足以代表1970年1月1日的每一秒”的部分。 (不幸的)先生。 直到1995年,高斯林才發明了Java虛擬機。18年后,Plumbr自己看到了曙光。 因此,我們直到1970年才需要回顧歷史,并且每個整數都有一堆零。 可以從01.01.2013開始,而不是從01.01.1970開始,并使用一個索引0對應于01.01.2013 00:00(UTC)。
重做我們的餐巾紙數學,并在實踐中檢查結果使我們成為贏家。 現在一年的數據量只能存儲在20MB中 。 與原始2GB相比,我們將所需容量減少了100倍 。 由于現有的基礎架構已經可以解決這個問題,因此已經處于舒適區域,因此我們沒有在優化路徑上走得更遠。
故事的道德啟示? 當您有需求時,請找出對應用程序性能的影響。 我的意思是性能的各個方面,因為不僅有延遲和吞吐量,還應該忘記容量。 并且–了解您的域名。 沒有它,您將無法做出決策,如果僅僅配備了有關數據結構的智能書籍,這些決策就顯得不安全且危險。
翻譯自: https://www.javacodegeeks.com/2015/09/squeezing-data-into-the-data-structure.html
數據結構壓縮
總結
以上是生活随笔為你收集整理的数据结构压缩_将数据压缩到数据结构中的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小说阅读器安卓版下载(小说阅读器安卓)
- 下一篇: 高性能 高可用 可弹性伸缩_性能,可伸缩