首页 > 编程语言 >thinkphp5.1水平分表实践(一)

thinkphp5.1水平分表实践(一)

时间:2024-07-15 14:56:54浏览次数:10  
标签:tableName 实践 value rule startTime 分表 thinkphp5.1 id

在thinkphp5.1中可以使用partition方法进行水平分表功能,但其分表功能较简单,不适用某些特殊场景。其在TP中的实现逻辑如下:

文件路径:thinkphp\library\think\db\Query.php   (555行)

 /**
     * 得到分表的的数据表名
     * @access public
     * @param  array  $data  操作的数据
     * @param  string $field 分表依据的字段
     * @param  array  $rule  分表规则
     * @return array
     */
    public function getPartitionTableName($data, $field, $rule = [])
    {
        // 对数据表进行分区
        if ($field && isset($data[$field])) {
            $value = $data[$field];
            $type  = $rule['type'];
            switch ($type) {
                case 'id':
                    // 按照id范围分表
                    $step = $rule['expr'];
                    $seq  = floor($value / $step) + 1;
                    break;
                case 'year':
                    // 按照年份分表
                    if (!is_numeric($value)) {
                        $value = strtotime($value);
                    }
                    $seq = date('Y', $value) - $rule['expr'] + 1;
                    break;
                case 'mod':
                    // 按照id的模数分表
                    $seq = ($value % $rule['num']) + 1;
                    break;
                case 'md5':
                    // 按照md5的序列分表
                    $seq = (ord(substr(md5($value), 0, 1)) % $rule['num']) + 1;
                    break;
                default:
                    if (function_exists($type)) {
                        // 支持指定函数哈希
                        $value = $type($value);
                    }

                    $seq = (ord(substr($value, 0, 1)) % $rule['num']) + 1;
            }

            return $this->getTable() . '_' . $seq;
        }
        // 当设置的分表字段不在查询条件或者数据中
        // 进行联合查询,必须设定 partition['num']
        $tableName = [];
        for ($i = 0; $i < $rule['num']; $i++) {
            $tableName[] = 'SELECT * FROM ' . $this->getTable() . '_' . ($i + 1);
        }

        return ['( ' . implode(" UNION ", $tableName) . ' )' => $this->name];
    }

 

仿照上面的实现逻辑,我实现了一个demo:

1.创建测试数据表

-- 用户信息
CREATE TABLE `t_user` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- 聊天内容
CREATE TABLE `t_chat` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `uid` int(10) unsigned NOT NULL,
  `content` varchar(100) NOT NULL,
  `create_time` datetime NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

 

2.application目录下场景traits目录,并在该目录下创建Partition

<?php

namespace app\traits;

trait Partition
{
    /**
     * 判断数据表是否存在
     * @param string $tableName 表名
     */
    protected function tableExists($tableName)
    {
        $result = $this->query("SHOW TABLES LIKE '{$tableName}'");
        return !empty($result);
    }

    /**
     * 创建分表
     * @param string $tableName 分表表名
     */
    protected function createPartitionTable($tableName)
    {
        $table = $this->getTable();
        $result = $this->query("SHOW CREATE TABLE {$table}");
        $createSql = $result[0]['Create Table'];
        $createSql = str_replace($table, $tableName, $createSql);
        $this->execute($createSql);
    }

    /**
     * 设置分表的的数据表名
     * 按业务、年月设置分表信息,进行后续CURD操作
     * @param string $businessType 业务类型,
     * @param string $startTime 开始时间,只传开始时间时用以insert、update、delete操作;同时传结束时间用以select操作
     * @param string $endTime 结束时间
     * @param string $alias 表名别名,用以select操作时各表union后的别名,默认使用模型的name属性
     */
    public function partitionYm($businessType, $startTime, $endTime = null, $alias = null)
    {
        // 开始和结束时间都不传时返回自身
        if (empty($startTime) && empty($endTime)) {
            return $this;
        }

        if (empty($endTime)) {
            $seq = date('Ym', strtotime($startTime));
            $tableName = $this->getTable() . '_' . $businessType . '_' . $seq;
            if (!$this->tableExists($tableName)) {
                $this->createPartitionTable($tableName);
            }
        } else {
            $startDate = new \DateTime($startTime);
            $endDate = new \DateTime($endTime);
            $endDate->modify('last day of this month');

            $interval = $startDate->diff($endDate, true);

            $startTime = strtotime($startTime);
            $startYM = date('Ym', $startTime);
            $tables = [];
            for ($i = 0; $i <= $interval->m; $i++) {
                $table = $this->getTable() . '_' . $businessType . '_' . $startYM;
                if (!$this->tableExists($table)) {
                    continue;
                }
                $tables[] = "SELECT * FROM {$table}";
                $startTime = strtotime("+1 month", $startTime);
                $startYM = date('Ym', $startTime);
            }

            // 时间区间内没有相应的数据表时返回自身
            $tableName = '(' . (empty($tables) ? 'SELECT * FROM ' . $this->getTable() : implode(" UNION ", $tables)) . ') ' . ($alias ?: $this->name);
            dump($tableName);
        }

        $this->table = $tableName;
        return $this;
    }
}

 

3.Chat模型里使用Partition

模型路径:application\model\Chat.php

<?php

namespace app\model;

