golang中如何配置 sql.DB 以获得更好的性能
有很多很好的教程讨论 Go 的sql.DB类型以及如何使用它来执行 SQL 数据库查询和语句。但它们中的大多数都掩盖了SetMaxOpenConns()、SetMaxIdleConns()和SetConnMaxLifetime()方法——您可以使用它们来配置 的行为sql.DB并改变其性能。
在这篇文章中,我想准确解释这些设置的作用,并展示它们可能产生的(积极和消极)影响。
打开和空闲连接
我将从一些背景开始。
对象sql.DB是许多数据库连接的池,其中包含“使用中”和“空闲”连接。当您使用连接执行数据库任务(例如执行 SQL 语句或查询行)时,连接将被标记为正在使用。任务完成后,连接将标记为空闲。
当您指示sql.DB执行数据库任务时,它将首先检查池中是否有任何空闲连接可用。如果有一个可用,那么 Go 将重用此现有连接,并将其标记为在任务期间正在使用。如果当你需要一个连接时池中没有空闲连接,那么 Go 将创建一个额外的新附加连接。
SetMaxOpenConns 方法
默认情况下,同时打开的连接数(使用中+空闲)没有限制。SetMaxOpenConns()但你可以通过这样的方法实现你自己的限制:
// Initialise a new connection pool
db, err := sql.Open("postgres", "postgres://user:pass@localhost/db")
if err != nil {log.Fatal(err)
}// Set the maximum number of concurrently open connections (in-use + idle)
// to 5. Setting this to less than or equal to 0 will mean there is no
// maximum limit (which is also the default setting).
db.SetMaxOpenConns(5)
在此示例代码中,池现在最大限制为 5 个并发打开的连接。如果所有 5 个连接都已标记为正在使用,并且需要另一个新连接,则应用程序将被迫等待,直到 5 个连接之一被释放并变为空闲。
为了说明更改的影响,MaxOpenConns我运行了基准测试,将最大打开连接设置为 1、2、5、10 和无限制。该基准测试在 PostgreSQL 数据库上执行并行语句,您可以在这个要点INSERT中找到代码。结果如下:
BenchmarkMaxOpenConns1-8 500 3129633 ns/op 478 B/op 10 allocs/op
BenchmarkMaxOpenConns2-8 1000 2181641 ns/op 470 B/op 10 allocs/op
BenchmarkMaxOpenConns5-8 2000 859654 ns/op 493 B/op 10 allocs/op
BenchmarkMaxOpenConns10-8 2000 545394 ns/op 510 B/op 10 allocs/op
BenchmarkMaxOpenConnsUnlimited-8 2000 531030 ns/op 479 B/op 9 allocs/op
PASS
编辑:要明确的是,此基准测试的目的不是模拟应用程序的“现实生活”行为。它只是为了帮助说明sql.DB幕后的行为方式以及更改MaxOpenConns对该行为的影响。
INSERT对于此基准测试,我们可以看到允许的打开连接越多,在数据库上执行操作所需的时间就越少(1 个打开连接的 3129633 ns/op 与无限制连接的 531030 ns/op 相比,大约快 6 倍)。这是因为允许的打开连接越多,可以并发执行的数据库查询就越多。
SetMaxIdleConns 方法
默认情况下,sql.DB连接池中最多保留2个空闲连接。您可以通过SetMaxIdleConns()如下方法更改此设置:
// Initialise a new connection pool
db, err := sql.Open("postgres", "postgres://user:pass@localhost/db")
if err != nil {log.Fatal(err)
}// Set the maximum number of concurrently idle connections to 5. Setting this
// to less than or equal to 0 will mean that no idle connections are retained.
db.SetMaxIdleConns(5)
从理论上讲,允许池中存在更多数量的空闲连接将提高性能,因为它使得需要从头开始建立新连接的可能性降低,从而有助于节省资源。
让我们看一下相同的基准测试,最大空闲连接数设置为none、1、2、5和10(并且打开的连接数不受限制):
BenchmarkMaxIdleConnsNone-8 300 4567245 ns/op 58174 B/op 625 allocs/op
BenchmarkMaxIdleConns1-8 2000 568765 ns/op 2596 B/op 32 allocs/op
BenchmarkMaxIdleConns2-8 2000 529359 ns/op 596 B/op 11 allocs/op
BenchmarkMaxIdleConns5-8 2000 506207 ns/op 451 B/op 9 allocs/op
BenchmarkMaxIdleConns10-8 2000 501639 ns/op 450 B/op 9 allocs/op
PASS
当MaxIdleConns设置为 none 时,必须为每个连接从头开始创建一个新连接,INSERT从基准测试中我们可以看到平均运行时间和内存使用量相对较高。
仅允许保留和重用 1 个空闲连接,这对这个特定的基准测试产生了巨大的影响——它使平均运行时间减少了约 8 倍,内存使用量减少了约 20 倍。继续增加空闲连接池的大小可以使性能变得更好,尽管改进不太明显。
那么你应该维护一个大的空闲连接池吗?答案是这取决于应用程序。
重要的是要认识到,保持空闲连接处于活动状态是有代价的 - 它会占用本来可用于应用程序和数据库的内存。
如果连接空闲时间太长,它也可能变得不可用。例如,MySQL 的wait_timeout设置将自动关闭 8 小时内未使用的所有连接(默认情况下)。
当这种情况发生时,sql.DB优雅地处理它。坏连接在放弃之前会自动重试两次,此时 Go 将从池中删除该连接并创建一个新连接。因此,设置得MaxIdleConns太高实际上可能会导致连接变得不可用,并且比拥有较小的空闲连接池(使用更频繁的连接更少)时使用更多的资源。因此,实际上,如果您可能很快会再次使用连接,那么您实际上只想保持连接空闲。
最后要指出的一件事是MaxIdleConns应该始终小于或等于MaxOpenConns。Go 会强制执行此操作,并在必要时自动减少MaxIdleConns。
SetConnMaxLifetime 方法
现在让我们看一下SetConnMaxLifetime()设置连接可以重用的最大时间长度的方法。如果您的 SQL 数据库还实现了最长连接生存期,或者例如您希望在负载均衡器后面轻松地交换数据库,那么这会很有用。
你像这样使用它:
// Initialise a new connection pool
db, err := sql.Open("postgres", "postgres://user:pass@localhost/db")
if err != nil {log.Fatal(err)
}// Set the maximum lifetime of a connection to 1 hour. Setting it to 0
// means that there is no maximum lifetime and the connection is reused
// forever (which is the default behavior).
db.SetConnMaxLifetime(time.Hour)
在此示例中,我们的所有连接将在首次创建 1 小时后“过期”,并且过期后无法重复使用。但请注意:
这并不能保证连接将在池中存在整整一个小时;连接很可能由于某种原因变得不可用并在此之前自动关闭。
连接在创建后仍然可以使用一小时以上 - 只是在那之后无法开始重用。
这不是空闲超时。连接将在首次创建后 1 小时到期,而不是在最后一次空闲后 1 小时。
每秒自动运行一次清理操作,以从池中删除“过期”连接。
从理论上讲,连接越短,ConnMaxLifetime连接过期的频率就越高,因此,需要从头开始创建连接的频率就越高。
ConnMaxLifetime为了说明这一点,我运行了设置为 100ms、200ms、500ms、1000ms 和无限制(永久重复使用)的基准测试,默认设置为无限制打开连接和 2 个空闲连接。这些时间段显然比您在大多数应用程序中使用的时间段短得多,但它们有助于很好地说明行为。
BenchmarkConnMaxLifetime100-8 2000 637902 ns/op 2770 B/op 34 allocs/op
BenchmarkConnMaxLifetime200-8 2000 576053 ns/op 1612 B/op 21 allocs/op
BenchmarkConnMaxLifetime500-8 2000 558297 ns/op 913 B/op 14 allocs/op
BenchmarkConnMaxLifetime1000-8 2000 543601 ns/op 740 B/op 12 allocs/op
BenchmarkConnMaxLifetimeUnlimited-8 3000 532789 ns/op 412 B/op 9 allocs/op
PASS
在这些特定的基准测试中,我们可以看到,与无限生命周期相比,100 毫秒生命周期的内存使用量增加了 3 倍多,并且每个生命周期的平均运行时间也INSERT稍长。
如果您ConnMaxLifetime在代码中进行了设置,请务必记住连接过期(并随后重新创建)的频率。例如,如果您总共有 100 个连接,连接时间ConnMaxLifetime为 1 分钟,那么您的应用程序每秒可能会终止并重新创建多达 1.67 个连接(平均)。您不希望这个频率太高,以至于最终会阻碍性能,而不是帮助它。
超出连接限制
最后,如果不提及超过数据库连接数量的硬限制会发生什么,那么本文就不完整。
作为说明,我将更改我的postgresql.conf文件,因此总共只允许 5 个连接(默认值为 100)…
max_connections = 5
然后以无限的开放连接重新运行基准测试…
BenchmarkMaxOpenConnsUnlimited-8 --- FAIL: BenchmarkMaxOpenConnsUnlimited-8main_test.go:14: pq: sorry, too many clients alreadymain_test.go:14: pq: sorry, too many clients alreadymain_test.go:14: pq: sorry, too many clients already
FAIL
一旦达到 5 个连接的硬限制,我的数据库驱动程序 ( pq ) 就会立即返回一条sorry, too many clients already错误消息,而不是完成INSERT.
为了防止出现此错误,我们需要将打开连接的最大总数(使用中 + 空闲)设置sql.DB为低于 5。如下所示:
// Initialise a new connection pool
db, err := sql.Open("postgres", "postgres://user:pass@localhost/db")
if err != nil {log.Fatal(err)
}// Set the number of open connections (in-use + idle) to a maximum total of 3.
db.SetMaxOpenConns(3)
sql.DB现在,任何时刻最多只能创建 3 个连接,并且基准测试运行时应该不会出现任何错误。
但这样做有一个很大的警告:当达到打开连接限制并且所有连接都在使用中时,应用程序需要执行的任何新数据库任务都将被迫等待,直到连接空闲并标记为空闲。例如,在 Web 应用程序的上下文中,用户的 HTTP 请求可能会“挂起”,甚至可能在等待数据库任务运行时超时。
为了缓解这种情况,您应该在进行数据库调用时始终传递一个context.Context具有固定、快速、超时的对象,使用启用上下文的方法,例如ExecContext(). 可以在此处的要点中看到一个示例。
总之
根据经验,您应该明确设置一个MaxOpenConns值。这应该大大低于数据库和基础设施对连接数量的硬性限制。
一般来说,MaxOpenConns和MaxIdleConns值越高,性能越好。但回报是递减的,您应该意识到,拥有太大的空闲连接池(连接未被重用并最终变坏)实际上会导致性能下降。
为了减轻上述第 2 点的风险,您可能需要设置相对较短的ConnMaxLifetime. 但您不希望这个时间太短,导致连接被频繁地不必要地终止和重新创建。
MaxIdleConns应始终小于或等于MaxOpenConns。
对于中小型 Web 应用程序,我通常使用以下设置作为起点,然后根据实际吞吐量水平的负载测试结果进行优化。
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5*time.Minute)
相关文章:
golang中如何配置 sql.DB 以获得更好的性能
有很多很好的教程讨论 Go 的sql.DB类型以及如何使用它来执行 SQL 数据库查询和语句。但它们中的大多数都掩盖了SetMaxOpenConns()、SetMaxIdleConns()和SetConnMaxLifetime()方法——您可以使用它们来配置 的行为sql.DB并改变其性能。 在这篇文章中,我想准确解释这…...
JAVA同城服务智慧养老小程序怎么开发?
随着人口老龄化的加剧,智慧养老成为了社会关注的焦点。智慧养老小程序作为一种便捷、高效的服务工具,为老年人提供了更全面、个性化的服务。本文将介绍如何使用JAVA编程语言开发一款同城服务智慧养老小程序。 一、设计思路 界面设计:小程序…...
Linux防火墙:Firewalld 常用命令
Linux防火墙:Firewalld 常用命令 CentOS 和 Fedora 中默认的防火墙是 Firewalld 查看防火墙状态 firewall-cmd --state 启动防火墙 systemctl start firewalld 重启防火墙 systemctl restart firewalld 暂时关闭防火墙 systemctl stop firewalld 永久关闭防火墙…...
Java BigInteger比Long更大的整数自增转字符串存储
文章目录 前言BigInteger自增BigInteger转化为StringBigInteger阶乘 前言 BigInteger类在Java中可以表示任意大小的整数,没有固定的范围限制。它使用内部的数组来存储整数的位数,并提供了各种方法来执行算术运算和其他操作。 BigInteger类的大小只受限…...

