文章目录
- 前情提要
- 注册/登录场景
- 实现拆表
- 拆表的散列算法
- 改造 Laravel 的 User Model
- 控制器内使用
- 结论
前情提要
这是一个使用 Laravel 实现的分表场景。
通常一个应用拥有了一定量级用户的时候,应用方都会自建自己的用户认证服务,无论使用何种实现方式,在这里我统称为 Passport Server ,其目的都是为了验证用户、保存维护用户、为成员网站提供当前用户的信息状态。
这时,我如果是作为其中的一个成员网站(比如统一的用户中心),在用户注册/登录访问时,就会将用户的登录表单信息提交给 Passport Server ,当 Passport Server 验证通过后会向我返回该用户的基本信息,随即我会根据这些信息或创建,或查找该用户在本系统的记录,以便提供本系统的各项服务。
随着成员网站的增多(可想象为一个大型游戏公司,注册一个账号,在旗下多款游戏可以通用),用户的数量的剧增,我这个成员网站作为一个重要的节点,所有用户都会过来进行操作,这时我就要考虑将这些巨量的用户数据通过拆表的方式进行处理。
注册/登录场景
在统一的注册页面有三种注册方式,手机号码注册、邮箱注册、自定义通行证注册,在这三种不同的注册方式内,根据相关要求,手机号码这个字段均存在且必填及短信验证。当用户注册完成后,将用户的注册信息提交给 Passport Server
在统一的登录页面,用户可根据手机号码、邮箱(可以后绑定)、自定义的通行证进行登录操作,当用户提交登录表单时,将用户的登录信息提交给 Passport Server 后返回相应信息,跳转回目标站点。
实现拆表
拆表的散列算法
该算法根据自身应用可自行选择,这里会根据登录成功 Passport Server 返回的共通字段(因为三种注册方式都必填手机号码)进行散列分表,算法如下
// 这个算法,会生成32张表,如:users_a7、users_70
function getPrefix($account)
{
$passport = strtolower($account);
$key = md5($passport);
$key_1 = $key [3];
$key_2 = $key [9];
if ($key_2 >= '7') {
$key_2 = 7;
} else {
$key_2 = 0;
}
return $key_1.$key_2;
}
改造 Laravel 的 User Model
<?php
namespace App\Models;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Eloquent\Model as EloquentModel;
class User extends EloquentModel
{
protected $fillable = [ 'username', 'email', 'password', 'mobile'];
public function __construct(array $attributes = [])
{
if (isset($attributes['mobile'])) {
$this->setTableByAccount($attributes['mobile']);
}
parent::__construct($attributes);
}
// 动态指定数据表
public function setTableByMobile($mobile)
{
$tb = 'users_'.$this->getPrefix($account);
// 可以用其他方式实现该步骤,查询表,不存在则创建
if(!Schema::hasTable($tb)){
Schema::create($tb, function ($table) {
$table->id('int', 11);
$table->string('username', 30);
$table->string('email', 30)->unique();
$table->string('mobile', 30)->unique();
$table->string('password', 32);
$table->timestamps();
$table->timestamp('deleted_at')->nullable();
});
}
$this->setTable($tb);
}
// 传递给构造函数根据哪个字段查找表
public static function selectTableByMobile($mobile)
{
return new static(['mobile' => $mobile]);
}
}
控制器内使用
<?php
namespace App\Http\Controllers;
use App\Models\User;
class UserController extends Controller
{
public function test()
{
$mobile = '13800138000';
$email = 'xxx@';
$user = User::selectTableByMobile($mobile)->where('email', $email)->first();
dd($user->toArray());
}
}
结论
上面所实现的拆表方式经过测试正常运行,如果没有上面我所说的 Passport Server 服务,在单一应用中只需要有适合自己业务的哈希分表算法,上述代码均可借鉴。
在查找表的过程中如果没有该表则创建的逻辑,可以使用中间件等其他方式实现,这里仅举例一种方式,并非唯一。