use app\traits\Partition;

class Chat extends Base
{
    use Partition;
}

 

4.控制器使用Chat模型

控制器路径:application\index\controller\Index.php

<?php

namespace app\index\controller;

use app\model\Chat as ChatModel;

class Index
{
    public $businessType = 1;
    public function index()
    {
        $model = new ChatModel;
        $res = $model->partitionYm($this->businessType, '2024-06-25', '2024-09-01', 'chat')
            ->leftJoin('user u', 'chat.uid = u.id')
            ->field(['chat.*', 'u.name'])
            ->order(['chat.create_time' => 'desc', 'chat.id' => 'desc'])
            ->select();
        dump($res);

        return 'index';
    }

    public function save()
    {
        $time = date('Y-m-d H:i:s');
        $data = [
            'uid'   => 2,
            'content' => 'think',
            'create_time' => $time,
        ];


        $model = new ChatModel;
        $model->partitionYm($this->businessType, $time)->insert($data);
        return 'save';
    }
}

 

标签:tableName,实践,value,rule,startTime,分表,thinkphp5.1,id
From: https://www.cnblogs.com/tros/p/18303135

相关文章

  • 深度解析:分库分表策略在数据库性能优化中的核心作用
        目录分库分表的核心原理分库(Sharding)分表(Partitioning)综合运用与挑战在探讨分库分表的深度理解之前,先回顾一下为什么数据库系统会面临性能瓶颈。随着互联网业务的飞速发展,数据量呈指数级增长,同时高并发的访问需求对数据库的读写性能提出了更高要求。传统的......
  • PHP转Go系列 | ThinkPHP与Gin框架之OpenApi授权设计实践
    大家好,我是码农先森。我之前待过一个做ToB业务的公司,主要是研发以会员为中心的SaaS平台,其中涉及的子系统有会员系统、积分系统、营销系统等。在这个SaaS平台中有一个重要的角色「租户」,这个租户可以拥有一个或多个子系统的使用权限,此外租户还可以使用平台所提供的开放API......
  • 第二部分:关键技术领域的开源实践【企业Maven私服】
    企业Maven私服主要解决了以下几个关键的痛点需求:网络访问限制:许多企业出于安全考虑,限制了对外部网络的访问,尤其是对公网上的远程仓库如Maven中央仓库的直接访问。Maven私服作为内部的仓库,可以替代对中央仓库的依赖,使得即使在没有互联网连接的情况下,开发人员也可以获取到所需的......
  • 2024华为云客服AI助手的大模型实践与思考(免费下载)
    【1】亲爱的读者,如果您想要下载文章完整版,请关注公众号并转发本文至您的微信朋友圈【2】公众号后台发送2024华为云客服AI助手的大模型实践与思考【3】即可获取本文对应的PDF学习文档。  ......
  • 分库分表策略深入解析:基于范围(Range)、基于哈希(Hash)以及基于映射表(Mapping Table)
    目录前言   1.基于范围的分库分表(Range)2.基于哈希的分库分表(Hash)3.基于映射表的分库分表(MappingTable)前言     分库分表是数据库优化中的一项重要技术,它通过将数据分散到多个数据库或表中,以提高系统的处理能力和响应速度。本篇将详细解析三种常见的分库......
  • Java中的异常处理最佳实践
    Java中的异常处理最佳实践大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!异常处理的基本原则在Java编程中,异常处理是保证程序健壮性和可靠性的重要手段之一。良好的异常处理实践可以提升代码的可读性和维护性,下面我们来深入探讨Java中的异常处理最佳实......
  • 第二部分:关键技术领域的开源实践【在线办公协同利器OnlyOffice】
    OnlyOffice是一个开源的文档编辑和协作平台,它提供了一套完整的在线办公解决方案,包括文字处理、电子表格、演示文稿编辑以及项目管理和客户关系管理(CRM)功能。OnlyOffice的主要特点在于其深度集成能力和高度定制性,允许企业将其集成到现有的企业资源规划(ERP)、内容管理系统(CMS)和其他......
  • 第二部分:关键技术领域的开源实践【分布式版本控制系统Git】
    企业源代码管理的重要性体现在多个方面,它不仅关乎软件开发的效率和质量,也是保护企业核心资产和维持竞争优势的关键。以下是一些主要的重要性点:版本控制:源代码管理确保每一次代码的修改都被记录和保存,这使得开发团队可以追踪任何变更,回滚到以前的状态,或者比较不同版本之间的差异......
  • 第二部分:关键技术领域的开源实践【内网穿透FRP】
    FRP简介FRP(FastReverseProxy)作为一种高性能的内网穿透工具,支持TCP、UDP、HTTP、HTTPS等多种协议。可以将内网服务以安全、便捷的方式通过具有公网IP节点(云服务器)的中转暴露到公网。为什么使用FRP?以下是一些常见的企业级应用场景:远程办公和远程桌面允许员工从外部网......
  • scrum最佳实践
    1. 明确且专注的产品愿景:产品负责人应清晰定义产品的长期愿景和目标,以便为每个Sprint提供明确的方向。2. 精心维护产品待办事项列表:产品待办事项列表要详细、准确且按优先级排序,反映出业务价值和客户需求。3. 合适的Sprint长度:根据项目特点和团队能力,选择合适的Sprint......