国内手机号为11位数字,第1-3位—网络识别号;第4-7位—地区编码;第8-11位—用户号码。
如:
199 0000 1111
网络识别号 地区编码 用户号码
目前有效号段有57个,每个号段可表示10^8 1亿个手机号。
130,131,132,133,134,135,136,137,138,139,141,145,146,147,148,149,150,151,152,153,155,156,157,158,159,162,165,166,167,170,171,172,173,174,175,176,177,178,180,181,182,183,184,185,186,187,188,189,190,191,192,193,195,196,197,198,199
那如何存储所有有效号段呢?换句话说怎么存储以1大头的11位数字呢
还是以上文19900001111为例,各种形式表示占用空间大小。
- 十进制:19900001111 (bigint 64bit 8byte)
- 二进制:10010100010001000011110101101010111 (35bit)
- 字符串:“19900001111” (11*8=88bit 11byte)
- MD5 16进制表示:d9d3b76f0d0777a8c62481d0a847ab99(128bit 16byte)
- MD5字符串:“d9d3b76f0d0777a8c62481d0a847ab99” (32*8=256bit 32byte)
- AES ECB模式 128位:22622C895B20A2C405708ED2F21897A6 (128bit 16byte)
手机号存储
使用bigint存储
(8*5700000000)/1024/1024/1024 = 42.46G
占用空间还是太大了,对于实时性要求比较高的场景,比如验证是否是黑名单等,需要响应速度快,存储空间小。
使用bit(位)存储,通过标记位是0或1表示数据是否存在,这其实是通过计算换存储的方式。
比如验证数字5是否存在,从右往左数,第5位为1即存在。
5
←
0 0 0 1 0 0 0 0
那如何表示19900001111呢?
上文有提到19900001111二进制表示为35位,而一般的bitset(bitmap)限制数量为 2^32个。
以Redis Bitmaps为例,bit数量上限为2^32,数据存储大小上限为512MB。
2^32 = 4294967296
4294967296/8/1024/1024 = 512MB
而要存储的手机号需要2^35个bit,所以我们需要对其进行拆分。
还是以19900001111为例,199做key,剩余值10^8=1亿,而1亿需要多少存储呢?
100000000 / 8 / 1024 / 1024 ≈ 12MB
而有效号段57个,于是总共需要 12*57=684MB,就可以存储所有有效手机号。
补充个操作示例:
getbit b_199 12345678
setbit b_199 12345678 1
手机号&对应MD5存储
按bit存储,手机号和对应MD5值,57个有效号段需要约108G
(((35+128)/8) * 5700000000)/1024/1024/1024 = 108.16G
由于MySQL bit 最长64位,这里使用手机号使用bigint(8byte),MD5使用字符串格式存储(32byte)
((8+32)*5700000000)/1024/1024/1024 = 212.34G
再加上索引还需要乘2,约425G