Hyperf方案 分库分表实现

张开发
2026/4/7 6:29:11 15 分钟阅读

分享文章

Hyperf方案 分库分表实现
?php/** * 案例标题分库分表实现 * 说明基于用户ID取模实现分表路由水平分片存储海量订单数据 * 需要安装的包 * composer require hyperf/db-connection */declare(strict_types1);// app/Sharding/ShardingStrategy.php namespaceApp\Sharding;/** * 分片策略根据用户ID决定用哪个库、哪张表 * 分表规则用户ID % 分片数 表后缀 */classShardingStrategy{privateint$tableShards8;// 分8张表orders_0 ~ orders_7privateint$dbShards2;// 分2个库db0 db1可选单库分表也行/** * 根据用户ID算出用哪张表 * 同一个用户的所有订单都在同一张表里便于按用户查 */publicfunctiongetTableName(int$userId,string$baseTableorders):string{$suffix$userId%$this-tableShards;// 取模0-7return{$baseTable}_{$suffix};// 比如 orders_3}/** * 根据用户ID算出用哪个数据库连接 */publicfunctiongetConnection(int$userId):string{$suffixintdiv($userId%$this-tableShards,$this-tableShards/$this-dbShards);returndb{$suffix};// 比如 db0 或 db1}/** * 获取所有分片的连接表名组合全表扫描时用 */publicfunctiongetAllShards(string$baseTableorders):array{$shards[];for($i0;$i$this-tableShards;$i){$dbIdxintdiv($i,$this-tableShards/$this-dbShards);$shards[][connectiondb{$dbIdx},table{$baseTable}_{$i}];}return$shards;}}// app/Repository/ShardingOrderRepository.php namespaceApp\Repository;useApp\Sharding\ShardingStrategy;useHyperf\DbConnection\Db;/** * 分表订单Repository * 所有对orders表的操作都通过这里屏蔽分表逻辑 */classShardingOrderRepository{publicfunction__construct(privateShardingStrategy$sharding){}/** * 新建订单根据用户ID路由到对应分表 */publicfunctioncreate(array$data):int{$userId$data[user_id];$table$this-sharding-getTableName($userId);// 算出表名$conn$this-sharding-getConnection($userId);// 算出连接名returnDb::connection($conn)// 用指定数据库连接-table($table)// 用指定分表-insertGetId($data[created_atdate(Y-m-d H:i:s)]);}/** * 按用户ID查订单直接定位到那张分表 */publicfunctionfindByUserId(int$userId,int$page1,int$perPage20):array{$table$this-sharding-getTableName($userId);$conn$this-sharding-getConnection($userId);returnDb::connection($conn)-table($table)-where(user_id,$userId)-orderByDesc(id)-forPage($page,$perPage)// 分页-get()-toArray();}/** * 按订单ID查订单订单ID里编码了分表信息 * 订单ID格式用户ID(8位) 时间戳(10位) 随机(4位) */publicfunctionfindById(string$orderId):?object{// 从订单ID解析出用户ID进而找到分表$userId(int)substr($orderId,0,8);// 取前8位是用户ID$table$this-sharding-getTableName($userId);$conn$this-sharding-getConnection($userId);returnDb::connection($conn)-table($table)-find($orderId);}/** * 生成带分片信息的订单号 * 把用户ID编码进订单号方便反查分片 */publicfunctiongenerateOrderNo(int$userId):string{returnsprintf(%08d%010d%04d,$userId,// 8位用户ID前缀0补齐time(),// 10位时间戳rand(1000,9999)// 4位随机数防重复);// 总共22位订单号}/** * 跨分片查询全局查询性能差能避免就避免 * 比如管理后台按状态查所有分片的订单 */publicfunctionfindAcrossShards(string$status,int$limit100):array{$allResults[];$shards$this-sharding-getAllShards(orders);foreach($shardsas$shard){// 每个分片查一部分然后合并$rowsDb::connection($shard[connection])-table($shard[table])-where(status,$status)-limit($limit)-get()-toArray();$allResultsarray_merge($allResults,$rows);if(count($allResults)$limit)break;// 够了就停}// 合并后按时间排序各分片的数据混在一起了usort($allResults,fn($a,$b)strcmp($b-created_at,$a-created_at));returnarray_slice($allResults,0,$limit);// 最终只返回limit条}}// config/autoload/database.php 多库配置 return[default[hostenv(DB_HOST,127.0.0.1),/* ... */],db0[hostenv(DB0_HOST,10.0.1.10),/* 分库0 */],db1[hostenv(DB1_HOST,10.0.1.11),/* 分库1 */],];

更多文章