本文最后更新于: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 : ''; $modelTable = $model->getTable(); $wheres = $builder->getQuery()->wheres; 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);
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