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个区间的右端点的大小关系,,如果大于等于,那么就无需操作,一旦…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建
制造业采购供应链管理是企业运营的核心环节,供应链协同管理在供应链上下游企业之间建立紧密的合作关系,通过信息共享、资源整合、业务协同等方式,实现供应链的全面管理和优化,提高供应链的效率和透明度,降低供应链的成…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...