BigDecimal应用——计算费用场景中用到Integer,Double,BigDecimal三种类型出现的意外情况 结合BigDecimal源码分析
引出 在一个计算费用的场景中,用到了Integer,Double,BigDecimal三种类型,在转换为bigdecimal的时候遇到的问题,结合源码进行了分析。 1.在new bigdecimal的时候,最好传入的是字符串;2.double类…...

数据抓取可以应用到哪些行业
随着互联网的发展,数据已经成为人们生活中不可或缺的一部分。数据抓取作为获取数据的重要手段之一,也被广泛应用于各个行业。本文将探讨数据抓取在各个行业中的应用。 首先,让我们来了解一下数据抓取的基本概念。数据抓取是指通过一定的技术…...
目标检测YOLO实战应用案例100讲-面向小目标检测的多尺度特征融合(续)
目录 3.3 实验结果及分析 3.3.1 实验设置 3.3.2 消融实验 3.3.3 在PASCAL VOC2007上的结果...

如何选择适合的美颜SDK?
美摄美颜SDK是一款专门为企业提供美颜技术支持的SDK,可以帮助企业开发出具有高品质美颜效果的移动应用。本文将介绍美摄美颜SDK的技术特点和面向企业提供的技术支持。 一、技术特点 美摄美颜SDK采用了先进的图像处理技术和人工智能算法,能够快速准确地…...
Spring-底层架构核心概念
Spring底层核心组件 BeanDefinition BeanDefinition表示Bean定义,有很多属性用来描述Bean的特点: class,表示Bean类型 scope,表示Bean作用域,单例或原型等 lazyInit:表示Bean是否是懒加载 initMethod…...

