[譯]咱們是如何設計存儲4億個電話號碼的

原文:How we store 400M phone number data with fast lookups
譯者:傑微刊-張帆 node

 

 

若是你居住在印度,當不但願接受任何電話推銷員的騷擾時,你能夠在全國客戶偏好登記冊(National Customer Preference Register,NCPR) 【1中進行註冊。政府維護了這個由用戶註冊的電話號碼組成的數據庫。如今,差很少有4億個註冊號碼。全部註冊的電話推銷員必須及時更新數據,以保證他們在進行推銷時會參考這個偏好設置進行工做。 git


這些數據由一捆ZIP文件(當下是40個)提供,每一個ZIP文件包含一個10M的CSV文件。這篇文章將會講述這2.4GB壓縮後的數據如何基於一些簡單的方式以一種可搜索的格式適配2GB的內存。 github


數據 web


下面是CSV文件一瞥(出於隱私緣由,有些數據進行了混淆) 數據庫

 

數據存儲,PostgreSQL,內存,SQL

 


關於存儲在SQL引擎中的一些說明 數組


在內存爲4GB的Linode機房的機器上, PostgreSQL數據表(使用COPY)加載數據約須要10分鐘: 測試

 

 

real    10m0.159s
user    2m42.243s
sys     0m26.363s

 

 


添加一個主鍵大約耗時1.5到2個小時: 優化

 

real    118m21.637s
user    0m0.043s
sys     0m0.020s

 


並使用32GB的硬盤空間: spa

 

數據存儲,PostgreSQL,內存,SQL

 


觀察CSV數據 code


分析了數據以後,咱們能夠看到:


* 將近400M行數據


* 電話號碼所有(phone numbers)是10位


* 服務區域碼(service area code)是1-23之間的天然數


* 偏好(preference)依靠`#`來界定,多是`0`或者是{1,2,3,4,5,7}的組合


* Ops類型(Opstype)用A表示啓用,用D表示未啓用


* 電話號碼類型(Phone Type)是{1,2,3}中的一個

 

 

這意味着一行數據能夠用2個字節表示:


第一個字節:1位存在標誌位(existence flag),5位服務區域碼,2位電話號碼類型。


第二個字節:7位偏好,1位Ops類型。

 

數據存儲,PostgreSQL,內存,SQL


數據能夠經過2*400MB來表示。存在標誌位將會在下面的部分討論。


使之可搜索


每一個條目都會按照電話號碼進行頻繁的搜索,而目前咱們並無將數據與電話號碼進行匹配。咱們須要添加字節來存儲電話號碼。不幸的是,10個數字並不能放入32位中(10 digits won't fit in 32 bits),使用5*400MB來存儲數字並非一個樂觀的狀況,並且根本沒辦法進行搜索。若是數據按順序排列(arranged in a sequence),那麼索引爲 (2*number) 和 (2*number+1)的內存位置便能給出所需的兩個字節。空行能夠用第一個字節中的存在標誌表示。這意味着咱們須要20GB的內存(2字節*10B的數字)。咱們能進一步壓縮嗎?該數組看起來很稀疏(只有40%被佔用)。


咱們的解決方案是:使用兩種格式類型。


更進一步


咱們還發現對於大多數移動手機號碼的數組是密集的【2】 。因此,若是10個數字分紅兩部分——4位的前綴(咱們能夠稱之爲頭部)和6位的數字偏移量(尾部)——這樣一來,固定的4位前綴的全部可能值按順序排列時,它們均可以被放入2MB的空間裏了。(每一個尾部2字節)。如今,搜索變得簡單了,由於咱們按照尾部進行偏移量計算,直接訪問數組便可。


這個稀疏的數列存儲在5字節的序列中,3個字節表示尾部,2個字節表示數據。尾部按照升序排列,因此搜索變的簡單了(二分搜索)。


對於持久化存儲,具備相同前綴的數字存儲在一個文件中,該文件的第一個字節是類型的指示框。這些共需1.8GB的空間,這些數據能夠存儲在內存中,經過webserver進行發佈。


加工處理


使用快速Python腳原本轉換CSV數據爲咱們須要的格式是十分耗時的。分析代表,大部分時間花費在迭代處理2M固定頭部數據時。咱們嘗試使用xrange進行優化,可是5小時對於處理整個數據,尤爲是PostgreSQL處理僅須要2小時,實在太多了。咱們但願能有些快速響應,更符合心理預期。相同的程序選擇Rust來實現,處理整個數據僅用20-30分鐘。

 

 

real    21m4.284s
user    20m58.427s
sys     1m37.607s

 


查找計時


爲了測量該解決方案的速度,咱們隨機生成了相同序列(固定的頭部)的電話號碼。結果以下圖所示。咱們選取「9818」和「9000」開頭的號碼去分別計算查找密集框(咱們稱之爲類型0)和稀疏框(類型1)的時間。對於SQL解決方案,頭部的密集程度並不影響。注意,在本次測量中,儘管咱們爲了公平起見,計時時包含了磁盤的讀寫,可是在咱們的解決方案中,數據一旦被加載或放入內存中,再也不須要磁盤訪問,以後因爲數據存儲格式的優勢,這個進程被加快。

 

 

數據存儲,PostgreSQL,內存,SQL

 


全部的測試都是在4GB的Linode機房機器上跑的,機器配置以下:


SSD, 4GB RAM, 4 virtual CPU cores, CPU Model: Intel(R) Xeon(R) CPU E5-2680 v2 @ 2.80GHz


API和開源


在SparkTG,咱們尊重客戶的偏好設置。儘管咱們的客戶大部分都是與註冊的客戶交流,但咱們仍是保證他們最終不會撥出一個無關的電話。咱們已經將該項目【3】開源,而且提供API【4】來查找號碼NCPR狀態,使得電話推銷找不到方式撥打註冊用戶的電話。


參考


1. https://en.wikipedia.org/wiki/Do_Not_Disturb_Registry


2. https://en.wikipedia.org/wiki/Mobile_telephone_numbering_in_India


3. Github repository


4. NCPR status lookup

 

 原文地址:http://www.jointforce.com/jfperiodical/article/925?f=jf_tg_bky

相關文章
相關標籤/搜索