2015/09/18

mysql根据手机号码进行分表, 处理一个10亿量级的分表


今天工作遇到需要处理大数据模块,游客数据表,结构大概是这样的:

create table if not exists tourist(
phone varchar(30) not null, #游客手机
name varchar(30) not null, #游客名称
place varchar(45), #籍贯
id_card varchar(36), #身份证
primary key(phone));

因为后期游客数据会达到10亿的量级,单个数据表肯定是不可行,根据经验,Mysql表数据一般达到百万级别,查询效率会很低,容易造成表锁,甚至堆积很多连接,直接挂掉。水平分表能够很大程度较少这些压力。

按照每张表存放1000w条记录,需要分100个表。而且分表后,我还能通过游客手机号码,准确判定到他数据落在哪张表上。


常见分表方式

1.按时间分表

这种分表方式有一定的局限性,当数据有较强的实效性,如微博发送记录、微信消息记录等,这种数据很少有用户会查询几个月前的数据,如就可以按月分表。

2.按区间范围分表

一般在有严格的自增id需求上,如按照user_id水平分表:

table_1  user_id从1~100w
table_2  user_id从101~200w
table_3  user_id从201~300w
...

3.hash分表

通过一个原始目标的ID或者名称通过一定的hash算法计算出数据存储表的表名,然后访问相应的表。


我这边选用的是hash取模分表:

create table if not exists tourist_(tblid)(
id bigint not null, #sprintf("%u", crc32($phone))
phone varchar(30) not null, #游客手机
name varchar(30) not null, #游客名称
place varchar(45), #籍贯
id_card varchar(36), #身份证
primary key(id));
//预先创建100个表,根据手机号码判定数据落到哪张表
function get_tblid($phone, $tblnum = 100) {
    //计算字符串的32位CRC(循环冗余校验), 该函数可用于验证数据完整性
    //为了确保从 crc32() 函数中获得正确的字符串表示,您需要使用 printf() 或 sprintf() 函数的 %u 格式符。
    //如果未使用 %u 格式符,结果可能会显示为不正确的数字或者负数。
    $crc = sprintf("%u", crc32($phone));

    //对结果进行取余数
    $tblid = intval(fmod($crc, $tblnum));

    //保证数据区域在[1, $tblnum]之间
    return ($tblid+1);
}