【PostgreSQL从零到精通】第48篇:PL/Proxy数据分片——PostgreSQL的水平扩展利器
上一篇【第47篇】Bucardo多主复制——实现真正的双向数据同步下一篇【第49篇】pgpool-II完全指南——连接池复制负载均衡的三合一方案单台 PostgreSQL 服务器的读写能力总有一个上限。当数据量达到 TB 级别、并发请求达到数万 QPS 时再怎么优化硬件也无济于事——你需要水平扩展把数据分散到多台服务器上。PL/Proxy就是 PostgreSQL 官方提供的数据分片方案通过函数级别的路由实现透明分片。一、PL/Proxy 概述1.1 什么是 PL/ProxyPL/Proxy 的核心理念 传统方式单机 ┌──────────────────────┐ │ PostgreSQL 单机 │ │ 所有数据在一个库中 │ → 瓶颈 └──────────────────────┘ PL/Proxy 方式分片 ┌─────────────┐ │ PL/Proxy │ ← 接收客户端请求根据分片规则路由 │ (路由节点) │ └──────┬──────┘ ┌────┼────────┐ ↓ ↓ ↓ ┌────┐┌────┐ ┌────┐ │ 分片0││ 分片1│ │ 分片2│ ← 数据分散存储 │ DB 0 ││ DB 1 │ │ DB 2 │ └─────┘└─────┘ └─────┘ 客户端完全无感知像使用一个数据库一样1.2 PL/Proxy 特性PL/Proxy 的关键特性 ┌──────────────────────────────────────────────────────────────┐ │ ✅ 函数级透明路由调用远程函数就像调用本地函数 │ │ ✅ 支持多种分片策略哈希、范围、自定义 │ │ ✅ 轻量级本身很轻只做路由不做存储 │ │ ✅ 支持并行查询SPLIT 参数可并行查询所有分片 │ │ ✅ 数据对客户端完全透明 │ │ ⚠️ 只能分片函数调用不能分片裸 SQL │ │ ⚠️ 不支持分布式事务跨分片事务需要应用层处理 │ │ ⚠️ 需要预先设计分片规则 │ └──────────────────────────────────────────────────────────────┘二、安装与配置2.1 安装# 从源码编译安装gitclone https://github.com/postgres/plproxy.gitcdplproxy# 确保已安装 PostgreSQL 开发包makeUSE_PGXS1sudomakeUSE_PGXS1install# 验证安装psql-cCREATE EXTENSION plproxy;2.2 基础配置-- 在路由节点上创建扩展CREATEEXTENSION plproxy;-- PL/Proxy 使用 libpq 连接后端数据库-- 连接字符串格式-- hostxxx portxxx dbnamexxx userxxx passwordxxx三、PL/Proxy 函数语言3.1 核心语法-- PL/Proxy 是一种过程语言-- 所有函数都需要用 PL/Proxy 语言编写-- 基本结构CREATEORREPLACEFUNCTIONremote_insert(p_user_idinteger,p_nametext,p_emailtext)RETURNSvoidAS$$CONNECThostshard0 dbnamemydb userproxy_user passwordxxx;TARGET remote_insert;-- 调用远程库的同名函数$$LANGUAGEplproxy;3.2 关键参数详解PL/Proxy 函数中的关键声明 ┌─────────────────────────────────────────────────────────────────┐ │ CONNECT 连接字符串 │ │ 指定连接到哪个后端数据库 │ │ 可以是固定连接字符串也可以使用集群名 │ │ │ │ CLUSTER 集群名 │ │ 使用预定义的集群连接推荐 │ │ │ │ TARGET 函数名 │ │ 指定在后端执行的函数名通常与当前函数同名 │ │ │ │ RUN ON 分片编号 │ │ 指定运行在哪个分片上0, 1, 2 ... │ │ 可以是固定值也可以是动态表达式 │ │ │ │ SPLIT │ │ 并行在所有分片上执行合并结果 │ │ │ │ SET parameter │ │ 设置后端连接参数 │ └─────────────────────────────────────────────────────────────────┘四、数据分片实战4.1 定义集群-- 方式1直接 CONNECT简单但不够灵活CREATEORREPLACEFUNCTIONget_user(p_idint)RETURNSSETOF usersAS$$CONNECThostshard0 dbnamemydb;TARGET get_user;$$LANGUAGEplproxy;-- 方式2使用 CLUSTER推荐集中管理连接-- 创建集群配置CREATEORREPLACEFUNCTIONplproxy_get_cluster_config(p_clustertext,p_outtext)RETURNSSETOFtextAS$$BEGINIFp_clustermy_shardsTHEN-- 返回集群中所有分片的连接信息RETURNNEXThostshard0 dbnamemydb userproxy_user;RETURNNEXThostshard1 dbnamemydb userproxy_user;RETURNNEXThostshard2 dbnamemydb userproxy_user;RETURN;ENDIF;RAISE EXCEPTIONUnknown cluster: %,p_cluster;END;$$LANGUAGEplpgsql SECURITYDEFINER;4.2 哈希分片-- 基于用户 ID 的哈希分片-- 将请求路由到对应的分片-- 1. 先创建一个获取分片数量的函数CREATEORREPLACEFUNCTIONget_shard_count()RETURNSintegerAS$$SELECT3;-- 3个分片$$LANGUAGEsql;-- 2. 创建哈希分片路由函数CREATEORREPLACEFUNCTIONget_user(p_user_idinteger)RETURNSSETOF usersAS$$ CLUSTERmy_shards;RUNONhashtext(p_user_id::text)%(SELECTget_shard_count());TARGET get_user;$$LANGUAGEplproxy;-- 使用示例-- SELECT * FROM get_user(42);-- PL/Proxy 会计算 hashtext(42) % 3自动路由到对应分片4.3 SPLIT 并行查询-- 并行查询所有分片并合并结果-- 适用于聚合查询、报表查询等-- 获取所有分片的用户总数CREATEORREPLACEFUNCTIONget_total_user_count()RETURNSbigintAS$$ CLUSTERmy_shards;SPLIT;TARGET get_total_user_count;$$LANGUAGEplproxy;-- PL/Proxy 会并行在 shard0、shard1、shard2 上执行-- 然后将所有分片的结果合并返回4.4 完整分片示例-- 后端分片数据库shard0/shard1/shard2-- 创建实际存储表CREATETABLEusers(user_idSERIALPRIMARYKEY,nametextNOTNULL,emailtextUNIQUE,created_at timestamptzDEFAULTnow());-- 创建后端处理函数CREATEORREPLACEFUNCTIONinsert_user(p_nametext,p_emailtext)RETURNSintegerAS$$INSERTINTOusers(name,email)VALUES(p_name,p_email)RETURNINGuser_id;$$LANGUAGEsql;CREATEORREPLACEFUNCTIONget_user(p_user_idinteger)RETURNSSETOF usersAS$$SELECT*FROMusersWHEREuser_idp_user_id;$$LANGUAGEsql;-- 路由节点PL/Proxy-- 写入路由根据 name 的哈希分片CREATEORREPLACEFUNCTIONinsert_user(p_nametext,p_emailtext)RETURNSintegerAS$$ CLUSTERmy_shards;RUNONhashtext(p_name)%(SELECTget_shard_count());TARGET insert_user;$$LANGUAGEplproxy;-- 读取路由需要知道数据在哪个分片CREATEORREPLACEFUNCTIONget_user(p_user_idinteger)RETURNSSETOF usersAS$$ CLUSTERmy_shards;RUNONhashtext(p_user_id::text)%(SELECTget_shard_count());TARGET get_user;$$LANGUAGEplproxy;-- 并行查询CREATEORREPLACEFUNCTIONsearch_users(p_name_patterntext)RETURNSSETOF usersAS$$ CLUSTERmy_shards;SPLIT;TARGET search_users;$$LANGUAGEplproxy;五、分片策略选择5.1 常见分片方式分片策略对比 ┌──────────┬──────────────────────┬──────────────────────┐ │ 分片方式 │ 适用场景 │ 优缺点 │ ├──────────┼──────────────────────┼──────────────────────┤ │ 哈希分片 │ 用户ID、订单ID等 │ 分布均匀扩容需重哈希│ │ 范围分片 │ 时间范围、地区 │ 范围查询方便热点风险│ │ 列表分片 │ 按业务线/客户类型 │ 灵活需要提前规划 │ │ 一致性哈希│ 动态扩缩容 │ 扩容平滑实现复杂 │ └──────────┴──────────────────────┴──────────────────────┘5.2 分片键选择原则选择分片键的原则 1. ✅ 数据分布均匀避免热点 2. ✅ 查询条件经常使用减少跨分片查询 3. ✅ 不可变或极少变化避免数据迁移 4. ❌ 不要选择自增 ID所有新数据都会到同一个分片 5. ❌ 不要选择低基数列如性别、状态等只有几个值六、高可用设计6.1 分片 主从复制PL/Proxy 流复制 高可用架构 ┌──────────────┐ │ PL/Proxy │ │ (路由节点) │ │ ×2 (HA) │ └──────┬───────┘ ┌───────────┼───────────┐ ↓ ↓ ↓ ┌──────┐ ┌──────┐ ┌──────┐ │ 分片0 │ │ 分片1 │ │ 分片2 │ │ Master│ │ Master│ │ Master│ └───┬───┘ └───┬───┘ └───┬───┘ ↓ ↓ ↓ ┌──────┐ ┌──────┐ ┌──────┐ │ 分片0 │ │ 分片1 │ │ 分片2 │ │ Slave │ │ Slave │ │ Slave │ └──────┘ └──────┘ └──────┘ 每个分片自身做流复制主从 PL/Proxy 节点也可以做 HA如 PgBouncer 负载均衡6.2 PL/Proxy 节点高可用-- CLUSTER 配置支持多个地址-- PL/Proxy 会自动尝试连接可用的节点CREATEORREPLACEFUNCTIONplproxy_get_cluster_config(p_clustertext,p_outtext)RETURNSSETOFtextAS$$BEGINIFp_clustermy_shardsTHEN-- 每个分片提供主备两个地址RETURNNEXThostshard0_master dbnamemydb userproxy_user;RETURNNEXThostshard1_master dbnamemydb userproxy_user;RETURNNEXThostshard2_master dbnamemydb userproxy_user;RETURN;ENDIF;RAISE EXCEPTIONUnknown cluster;END;$$LANGUAGEplpgsql SECURITYDEFINER;七、PL/Proxy 的限制PL/Proxy 的主要限制 ┌──────────────────────────────────────────────────────────────┐ │ 1. 只能通过函数访问数据不支持直接 SQL 查询分片 │ │ 2. 不支持分布式事务跨分片 ACID │ │ 3. 跨分片 JOIN 需要在应用层实现 │ │ 4. 不支持 DDL 自动下发到分片 │ │ 5. 扩容需要数据迁移重新分片 │ │ 6. SPLIT 结果合并有限制返回类型必须一致 │ │ 7. 社区活跃度不高更新较慢 │ └──────────────────────────────────────────────────────────────┘ 与现代方案对比 - PostgreSQL 原生分区表声明式分区适合单机大表 - PL/Proxy 适合需要多机水平扩展的场景 - Citus扩展更适合现代分布式 PostgreSQL 需求八、总结PL/Proxy是 PostgreSQL 官方的函数级分片路由方案核心机制CLUSTER 定义连接池 → RUN ON 路由到指定分片 → SPLIT 并行查询分片键选择高基数、均匀分布、不可变、常用查询条件高可用分片本身做流复制主从PL/Proxy 节点做 HA局限不支持分布式事务、跨分片 JOIN复杂场景建议考虑 Citus下一篇我们学习pgpool-II完全指南——连接池复制负载均衡的三合一方案。标签PostgreSQL、PL/Proxy、数据分片、水平扩展、分布式数据库上一篇【第47篇】Bucardo多主复制——实现真正的双向数据同步下一篇【第49篇】pgpool-II完全指南——连接池复制负载均衡的三合一方案
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2592814.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!