Laravel的分库分表解决方案

本文最后更新于:10 天前

Laravel的分库分表解决方案


分库

1、按功能分
用户类库、商品类库、订单类库、日志类、统计类库…

2、按地区分
每个城市或省市一个同样的库,加上后缀或前缀如:db_click_bj、db_click_sh

分表

1、横向分表 解决表记录太大问题**

1)按某个字段分,
根据tid最后一位来决定附件要保存在哪个分表

2)按日期分表
一些日志、统计类的可以按年、月、日、周分表
如:点击量统计click_201601、click_201602

3)使用mysql的merge
先把分表创建好,然后创建总表指定engine= MERGE UNION=(table1,table2) INSERT_METHOD = LAST;

2、纵向分表 解决列过多问题

1)经常组合查询的列放在一个表,常用字段的表可考虑Memory引擎
2)不经常使用的字段单独成表
3)把text、blob等大字段拆分放在附表
如:phpcms的文章表分成主表v9_news和从表v9_news_data,主表存标题、关键字、浏览量等,从表存具体内容、模板等

注意事项

1、避免分表join操作
因为关联的表有可能不在同一数据库中
2、避免跨库事务
避免在一个事务中修改db0中的表的时候同时修改db1中的表,一个是操作起来更复杂,效率也会有一定影响
3、分表宜多不宜少
这样做主要是为了尽量避免后期可能遇到的二次拆分
4、尽量把同一组数据放到同一DB服务器上

Laravel实现

1、根据日期横向分表

场景 :
logs表根据时间分表,表名为logs_202201 logs_202202

解决方案:
全局作用域 scope

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

/**
* 按照日期分表的作用域
*/
class LinLogScope implements Scope {


public function apply(Builder $builder, Model $model)
{

$splitType = isset($model->splitType) ? $model->splitType : ''; // 获取设置在 model 中的日期字段
$modelTable = $model->getTable();
$wheres = $builder->getQuery()->wheres;
// 从 where 中获取 日期 条件
foreach($wheres as $where){
if(isset($where['column']) && $where['column'] === $splitType){
$splitValue = $where['values'];
break;
}
}
$startDate = Carbon::parse($splitValue[0]);
$endDate = Carbon::parse($splitValue[1]);
$queries = collect();
$connection = $model->getConnectionName();

for($i = $startDate; $i->format('Ym') <= $endDate->format('Ym'); $i->addMonth() ){
$table = $modelTable . '_' . $i->format('Ym');
$db = DB::connection($connection)->table($table);

// 拼接 where 条件, 目前处理了 between, in, = 三种条件
foreach($wheres as $where){

if($where['type'] == 'between'){
$db->whereBetween($where['column'], $where['values'], $where['boolean'], $where['not']);
}else if($where['type'] == 'In'){
$db->whereIn($where['column'], $where['values'], $where['boolean']);
}else if($where['type'] == 'NotIn'){
$db->whereNotIn($where['column'], $where['values'], $where['boolean']);
}else if($where['type'] == 'Basic'){
$db->where($where['column'],$where['operator'], $where['value'], $where['boolean']);
}else if($where['type'] == 'Nested'){
$db->addNestedWhereQuery($where['query'], $where['boolean']);
}else if($where['type'] == 'Exists'){
$exists = $where['query'];
$db->whereExists(function($query) use($exists, $modelTable, $table){
$wheres = $exists->wheres;
$column = $wheres[0];
// 替换表为对应带日期后缀的表
$column['first'] = str_replace($modelTable, $table, $column['first']);
$query->select(DB::raw(1))
->from($exists->from)
->whereRaw("{$column['first']} {$column['operator']} {$column['second']}");
// 添加额外的补充条件, 目前只加一条
if(isset($wheres[1])){
$query->where($wheres[1]['column'], $wheres[1]['operator'], $wheres[1]['value'], $wheres[1]['boolean']);
}
});
}
}
$queries[] = $db;
}

$unionQuery = $queries->shift();
$queries->each(function($item, $key) use ($unionQuery){
$unionQuery->unionAll($item);
});
$sql = $builder->from(DB::raw("({$unionQuery->toSql()}) as {$modelTable}"))
->mergeBindings($unionQuery);
return $sql;
}
}

问题:

1.增删改查的前置事件处理
laravel需要重写build

2.分库分表后的关联查询

3、采用云原生分布式数据库

  • 阿里云 OceanBase
  • 华为云 GaussDB
  • TIDB

Laravel的分库分表解决方案
https://calmchen.com/posts/da628193.html
作者
Calm
发布于
2022年8月29日
更新于
2022年8月30日
许可协议