当前位置: 首页 > news >正文

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 学习

如下&#xff0c;prj是空文件夹&#xff1b; add.h; #include <iostream>using namespace std;int add1(int a, int b); num.h; int num1100; int num2301; add.cpp&#xff1b; #include "add.h"int add1(int i, int j) {return i j; } main.cpp&#x…...

Mybatis中延迟加载~

延迟加载&#xff1a; 等一会加载&#xff0c;在多表关联查询操作的时候可以使用到的一种方案&#xff0c;如果是单表操作就完全没有延迟加载的概念。 多表查询例如&#xff0c;查询用户和部门信息&#xff0c;如果我们仅仅只是需要用户的信息&#xff0c;而不需要用户对应的…...

【C语言】memmove()函数(拷贝重叠内存块函数详解)

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏: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

迭代器是一种设计模式&#xff0c;它用于遍历集合或容器中的元素&#xff0c;能够访问集合的元素而无需关心集合的内部结构&#xff1a; 特点&#xff1a; 封装集合访问&#xff1a;迭代器封装了对集合元素的访问&#xff0c;通过迭代器访问集合中的元素&#xff0c;而无需了…...

掌握CSS Flexbox,打造完美响应式布局,适配各种设备!

&#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 ⭐ 专栏简介 &#x1f4d8; 文章引言 基…...

FlutterUnit 周边 | 收录排序算法可视化

theme: cyanosis 1. FlutterUnit 更新&#xff1a;排序算法可视化 排序算法可视化是用视图层表现出算法执行过程中排序的过程&#xff0c;感谢 编程的平行世界 在 《十几种排序算法的可视化效果&#xff0c;快来看看&#xff01;&#x1f440;》》 一文中提供的算法支持。我进行…...

代码随想录Day30 贪心05 LeetCode T435无重叠区间 T763划分字母区间 T56 合并区间

LeetCode T435 无重叠区间 题目链接:435. 无重叠区间 - 力扣&#xff08;LeetCode&#xff09; 题目思路: 这题思路和昨天的打气球类似,我们需要按照左区间或者右区间进行排序,然后哦判断第i个区间的左端点和第i-1个区间的右端点的大小关系,,如果大于等于,那么就无需操作,一旦…...

发展高质量存储力,中国高科技力量聚浪成潮

中国信息通信研究院指出&#xff0c;在全球数字化转型与产业变革的浪潮下&#xff0c;算力正在成为改变全球竞争格局的关键力量。而根据最新的《算力基础设施高质量发展行动计划》&#xff0c;算力是集信息计算力、数据存储力和网络运载力于一体的新型生产力。当前&#xff0c;…...

修改svc的LoadBalancer的IP引发的惨案

文章目录 背景修改externalIPs的操作api-server报错日志挽救教训 背景 k8s集群没有接外部负载均衡&#xff0c;部署istio的时候ingressgateway一直pending。 于是手动修改了这个lb svc的externalIP&#xff0c;于是k8s就崩了&#xff0c;如何崩的&#xff0c;且听我还道来。 …...

2520. 统计能整除数字的位数

2520. 统计能整除数字的位数 class Solution {public int countDigits(int num) {int res 0;int o num;while (num > 0) {if (o % (num % 10) 0) {res 1;}num num / 10;}return res;} }...

BeanUtils.copyProperties的用法

常见场景 我们如果有两个具有很多相同属性名的JavaBean对象a和b&#xff0c;想把a中的属性赋值到b&#xff0c;例如 接口中将接收到的前端请求参数XxxReqVo,我们想把这个入参转化为XxxQuery对象作为数据库的查询条件对象 传统做法是手动set&#xff0c;即 XxxQuery xxxQuer…...

【RabbitMQ 实战】12 镜像队列

一、镜像队列的概念 RabbitMQ的镜像队列是将消息副本存储在一组节点上&#xff0c;以提高可用性和可靠性。镜像队列将队列中的消息复制到一个或多个其他节点上&#xff0c;并使这些节点上的队列保持同步。当一个节点失败时&#xff0c;其他节点上的队列不受影响&#xff0c;因…...

PyCharm社区版安装

PyCharm社区版安装 到中国官网下载 https://www.jetbrains.com/zh-cn/pycharm/download/?sectionwindows 首次创建项目&#xff0c;会自动下载安装Python 3.9 社区版的区别 社区版的区别...

【LeetCode每日一题合集】2023.10.16-2023.10.22(只出现一次的数字Ⅲ)

文章目录 260. 只出现一次的数字 III⭐&#xff08;异或&#xff09;&#x1f402;2652. 倍数求和解法1——枚举模拟解法2—— O ( 1 ) O(1) O(1)容斥原理相似题目——1201. 丑数 III&#xff08;二分查找容斥原理&#xff09; 2530. 执行 K 次操作后的最大分数解法1——贪心优…...

尚硅谷大数据项目《在线教育之实时数仓》笔记003

视频地址&#xff1a;尚硅谷大数据项目《在线教育之实时数仓》_哔哩哔哩_bilibili 目录 第7章 数仓开发之ODS层 P015 第8章 数仓开发之DIM层 P016 P017 P018 P019 01、node001节点Linux命令 02、KafkaUtil.java 03、DimSinkApp.java P020 P021 P022 P023 第7章 数…...

【Linux】部署单体项目以及前后端分离项目(项目部署)

一、简介 以下就是Linux部署单机项目和前后端分离项目的优缺点&#xff0c;希望对你有所帮助。 1、Linux部署单机项目&#xff1a; 优点&#xff1a; 1.简化了系统管理&#xff1a;由于所有服务都在同一台机器上运行&#xff0c;因此可以简化系统管理和维护。 2.提高了性能&a…...

设计模式之门面模式

前言 什么是门面模式 门面模式是一种结构型设计模式&#xff0c;它提供了一个统一的接口&#xff0c;用来访问子系统中的一群接口。它定义了一个高层接口&#xff0c;让子系统更容易使用。这种模式常用于将一个复杂的子系统封装成一个简单的接口&#xff0c;使得客户端可以方…...