字節(jié)一面:20億手機(jī)號存儲選int還是string?varchar還是char?為什么?
前言
大家好,我是田螺。
最近一位星球粉絲說,他去面試了字節(jié),問了這么一道題,20億手機(jī)號存儲,選int還是string?varchar還是char?為什么?
他支支吾吾回答了幾句,好像看起來,面試官面色凝重,對他不是很滿意,果然最好還是掛了。。。
本文跟大家聊聊我的思路。
- 20億數(shù)據(jù),用Int存儲存在哪些問題?
- 面試官的隱藏考察點
- 日常開發(fā)避坑點
1. 20億數(shù)據(jù),用Int或者BigInt能有在哪些問題?
1.1 int存得下11位數(shù)字嘛?
首先,我們都知道手機(jī)號,是11位的數(shù)字,比如13728199213.
在Java中,int是 32位,最大值為 2^31 - 1 = 2,147,483,647。約等于 2×10?。顯然,如果用int,根本存不下 11位的手機(jī)號碼。
要想存得下,得用64位的Long類型,也就是對應(yīng)數(shù)據(jù)庫的bigInt。
1.2 數(shù)據(jù)完整性
例如手機(jī)號01324567890,用Long存會變成1324567890,直接破壞數(shù)據(jù)完整性。
Long phoneNumber =01324567890L; //編譯報錯,Java不允許前導(dǎo)0的Long整數(shù)
并且,有時候,有些手機(jī)號可能包含國家代碼如(+86),或者有些時候,是有連字符的,比如137-2819-9213. 這些原因都導(dǎo)致不能用整型類型存儲。
1.3 查詢麻煩
比如,你要查找,手機(jī)號是137開頭的手機(jī)號號碼,如果用BigInt(Long類型)需先轉(zhuǎn)字符串再模糊匹配,效率暴跌。
2. 用String有哪些好處
- 保真:數(shù)字、符號、前導(dǎo)零全能存,原樣保留。
- 靈活:支持模糊查詢、國際號碼,擴(kuò)展無憂。
- 省心:無需擔(dān)心溢出或格式轉(zhuǎn)換問題。
CREATE TABLE user_tab (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用戶ID',
phone_number VARCHAR(20) NOT NULL COMMENT '手機(jī)號',
PRIMARY KEY (id),
UNIQUE KEY idx_phone (phone_number)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用戶表';
2. 面試官的隱藏考察點
面試的時候,面試官主要考察候選人的一些業(yè)務(wù)擴(kuò)展性、數(shù)據(jù)容錯性、思考問題全面性等能力。我們先通過:為什么用 VARCHAR(20) 而不是 VARCHAR(11),來給面試官秀一波肌肉~~
2.1 為什么用 VARCHAR(20) 而不是 VARCHAR(11)
我們就拿手機(jī)號來說,為什么更建議用 VARCHAR(20),而不是VARCHAR(11)呢?
因為我們都知道,手機(jī)號是11位的,為什么不直接用VARCHAR(11)呢?
如果你日常開發(fā)中,就有思考數(shù)據(jù)容錯性習(xí)慣的話,就會想到:
- 如果遇到國際號碼:+8613822223333(14位)
- 帶國家碼的號碼:008613822223333(15位)
- 分機(jī)號:13822223333#123(超11位)
這些場景,都會導(dǎo)致VARCHAR(11)報錯崩盤。
其次就是業(yè)務(wù)擴(kuò)展性思考:VARCHAR(11)只能存純11位數(shù)字,假設(shè)未來業(yè)務(wù)需要:
- 支持座機(jī)號(如010-62223333,含橫杠)
- 支持虛擬號(如17012341234-5678)
- 支持其他登錄方式(如郵箱+手機(jī)號混合存儲)
因此,字段長度和類型需提前為業(yè)務(wù)變化留余地,避免頻繁改表。這就是日常開發(fā)中的,業(yè)務(wù)擴(kuò)展性思維思考。
還有數(shù)據(jù)容錯性思考,
- 輸入不可控性:用戶可能輸入帶空格/符號的號碼(如138 2222 3333),直接存原始值更方便清洗。
- 設(shè)計妥協(xié):若強(qiáng)制用VARCHAR(11),需在代碼層嚴(yán)格過濾非數(shù)字字符,增加復(fù)雜度。
還有思考問題全面性,比如存儲成本思考。
- VARCHAR(11):最大占 11字節(jié)(utf8mb4下1字符占4字節(jié),但數(shù)字和+號只占1字節(jié))
- VARCHAR(20):最大占 20字節(jié)
- 20億數(shù)據(jù)相差僅約 18GB(和用BIGINT的16GB對比,總成本仍可接受)。
所以面試官期待的答案公式
合理長度 = 基礎(chǔ)需求 + 國際擴(kuò)展 + 容錯緩沖
當(dāng)然,這個不是固定答案,主要還是面試的時候,你回答面試官的思路和表達(dá),最好體現(xiàn)你有這幾個方面的思考:業(yè)務(wù)擴(kuò)展性、數(shù)據(jù)容錯性、思考問題全面性。
2.2 極端場景
如果手機(jī)號是純數(shù)字,并且第一位不是0的話,可以用BIGINT的,但是永遠(yuǎn)不要使用INT。通過這些極端場景的舉例,也體現(xiàn)你思考問題全面性的一個能力。
3. 日常開發(fā)避坑點
設(shè)計手機(jī)號存儲的時候,有哪些需要避的坑的。
主要有這幾個吧:
3.1 字段長度設(shè)計過小
用 VARCHAR(11) 只存純數(shù)字,遇到 +8613822223333(14位)直接截斷。
用 VARCHAR(20) 兼容國際號、分機(jī)號(如 13822223333#123)。'
3.2 字符集和排序規(guī)則
使用 utf8 字符集,無法存儲 emoji 或特殊符號
用 utf8mb4 + utf8mb4_unicode_ci,兼容所有 Unicode 字符(如 + * #)。
3.3 索引設(shè)計不當(dāng)
未對手機(jī)號加唯一索引,導(dǎo)致重復(fù)數(shù)據(jù)。
添加 UNIQUE 約束:ALTER TABLE user ADD UNIQUE INDEX idx_phone (phone);
3.4 數(shù)據(jù)清洗與校驗缺失
用戶輸入 138-2222-3333 或 138 222 23333,直接存儲導(dǎo)致格式混亂。
- 入庫前統(tǒng)一清洗:移除空格、橫杠等符號,只保留 + 和數(shù)字。
- 正則校驗:例如 ^+?\d{8,20}$(允許帶 + 號的 8~20 位數(shù)字)。
3.5 忽視隱私與安全
明文存儲手機(jī)號,泄露用戶隱私。
- 加密存儲:使用 AES 加密或數(shù)據(jù)庫內(nèi)置加密函數(shù)。
- 脫敏顯示:查詢結(jié)果返回 138****3333。
3.5 風(fēng)控校驗
// 嚴(yán)格校驗(11位純數(shù)字,無國際碼)
String regex = "^1(3[0-9]|4[579]|5[0-35-9]|6[2567]|7[0-8]|8[0-9]|9[0-35-9])\\d{8}$";
// 寬松校驗(允許帶國際碼,如+86 13812345678)
String looseRegex = "^(\\+\\d{1,3})?1(3\\d|4[579]|5[0-35-9]|6[2567]|7[0-8]|8\\d|9[0-35-9])\\d{8}$";