在我们业务中,在并发很高的IO下,往往是需要水平分表(分库)或者是垂直分表(分库)。
本文,就详细介绍水平分表之取模算法:
假设一个场景,APP需要手机号登录或者注册,而一张用户表中已经有千万级的数据,单表的IO读写已经很吃力,需要迫切的分表,那么该如何分表,以什么形式去分
分表方法:
1.取模算法
2.哈希算法
3.范围分表
本文针对水平分表的取模算法来分析:
1.取模实际上就是对被除数进行取余的操作 A%B=余数
2.利用用户手机尾号进行求余的操作
3.假如我们水平分表5张表,那么求余公式则为尾号/5=余数
4.这个余数就是我们的表名,进而得出该用户信息应该在哪张表
5.业务直接去以余数命名的该表查询信息
原始业务图解:
WechatIMG489.jpeg
将要实现的:
WechatIMG490.jpeg
开始实践:
我们将用户表设计为5张表 从user_0
....user_4
;
表结构如下:
WechatIMG488.jpeg字段name
即为手机号
在上期中,我们了解了负载均衡与消息队列,我们这次将请求还是与之前的结合起来
producer项目里创建controller
public function AddUser():void {
$data['name'] = time();
$json = json_encode($data);
dispatch(new UserJob($json))->onQueue('User');
}
`
将当前时间戳模拟为用户手机号,并推入到RabbitMQ
consumer项目的队列消费者进程开始消费
public function handle(){
$data = json_decode($this->data,true);
$table = 'User_'.$data['name']%5;
$tableObj = $this->tableObj($table);
$tableObj->name = $data['name'];
$tableObj->save();
}
/**
* 通过反射返回Model对象
* @param $class_name
* @return object
* @author mjShu
* @throws \ReflectionException
*/
private function tableObj($class_name){
$class_name = 'App\Http\Model\\'.$class_name;
$class = new \ReflectionClass($class_name);
$obj = $class->newInstance();
return $obj;
}
在handle
方法里,我们通过对producer生产的数据中的模拟手机号进行求余
得到余数,通过字符串拼接得到表名,表名等于User_余数
进而通过PHP的反射机制
实例化Model
类,将其保存进表
启动AB测试
ab -c 100 -n 2000 http://192.168.2.101/user
RabbitMQ
消费后通过Lumen
队列插入数据库
启动horizon
,应该如图
等RabbitMQ ACK
确认完毕后,我们来查看下表:
由此可见,我们的数据均是是被水平均匀的拆分到5张表中,怎么样,是不是很简单呢,原理就是通过求余来确定数据的落表地点,进而直接查询该表,均匀打散了数据,并且有规律可循,避免了单表的压力过大,相同理论,该分表方法也可以运用到其他方面,本文只是做一下探讨,实际上求余的操作,都是由中间件完成,不应该放在业务中,因为太繁琐,比如查询某个日期的数据,必须每张表都要执行查询最终合并结果集返回
网友评论