决策:如何设置连接池?
每个数据库连接都有内存和调度开销;连接数不受控制地增长会使服务器变慢,甚至宕机。连接池通过复用少量持久连接来解决这一问题。这棵决策树指导你决定连接池的位置、使用哪种模式(session/transaction/statement)以及合适的大小,并强调为什么在 serverless 环境中必须使用外部连接池。
你的应用架构如何连接数据库?
连接池的决策从架构开始。是单个长期运行的应用进程(传统服务器),许多独立的进程/实例(微服务、大量 pod),还是随每个请求创建和销毁的 serverless/Lambda 函数?每种情况需要不同的连接池部署位置。
- 一个/少数几个长期运行的应用进程
- 许多进程/实例/pod 连接同一个数据库
- Serverless / Lambda / 短生命周期函数
- 先告诉我如何选择合适的连接池大小
如何选择中间件连接池的模式?
客户端很多时,中间件连接池(PgBouncer、ProxySQL)可以使数据库上的连接数保持较低。但池模式取决于应用使用的功能:你是否依赖与会话绑定的状态(prepared statement、临时表、SET、advisory lock、会话级功能),还是所有操作都在单个事务内完成?
- 我依赖会话状态(SET、临时表、会话级 prepared statement)
- 事务简短且独立;我不保留会话状态
建议:应用内连接池
对于一个/少数几个长期运行的进程,应用内连接池库(HikariCP、Go database/sql 的内置连接池、.NET connection pooling)可以满足大多数需求。连接只打开一次,然后反复复用。
建议:正确设置连接池大小
连接池既不应太小(请求需等待),也不应太大。连接过多会造成 CPU 上下文切换和锁争用,降低吞吐量。池大小不是一个可以无限增大的数字,而是基于核心数和 I/O 的一个较小数值。
建议:Session 池模式
在 session 模式下,客户端连接在其会话结束前固定绑定到一个后端服务器连接。会话级状态(SET、临时表、advisory lock、会话级 prepared statement)得以保留,但连接共享程度最低。
建议:Transaction 池模式
在 transaction 模式下,服务器连接仅在一个事务期间借给客户端,事务结束后归还连接池。因此成千上万的客户端可以使用数据库上极少的真实连接工作;这是最常见的大规模模式。
建议:serverless 必须使用外部连接池
Serverless/Lambda 函数扩展迅速,每个实例都会打开自己的连接;应用内连接池在这里无济于事,因为实例生命周期短且互不共享。当成百上千个实例突然启动时,数据库连接上限会被突破。解决方法是在数据库前面部署外部连接池。