项目中日志表数据量大一般会做分表的处理。
思路
1.基础表:orders 里面包含所有字段的类型 2.分表,按照后缀分表,例:orders_202112 但是这些表肯定是后台代码创建而不是人为创建的。 表创建的时候肯定的先判断表是否存在,不存在则创建。 这个判断肯定会使用多次,需要写一个trait 3.就是单表或者多表的插入跟查询了
设计
1.Trait
这个是我从网上粘了一个。我感觉设计思路还挺好的。 主要就是判断表是否存在不存在就创建
<?php
namespace App\Traits;
use App\Exceptions\ResponseApiException;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
trait SplitTableTrait
{
//是否分表,默认false,即不分表
protected $isSplitTable = true;
//原表
public $originTable;
//表
public $endTable;
/**
* 后缀参数
* @var string
*/
protected $suffix = null;
/**
* 年月参数:202104
* @var string
*/
public $ym;
public function init(array $attributes = [], $suffix = null)
{
//默认原表
$this->originTable = $this->table;
//默认最终表
$this->endTable = $this->table;
$this->ym = Carbon::now()->format('Ym');
//isSplitTable参数为true时进行分表,否则不分表
if ($this->isSplitTable) {
//初始化后缀,未传则默认年月分表
$this->suffix = $suffix ?: $this->ym;
}
//初始化分表表名并创建
$this->setSuffix();
}
/**
* 设置表后缀, 如果设置分表后缀,可在service层调用生成自定义后缀表名,
* 但每次操作表之前都需要调用该方法以保证数据表的准确性
* @param $suffix
*/
public function setSuffix($suffix = null)
{
//isSplitTable参数为true时进行分表,否则不分表
if ($this->isSplitTable) {
//初始化后缀,未传则默认年月分表
$this->suffix = $suffix ?: $this->ym;
}
if ($this->suffix !== null) {
//$this->endTable = $this->getTable() . '_' . $suffix;
$this->endTable = $this->originTable . '_' . $this->suffix;
//最终表替换模型中声明的表作为分表使用的表
$this->table = $this->endTable;
}
//调用时,创建分表,格式为 table_{$suffix}
//未传自定义后缀情况下,,默认按年月分表格式为:b_log_202101
//无论使用时是否自定义分表名,都会创建默认的分表,除非关闭该调用
$this->createTable();
}
/**
* 提供一个静态方法设置表后缀
* @param string $suffix
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function suffix($suffix = null)
{
$instance = new static;
$instance->setSuffix($suffix);
return $instance->newQuery();
}
/**
* 创建新的"table_{$suffix}"的模型实例并返回
* @param array $attributes
* @param bool $exists
* @return object $model
*/
// 创建新的"chapters_{$suffix}"的模型实例并返回
public function newInstance($attributes = [], $exists = false)
{
$model = parent::newInstance($attributes, $exists);
$model->setSuffix($this->suffix);
return $model;
}
/**
* 创建分表,没有则创建,有则不处理
*/
protected function createTable()
{
info("createTable===============", [Schema::hasTable($this->endTable)]);
//初始化分表,,按年月分表格式为:b_log_202101
if (!Schema::hasTable($this->endTable)) {
info("创建表==========", [$this->endTable]);
DB::update("create table {$this->endTable} like {$this->originTable}");
}
}
/**
* 排序字段
* @var
*/
protected $orderByField = null;
/**
* 排序类型,asc:正序,desc:倒序,默认倒序
* @var
*/
protected $orderBy = 'desc';
/**
* 执行union all对分表的最终扥分页查询
* @param $queries
* @return array
*/
public function dealListByUnionAllQuery($queries, $limit= 10)
{
//弹出一张表作为union的开始
$unionQuery = $queries->shift();
//循环剩下的表添加union
$queries->each(function ($item, $key) use ($unionQuery) {
$unionQuery->unionAll($item);
});
//设置临时表的名称,添加临时表,顺序不能反过来,否则用关联约束会找不到表
$endQuery =
DB::table(DB::raw("({$unionQuery->toSql()}) as union_" . $this->originTable))
//合并查询条件
->mergeBindings($unionQuery);
if ($this->orderByField) {
$endQuery->orderBy($this->orderByField, $this->orderBy);
}
$lists = $endQuery
//分页
->paginate($limit)
->toArray();
//处理分页数据
// $list = dealPaginate($lists);
return $lists;
}
}
2.model使用
- 使用trait: use SplitTableTrait;
- 构造方法里面初始化
<?php
namespace App\Models;
use App\Traits\SplitTableTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
class Orders extends Model
{
use SplitTableTrait;
// 表名
protected $table = "orders";
// 主键
protected $primaryKey = 'id';
protected $columns; // 未知
protected $fillable = ['name'];
public function __construct(array $attributes = [])
{
parent::__construct($attributes);
info("Orders==========构造方法");
// 初始化分表处理
$this->init();
// // 初始化表字段
// $table_columns = DB::select('show columns from '.$this->table);
// $this->columns = array_column($table_columns, 'Field');
// $this->fillable = array_values($this->columns);
}
}
3.单表查询
// 普通查询
$order = new Orders();
$order->setTable('orders_'. $prefix );
$res = $order->get();
dd($res);
// model查询
$order = Orders::suffix($prefix);
$res = $order->get()->pluck("name");
dd($res);
// 普通保存可以
$order = new Orders();
$order->setTable('orders_'. $prefix );
$order->name = "333333";
$order->save();
// model保存可以
$order = Orders::suffix($prefix);
$res = $order->firstOrCreate(["name" => "wwwwww"]);
// 普通更新
$order = new Orders();
$order->setTable('orders_'. $prefix );
$order_desc = $order->where("id" , 1)->first();
$order_desc->name= "hshshs";
$order_desc->save();
$res = $order->update( [
"name" => "heshuo"
]);
dd($res);
// model更新
$order = Orders::suffix($prefix);
$res = $order->updateOrCreate( [
"id" => 1
],[
"name" => "heshuo111"
]);
dd($res);
4.连表查询
$query = new Orders();
// 查询集合
$queries = collect();
// 循环比较年月,添加每一张表的查询
$startTime = Carbon::parse('-1 months')->firstOfMonth()->toDateTimeString();
$endTime = Carbon::now()->toDateTimeString();
$start = Carbon::parse($startTime);
$end = Carbon::parse($endTime);
$fields = [];
for ($i = $start->copy(); $i->format('Ym') <= $end->format('Ym'); $i->addMonth()) {
//根据是否分表,确定查询真正表
$curTable = "{$query->originTable}_{$i->format('Ym')}";
//如果表不存在则跳过本次循环
if (!Schema::hasTable($curTable)) {
continue;
}
$queries->push(
DB::table($curTable)
// 建议都用select查询字段,SQL尽可能的优化性能
->where('created_at', '>=', $startTime)
->where('created_at', '<=', $endTime)
->when($fields, function ($query) use ($fields) {
info("-----走when了", []);
$query->select($fields);
})
);
}
$res = $query->dealListByUnionAllQuery($queries);
dd($res);
|