首页 / 资讯中心 / 文章详情

DBProxy入门实战:从连接池耗尽到读写分离与分库分表配置

DBProxy入门实战:从连接池耗尽到读写分离与分库分表配置 ★ FEATURED ARTICLE
1. 从一次连接池耗尽说起DBProxy到底解决什么问题我第一次接触DBProxy是因为一个线上服务在晚高峰突然大量报错日志里全是“too many connections”。当时第一反应是加数据库最大连接数但运维同事直接拦住我说数据库侧连接数已经到瓶颈再加只会把数据库拖垮。问题的根子在于应用层每个实例都各自维护连接池实例一扩容连接数就线性上涨数据库根本扛不住。DBProxy这类中间件要解决的核心问题就是在应用和数据库之间插入一个代理层把连接管理、读写分离、负载均衡这些事从应用侧收上来统一处理。应用不再直连数据库而是连到DBProxy由它来维护到后端数据库的真实连接。这样一来几十个应用实例可能只需要对应十几个后端连接连接数被收敛了。你可以把它理解成一个“数据库流量的调度中心”。应用发过来的SQL先到DBProxy它根据配置决定这条SQL该走主库还是从库、该发到哪个后端节点、要不要做结果合并。对应用来说它只是连了一个普通的MySQL地址完全感知不到后面有一堆数据库在支撑。这篇内容适合几类人看一是刚接触数据库中间件、想快速把DBProxy跑起来的开发者二是正在做读写分离或分库分表、需要选型代理层的架构同学三是遇到连接数瓶颈、想找解决方案的运维。我会从部署、配置、路由规则、踩坑几个角度把DBProxy的入门路径讲清楚尽量让你看完就能动手搭一套能用的环境。需要先说明一点DBProxy是一个泛指市面上有多个同类实现本文讲的是基于MySQL协议、支持读写分离和分库分表的通用代理方案具体配置项名称可能因实现不同略有差异但核心思路是相通的。你照着思路走换成具体某个实现也能快速对上号。2. 部署前必须想清楚的三个选型问题2.1 代理层放在哪独立部署还是SidecarDBProxy的部署位置直接决定了它的可用性和运维复杂度。最常见的做法是独立部署也就是找几台机器专门跑DBProxy应用通过网络连过去。这种模式的好处是集中管理配置改一处就生效监控也方便。坏处是DBProxy本身成了单点一旦挂了所有应用都连不上数据库所以通常要部署多个实例前面再挂一层负载均衡。另一种是Sidecar模式把DBProxy跟应用部署在同一台机器或同一个容器里应用连本地的DBProxy。这种模式延迟最低因为走的是本地回环而且每个应用实例有自己的代理不会因为代理故障影响全局。但缺点是配置分散几十个实例就要管几十份配置升级也麻烦。我个人的经验是中小规模优先选独立部署实例数控制在3到5个配合健康检查和自动摘除基本够用。规模特别大、对延迟极度敏感的场景再考虑Sidecar。选型时还要考虑一个点你的应用是否支持连接地址动态变更。如果应用配置写死了数据库地址那代理层换IP就是一场灾难这种情况建议在应用和代理之间再加一层域名或服务发现。2.2 后端节点怎么分组主从角色要标清楚DBProxy要做的第一件事就是知道哪个节点是主库、哪些是从库。配置里通常会给每个后端节点打上角色标签比如master、slave或者用权重来表示。这里有个容易忽略的细节主库和从库的地址最好用不同的端口或不同的账号区分避免配置写错导致写请求打到从库上。分组的时候还要考虑机房和可用区。如果主库在A机房、从库在B机房跨机房访问的延迟可能比同机房高一个数量级。DBProxy一般支持按权重分配读请求你可以把同机房的从库权重调高跨机房的调低这样大部分读请求走本地只有本地从库挂了才切到远端。另外从库的延迟是个绕不开的问题。主从复制有延迟刚写入的数据可能从库还查不到。DBProxy通常提供“强制走主库”的开关对于写后立即读的场景可以在SQL上加hint或者走特定账号强制路由到主库。这个机制后面讲路由规则时会细说。2.3 连接池参数不是越大越好DBProxy到后端数据库的连接池大小是入门阶段最容易配错的地方。很多人觉得连接数开大点总没错实际上后端连接池的总大小应该小于数据库的max_connections而且要留出余量给运维和监控使用。举个例子假设数据库max_connections是1000你有3个DBProxy实例每个实例到主库的连接池如果配500那3个实例就是1500直接超了。正确的算法是单实例连接池大小 (数据库最大连接数 - 预留连接数) / DBProxy实例数。预留连接数一般留100到200给监控、备份、人工登录用。连接池还有两个关键参数最小空闲连接和最大空闲时间。最小空闲连接保证有请求进来时不用现建连接减少握手开销最大空闲时间则避免连接长时间不用被数据库或防火墙断开。我一般把最小空闲连接设为最大连接数的20%左右最大空闲时间设为数据库wait_timeout的80%这样连接会在被数据库踢掉之前主动回收重建。3. 从零搭一套能跑的DBProxy环境3.1 环境准备与依赖检查动手之前先把基础环境确认一遍。DBProxy通常依赖一些基础库比如网络库、配置解析库具体取决于实现语言。以常见的C/C实现为例需要确认系统里有gcc、make、cmake以及libmysqlclient的开发包。如果是Go或Java实现依赖会少一些但也要确认运行时版本。数据库侧要提前准备好一个主库、至少一个从库主从复制已经跑通。可以用show slave status确认从库的Slave_IO_Running和Slave_SQL_Running都是Yes。还要在主库上创建一个给DBProxy用的账号权限至少要有SELECT、INSERT、UPDATE、DELETE如果要做分库分表可能还需要CREATE、DROP等DDL权限。网络方面确认DBProxy所在机器能连通所有后端数据库的端口数据库的防火墙或安全组要放行DBProxy的IP。这一步经常被忽略结果配置都对了但就是连不上排查半天发现是安全组没开。3.2 配置文件逐项拆解DBProxy的配置文件一般分几块监听配置、后端节点配置、路由规则配置、连接池配置。我拿一个典型的配置结构来逐项说明具体字段名以你用的实现为准。监听部分要指定DBProxy对外服务的地址和端口比如0.0.0.0:6033。这里建议监听0.0.0.0而不是127.0.0.1除非你确定只允许本机访问。端口不要用3306避免和本机数据库冲突6033是很多代理默认用的端口。后端节点配置里每个节点要有名字、地址、端口、角色、权重。角色决定它能不能接受写请求权重决定读请求的分配比例。比如主库权重设为0不参与读两个从库权重各50这样读请求就平均分到两个从库上。路由规则是DBProxy的灵魂。最简单的规则是“写走主库、读走从库”但实际场景往往更复杂。比如有些读请求必须走主库强一致读有些写请求要按分片键路由到特定库。配置里通常用正则或SQL类型来匹配匹配到之后指定目标节点组。连接池配置要分别设置到主库和从库的参数。到主库的连接池可以小一些因为写请求通常比读少到从库的连接池要大一些因为读请求量大。每个池子都要设最大连接数、最小空闲数、连接超时、空闲超时这几个参数。3.3 启动与连通性验证配置写好后先用--check-config之类的参数做一次配置校验很多实现都提供这个功能能在启动前发现语法错误或字段缺失。校验通过再正式启动启动后看日志确认监听端口起来了、后端节点都连上了。验证分几步走。第一步用mysql客户端连到DBProxy的端口执行select 1能返回就说明代理层通了。第二步执行一条写语句比如建一张测试表插一条数据然后到主库上确认数据写进去了。第三步执行读语句到从库上确认能查到数据。第四步故意把主库停掉看DBProxy是否能把写请求正确报错、读请求是否还能走从库。这一步有个小技巧在DBProxy上开一个管理端口或管理命令很多实现支持通过特定SQL查看后端节点状态、连接池使用情况。比如执行show proxy status之类的命令能看到每个节点的连接数、请求数、延迟。这个在排查问题时非常有用建议入门阶段就把它摸熟。4. 读写分离与路由规则的实战配置4.1 读写分离的基本逻辑与边界读写分离听起来简单写走主、读走从但实际落地时边界条件不少。最典型的是事务内的读。一个事务里如果先写后读这个读必须走主库否则可能读不到刚写的数据。DBProxy一般会识别事务状态事务开始后的所有请求都路由到主库直到事务提交或回滚。另一个边界是写后立即读。应用写完一条数据马上要查出来返回给前端这种读如果走从库可能因为复制延迟查不到。解决办法有两种一是在SQL上加hint强制走主库二是用支持“会话一致性”的账号同一个连接上的写后读自动走主库。前者需要改应用代码后者对应用透明但需要DBProxy支持。还有一种情况是批量读。有些报表查询会扫大量数据走从库没问题但如果从库延迟很高查出来的数据可能不是最新的。这种场景要看业务能不能接受延迟不能接受就只能走主库但要做好主库压力评估。4.2 分库分表场景下的路由键选择如果只是读写分离DBProxy的配置相对简单。一旦涉及分库分表路由键的选择就成了核心问题。路由键就是用来决定一条SQL该发到哪个分片的字段通常选业务上的高频查询字段比如用户ID、订单ID。选路由键有几个原则。第一尽量让同一个业务实体的数据落在同一个分片比如同一个用户的所有订单放一起这样按用户查订单只需要访问一个分片。第二路由键要出现在查询条件里如果一条SQL不带路由键DBProxy就不知道该去哪个分片只能广播到所有分片再合并结果性能很差。第三路由键的分布要均匀避免热点分片。配置路由规则时通常用取模或范围的方式。取模适合数据量均匀、查询随机的场景范围适合有时间维度的数据比如按月份分表。取模的配置大概是分片数4路由算法hash(user_id) % 4。范围的话要指定每个分片的边界值。这里有个坑分片数一旦确定后期扩容很麻烦。取模方式下从4个分片扩到8个几乎所有数据都要重新分布。所以入门阶段如果预判数据会增长可以一开始就多分几个片或者选用支持一致性哈希的实现。4.3 强制走主库的几种触发方式前面提到写后读要走主库具体怎么触发不同实现提供了不同方式。最常见的是SQL注释hint比如在SQL前面加/*master*/DBProxy解析到注释就把这条请求路由到主库。这种方式灵活但需要改SQL对应用有侵入。第二种是账号区分给应用配置两个账号一个走从库、一个走主库需要强一致读的时候切换到主库账号。这种方式对SQL无侵入但应用要管理两套连接。第三种是会话变量应用执行set session read_from_master1之后的读都走主库直到重置。这种方式适合一个会话里有一段逻辑需要强一致读的场景。我一般推荐hint方式因为粒度最细只影响单条SQL不会误伤其他请求。但要注意hint的写法要和DBProxy实现匹配写错了会被当成普通注释忽略结果读请求还是走了从库问题很隐蔽。5. 那些文档里不会写的踩坑记录5.1 连接池泄漏一个被忽视的慢查询引发的连锁反应有一次线上DBProxy到从库的连接池突然被打满新请求全部排队超时。查监控发现从库的活跃连接数一直下不来但QPS并不高。最后定位到是一条慢查询执行时间很长占着连接不放而连接池的最大连接数设得偏小几条慢查询就把池子占满了。这个坑的教训是连接池大小要结合慢查询情况来定。如果业务里有偶发慢查询连接池要留出足够的冗余否则慢查询会迅速耗尽连接。另外要配置连接的最大存活时间避免慢查询把连接永久占住。DBProxy一般支持查询超时设置超过时间就杀掉查询释放连接这个参数建议一定要开。排查这类问题的思路是先看DBProxy的连接池监控确认是哪个后端节点的池子满了再到对应数据库上执行show processlist看哪些连接在跑什么SQL、跑了多久找到慢查询后分析执行计划该加索引加索引该改SQL改SQL。5.2 主从延迟导致的“数据消失”假象用户反馈刚提交的订单查不到刷新几次又出来了。这种“数据消失”十有八九是主从延迟导致的。写入走主库成功了但读请求走从库从库还没同步到这条数据所以查不到。等复制追上来了再查就有了。解决这个问题不能靠“让用户多刷新几次”而是要在代码层面识别哪些读需要强一致。我的做法是写操作返回的主键ID在后续的读请求里带上并且对这类读强制走主库。比如下单成功后跳转到订单详情页详情页的查询带上订单ID并走主库这样一定能查到。监控主从延迟也很重要。DBProxy一般能暴露每个从库的延迟秒数可以接入监控系统延迟超过阈值就告警。如果延迟持续很高说明从库压力太大或者复制配置有问题需要从库扩容或优化复制。5.3 配置热更新的陷阱改了配置但没生效DBProxy通常支持配置热更新不用重启就能让新配置生效。但热更新有个陷阱已经建立的连接不会自动切换到新配置。比如你把某个从库的权重从50改成0希望读请求不再打到它但已经连到那个从库的连接还在新的请求可能复用这些连接导致权重没生效。正确的做法是热更新配置后主动断开受影响的连接让它们按新配置重建。有些实现提供“优雅重载”功能会等当前请求处理完再断开连接这样不影响正在进行的业务。如果实现不支持就只能重启DBProxy但要配合负载均衡做滚动重启避免服务中断。还有一个坑是配置文件的权限。热更新通常通过管理接口触发如果管理接口没有鉴权任何人都能改配置这是很大的安全隐患。入门阶段就要把管理接口的访问控制配好只允许内网特定IP访问。6. 性能调优与监控指标怎么看6.1 关键性能指标与正常范围DBProxy的性能监控主要看几个指标QPS、响应时间、连接池使用率、后端节点延迟。QPS反映吞吐响应时间反映用户体验连接池使用率反映资源是否紧张后端延迟反映数据库健康度。响应时间的正常范围取决于业务但一般来说代理层引入的额外延迟应该在毫秒级。如果发现响应时间比直连数据库高很多要排查是不是代理层做了太多解析或结果合并。连接池使用率长期超过80%就要考虑扩容长期低于20%可以考虑缩容。后端节点延迟要区分主库和从库。主库延迟高通常是写入压力大或锁竞争从库延迟高可能是复制慢或读压力大。DBProxy一般会记录每个节点的请求数和平均耗时通过这些数据能快速定位是哪个节点拖了后腿。6.2 慢查询在代理层的识别与处理DBProxy作为流量的必经之路是识别慢查询的好位置。它可以记录每条SQL的执行时间超过阈值的打上慢查询标记。这个功能比数据库自带的慢查询日志更灵活因为可以在代理层做采样避免慢查询日志把磁盘写满。配置慢查询阈值时要结合业务。OLTP场景一般设100毫秒到500毫秒OLAP场景可以放宽到几秒。识别到慢查询后DBProxy可以只记录不处理也可以主动杀掉。我建议入门阶段先只记录观察一段时间确认哪些慢查询是必须优化的再决定要不要开自动杀。慢查询记录里要包含足够的信息SQL文本、执行时间、目标节点、客户端地址。有了这些信息排查时能直接定位到是哪个应用、哪条SQL、打到了哪个库。6.3 连接池参数的动态调整思路连接池参数不是配一次就完事要根据业务变化动态调整。调整的依据主要是监控数据如果连接池使用率长期高位说明最大连接数不够如果最小空闲连接经常被用光说明预热不够如果连接频繁创建销毁说明空闲超时设得太短。调整时要小步走每次改一个参数观察一段时间再改下一个。比如先把最大连接数提高20%看响应时间和数据库负载的变化稳定了再考虑下一步。切忌一次改多个参数出了问题都不知道是哪个参数导致的。还有一个经验不同后端节点的连接池参数可以不同。主库连接池可以小一些因为写请求少从库连接池要大一些因为读请求多。如果某个从库性能差一些它的连接池可以配小一点避免过多请求压垮它。7. 从单实例到高可用进阶部署思路7.1 多实例部署与负载均衡单实例DBProxy最大的问题是单点故障。解决办法是部署多个实例前面挂负载均衡。负载均衡可以用四层TCP也可以用七层MySQL协议感知。四层简单但无法感知后端DBProxy的健康状态七层能感知但配置复杂一些。多实例部署后配置管理成了新问题。几个实例的配置必须一致否则请求打到不同实例可能得到不同结果。解决办法是用配置中心统一管理DBProxy启动时从配置中心拉取配置配置变更时推送到所有实例。这样改一处就全生效避免手工同步出错。负载均衡的健康检查要配好。检查方式一般是定期连一下DBProxy的端口能连上就认为健康。更严格的检查是执行一条简单SQL比如select 1能返回才认为健康。检查间隔不要太短避免频繁探测增加负担也不要太长避免故障实例迟迟不被摘除。7.2 故障转移与自动摘除后端数据库故障时DBProxy要能自动把故障节点摘除把请求转到健康节点。主库故障的转移比较复杂因为写请求不能自动切到从库除非有主从切换机制。DBProxy一般只负责检测到主库故障后报错切换由外部的高可用组件完成。从库故障的转移相对简单DBProxy检测到从库不可用后把它的权重设为0读请求自动转到其他从库。检测方式可以是定期心跳也可以是根据请求失败率判断。心跳检测更及时但会增加一点网络开销失败率判断更省资源但故障发现会慢一些。自动摘除后还要有自动恢复。故障节点恢复后DBProxy要能重新把它加入节点组。加入前最好做一次健康检查确认它真的可用了再加入避免刚恢复又挂掉导致请求反复失败。7.3 版本升级与配置回滚DBProxy本身也需要升级升级过程中要不影响业务。滚动升级是常用方式先摘除一个实例升级完再加入然后升级下一个。升级前要确认新版本兼容旧配置避免升级后配置解析失败。配置回滚同样重要。每次改配置前先备份改完后观察一段时间确认没问题再继续。如果发现异常能快速回滚到上一个版本。配置中心一般支持版本管理可以一键回滚但回滚后要确认所有实例都拉到了旧配置。升级和回滚都要有预案。比如升级后发现性能下降要能快速切回旧版本回滚后发现配置没生效要能手动触发同步。这些操作最好在测试环境演练一遍确认流程顺畅再上生产。8. 一些个人体会和后续可扩展的方向DBProxy入门阶段最容易犯的错是“配完就不管了”。实际上代理层的配置和数据库一样需要持续观察和调整。我自己的习惯是每周看一次连接池使用率和慢查询记录每月做一次配置回顾看看有没有可以优化的参数。另一个体会是不要指望DBProxy解决所有数据库问题。它擅长连接管理、读写分离、分库分表路由但不擅长SQL优化、索引设计、事务管理。这些还是要回到数据库本身去解决。代理层是锦上添花不是雪中送炭。后续如果要把这套环境用得更深入可以往几个方向扩展一是接入监控告警把关键指标可视化二是做压测摸清代理层的性能上限三是研究分库分表后的分布式事务问题这个比读写分离复杂得多。入门先把读写分离跑稳再逐步往深水区走步子迈太大容易扯着。
阅读完成 · 觉得有帮助?
咨询建站