RabbitMQ初入门
1、RabbitMQ是什么 RabbitMQ是“实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均…...

电脑定时关机
电脑定时关机 1.右键 管理 2. 3. 4. 5. shutdown.exe/s /f /t 06.点击完成就好了 7.这里面可以 看到定时任务和启动 右键有运行 结束 禁用...

【算法】滑动窗口题单——4.不定长滑动窗口(求子数组个数)
文章目录 前言2799. 统计完全子数组的数目解法1——枚举右端点,移动左端点解法2——枚举左端点,扩展右端点 713. 乘积小于 K 的子数组1358. 包含所有三种字符的子字符串数目2302. 统计得分小于 K 的子数组数目2537. 统计好子数组的数目2762. 不间断子数组…...

CMake aux_source_directory 学习
如下,prj是空文件夹; add.h; #include <iostream>using namespace std;int add1(int a, int b); num.h; int num1100; int num2301; add.cpp; #include "add.h"int add1(int i, int j) {return i j; } main.cpp&#x…...
Mybatis中延迟加载~
延迟加载: 等一会加载,在多表关联查询操作的时候可以使用到的一种方案,如果是单表操作就完全没有延迟加载的概念。 多表查询例如,查询用户和部门信息,如果我们仅仅只是需要用户的信息,而不需要用户对应的…...

