每日一面 - mysql中,我存十億個手機號碼,考慮存儲空間和查詢效率,怎麼設計?

首先提出假設:

  1. 手機號碼不會更新,只會插入和刪除。

  2. 查詢包括精確查詢某個手機號是否存在,以及獲取某一號碼段的所有手機號

假設表只有一個字段,就是手機號 phone,並且設置爲主鍵。如果不設置主鍵並且沒有唯一索引,InnoDB 會給我們自動生成一個隱藏主鍵列,浪費空間。


MyISAM or InnoDB

如果插入和刪除並不頻繁,手機號是提前載入的字典表,而不是用戶主動註冊而產生的,則 MyISAM 看上去比 InnoDB 要好。因爲 MyISAM 不涉及事務,更新都是表級鎖。如果是用戶觸發的插入和刪除,則需要用 InnoDB。

字段類型

考慮三種類型,BigInt,Char,Varchar

這幾種類型在 InnoDB 引擎下默認行格式的存儲方式爲:

  • 對於 bigint 類型,如果不爲 NULL,則佔用8字節,首位爲符號位,剩餘位存儲數字,數字範圍是 -2^63 ~ 2^63 - 1 = -9223372036854775808 ~ 9223372036854775807。如果爲 NULL,則不佔用任何存儲空間

  • 對於定長字段,不需要存長度信息直接存儲數據即可,如果不足設定的長度則補充。對於 char 類型,補充 0x20, 對應的就是空格。

  • 數據開頭有可變長度字段長度列表,所以 varchar 只需要保存實際的數據即可,不需要填充額外的數據。正是由於這個特性,對於可變長度字段的更新,一般都是將老記錄標記爲刪除,在記錄末尾添加新的一條記錄填充更新後的記錄。這樣提高了更新速度,但是增加了存儲碎片。

由於手機號不更新,並且不同國家的手機號長度不同,並且可能有特殊字符,字符類型在默認的編碼和排序規則下進行範圍匹配也能滿足我們的需求,所以爲了節省空間,使用 varchar 類型。


索引類型

由於涉及到範圍查詢,所以最好不用 Hash 索引,而是用默認的 B+ 樹索引

分區

這個數據量比較大了,需要用分區。phone 可以作爲分區鍵,可以按照範圍分區,例如:

PARTITION BY RANGE COLUMNS( phone ) (

PARTITION p0 VALUES LESS THAN ('13100000000'),

PARTITION p1 VALUES LESS THAN ('13200000000'),

。。。。

PARTITION pn VALUES LESS THAN MAXVALUE

);

也可以按照 hash 分區,例如:

PARTITION BY HASH( phone )
PARTITIONS 64;

這樣查詢某個手機號是否存在這種業務就能更快,因爲一張表被劃分成了很多張小表。並且如果涉及多張小表 MySQL 還可以多線程併發查,效率提升很多。如果考慮獲取某一號碼段的所有手機號,那最好還是按照範圍分區,可以使邏輯查詢範圍更小。但是 hash 分區數據可能比範圍分區更加均衡。

注意,對於 HASH 分區個數最好是 2^n。因爲對於 2^n 取餘相當於對 2^n - 1 取與運算,增加了查詢時的計算分區的效率

進一步優化

對於查詢某個手機號是否存在,可以在數據庫上層加一層布隆過濾器,提高效率。

同時爲了提高準確性,可以通過號碼號段,不同號段使用不同的布隆過濾器。在插入數據庫的同時,放入布隆過濾器中。如果布隆過濾器中檢測不存在,則肯定不存在。爲了減少布隆過濾器的誤判概率,可以使用更多的布隆過濾器,同時設置交叉範圍,例如一個 13000000000~13200000000 用布隆過濾器 A,13100000000~13300000000 用布隆過濾器 B, 13211111111就要經過布隆過濾器 A 和 布隆過濾器 B 的驗證。

每日一刷,輕鬆提升技術,斬獲各種offer:


本文分享自微信公衆號 - 我的編程喵(MyProCat)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章