【C语言】memmove()函数(拷贝重叠内存块函数详解)
🦄个人主页:修修修也 🎏所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.memmove()函数简介 1.函数功能 2.函数参数 1>.void * destination 2>.onst void * source 3>.size_t num 3.函数返回值 4.函数头文件 二.memmove()函数…...
04-流媒体-ffmpeg.c源码分析
ffmpeg.c是一个使用ffmpeg库的参考代码,实现了视频格式转换的功能,类似于我们常用的格式工产,源代码的的目录是: ffmpeg-4.2.2/fftools/ffmpeg.c 和前面的ffplay一样,我们分析其源代码,主要只是为了让读者了解ffmpeg.c此文件的大概流程,并且熟悉常用的ffmpeg库的API。 下…...
迭代器 Iterator
迭代器是一种设计模式,它用于遍历集合或容器中的元素,能够访问集合的元素而无需关心集合的内部结构: 特点: 封装集合访问:迭代器封装了对集合元素的访问,通过迭代器访问集合中的元素,而无需了…...

掌握CSS Flexbox,打造完美响应式布局,适配各种设备!
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 ⭐ 专栏简介 📘 文章引言 基…...

FlutterUnit 周边 | 收录排序算法可视化
theme: cyanosis 1. FlutterUnit 更新:排序算法可视化 排序算法可视化是用视图层表现出算法执行过程中排序的过程,感谢 编程的平行世界 在 《十几种排序算法的可视化效果,快来看看!👀》》 一文中提供的算法支持。我进行…...

代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间
LeetCode T435 无重叠区间 题目链接:435. 无重叠区间 - 力扣(LeetCode) 题目思路: 这题思路和昨天的打气球类似,我们需要按照左区间或者右区间进行排序,然后哦判断第i个区间的左端点和第i-1个区间的右端点的大小关系,,如果大于等于,那么就无需操作,一旦…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

stm32wle5 lpuart DMA数据不接收
配置波特率9600时,需要使用外部低速晶振...
Android多媒体——音/视频数据播放(十八)
在媒体数据完成解码并准备好之后,播放流程便进入了最终的呈现阶段。为了确保音视频内容能够顺利输出,系统需要首先对相应的播放设备进行初始化。只有在设备初始化成功后,才能真正开始音视频的同步渲染与播放。这一过程不仅影响播放的启动速度,也直接关系到播放的稳定性和用…...
民锋视角下的资金流效率与账户行为建模
民锋视角下的资金流效率与账户行为建模 在当前复杂多变的金融环境中,资金流效率已成为衡量一家金融服务机构专业能力的重要指标。民锋在账户管理与资金调配的实战经验中,逐步建立起一套以资金流路径为核心的行为建模方法,用以评估客户行为、交…...