go语言数据池,go语言操作数据库
golang sync.pool对象复用 并发原理 缓存池
在go http每一次go serve(l)都会构建Request数据结构。在大量数据请求或高并发的场景中,频繁创建销毁对象,会导致GC压力。解决办法之一就是使用对象复用技术。在http协议层之下,使用对象复用技术创建Request数据结构。在http协议层之上,可以使用对象复用技术创建(w,*r,ctx)数据结构。这样即可以回快TCP层读包之后的解析速度,也可也加快请求处理的速度。
创新互联公司主打移动网站、网站建设、网站设计、网站改版、网络推广、网站维护、空间域名、等互联网信息服务,为各行业提供服务。在技术实力的保障下,我们为客户承诺稳定,放心的服务,根据网站的内容与功能再决定采用什么样的设计。最后,要实现符合网站需求的内容、功能与设计,我们还会规划稳定安全的技术方案做保障。
先上一个测试:
结论是这样的:
貌似使用池化,性能弱爆了???这似乎与net/http使用sync.pool池化Request来优化性能的选择相违背。这同时也说明了一个问题,好的东西,如果滥用反而造成了性能成倍的下降。在看过pool原理之后,结合实例,将给出正确的使用方法,并给出预期的效果。
sync.Pool是一个 协程安全 的 临时对象池 。数据结构如下:
local 成员的真实类型是一个 poolLocal 数组,localSize 是数组长度。这涉及到Pool实现,pool为每个P分配了一个对象,P数量设置为runtime.GOMAXPROCS(0)。在并发读写时,goroutine绑定的P有对象,先用自己的,没有去偷其它P的。go语言将数据分散在了各个真正运行的P中,降低了锁竞争,提高了并发能力。
不要习惯性地误认为New是一个关键字,这里的New是Pool的一个字段,也是一个闭包名称。其API:
如果不指定New字段,对象池为空时会返回nil,而不是一个新构建的对象。Get()到的对象是随机的。
原生sync.Pool的问题是,Pool中的对象会被GC清理掉,这使得sync.Pool只适合做简单地对象池,不适合作连接池。
pool创建时不能指定大小,没有数量限制。pool中对象会被GC清掉,只存在于两次GC之间。实现是pool的init方法注册了一个poolCleanup()函数,这个方法在GC之前执行,清空pool中的所有缓存对象。
为使多协程使用同一个POOL。最基本的想法就是每个协程,加锁去操作共享的POOL,这显然是低效的。而进一步改进,类似于ConcurrentHashMap(JDK7)的分Segment,提高其并发性可以一定程度性缓解。
注意到pool中的对象是无差异性的,加锁或者分段加锁都不是较好的做法。go的做法是为每一个绑定协程的P都分配一个子池。每个子池又分为私有池和共享列表。共享列表是分别存放在各个P之上的共享区域,而不是各个P共享的一块内存。协程拿自己P里的子池对象不需要加锁,拿共享列表中的就需要加锁了。
Get对象过程:
Put过程:
如何解决Get最坏情况遍历所有P才获取得对象呢:
方法1止前sync.pool并没有这样的设置。方法2由于goroutine被分配到哪个P由调度器调度不可控,无法确保其平衡。
由于不可控的GC导致生命周期过短,且池大小不可控,因而不适合作连接池。仅适用于增加对象重用机率,减少GC负担。2
执行结果:
单线程情况下,遍历其它无元素的P,长时间加锁性能低下。启用协程改善。
结果:
测试场景在goroutines远大于GOMAXPROCS情况下,与非池化性能差异巨大。
测试结果
可以看到同样使用*sync.pool,较大池大小的命中率较高,性能远高于空池。
结论:pool在一定的使用条件下提高并发性能,条件1是协程数远大于GOMAXPROCS,条件2是池中对象远大于GOMAXPROCS。归结成一个原因就是使对象在各个P中均匀分布。
池pool和缓存cache的区别。池的意思是,池内对象是可以互换的,不关心具体值,甚至不需要区分是新建的还是从池中拿出的。缓存指的是KV映射,缓存里的值互不相同,清除机制更为复杂。缓存清除算法如LRU、LIRS缓存算法。
池空间回收的几种方式。一些是GC前回收,一些是基于时钟或弱引用回收。最终确定在GC时回收Pool内对象,即不回避GC。用java的GC解释弱引用。GC的四种引用:强引用、弱引用、软引用、虚引用。虚引用即没有引用,弱引用GC但有空间则保留,软引用GC即清除。ThreadLocal的值为弱引用的例子。
regexp 包为了保证并发时使用同一个正则,而维护了一组状态机。
fmt包做字串拼接,从sync.pool拿[]byte对象。避免频繁构建再GC效率高很多。
GO语言(三十):访问关系型数据库(上)
本教程介绍了使用 Godatabase/sql及其标准库中的包访问关系数据库的基础知识。
您将使用的database/sql包包括用于连接数据库、执行事务、取消正在进行的操作等的类型和函数。
在本教程中,您将创建一个数据库,然后编写代码来访问该数据库。您的示例项目将是有关老式爵士乐唱片的数据存储库。
首先,为您要编写的代码创建一个文件夹。
1、打开命令提示符并切换到您的主目录。
在 Linux 或 Mac 上:
在 Windows 上:
2、在命令提示符下,为您的代码创建一个名为 data-access 的目录。
3、创建一个模块,您可以在其中管理将在本教程中添加的依赖项。
运行go mod init命令,为其提供新代码的模块路径。
此命令创建一个 go.mod 文件,您添加的依赖项将在其中列出以供跟踪。
注意: 在实际开发中,您会指定一个更符合您自己需求的模块路径。有关更多信息,请参阅一下文章。
GO语言(二十五):管理依赖项(上)
GO语言(二十六):管理依赖项(中)
GO语言(二十七):管理依赖项(下)
接下来,您将创建一个数据库。
在此步骤中,您将创建要使用的数据库。您将使用 DBMS 本身的 CLI 创建数据库和表,以及添加数据。
您将创建一个数据库,其中包含有关黑胶唱片上的老式爵士乐录音的数据。
这里的代码使用MySQL CLI,但大多数 DBMS 都有自己的 CLI,具有类似的功能。
1、打开一个新的命令提示符。
在命令行,登录到您的 DBMS,如下面的 MySQL 示例所示。
2、在mysql命令提示符下,创建一个数据库。
3、切到您刚刚创建的数据库,以便您可以添加表。
4、在文本编辑器的 data-access 文件夹中,创建一个名为 create-tables.sql 的文件来保存用于添加表的 SQL 脚本。
将以下 SQL 代码粘贴到文件中,然后保存文件。
在此 SQL 代码中:
(1)删除名为album表。 首先执行此命令可以让您更轻松地稍后重新运行脚本。
(2)创建一个album包含四列的表:title、artist和price。每行的id值由 DBMS 自动创建。
(3)添加带有值的四行。
5、在mysql命令提示符下,运行您刚刚创建的脚本。
您将使用以下形式的source命令:
6、在 DBMS 命令提示符处,使用SELECT语句来验证您是否已成功创建包含数据的表。
接下来,您将编写一些 Go 代码进行连接,以便进行查询。
现在你已经有了一个包含一些数据的数据库,开始你的 Go 代码。
找到并导入一个数据库驱动程序,该驱动程序会将您通过database/sql包中的函数发出的请求转换为数据库可以理解的请求。
1、在您的浏览器中,访问SQLDrivers wiki 页面以识别您可以使用的驱动程序。
2、使用页面上的列表来识别您将使用的驱动程序。为了在本教程中访问 MySQL,您将使用 Go-MySQL-Driver。
3、请注意驱动程序的包名称 - 此处为github.com/go-sql-driver/mysql.
4、使用您的文本编辑器,创建一个用于编写 Go 代码的文件,并将该文件作为 main.go 保存在您之前创建的数据访问目录中。
5、进入main.go,粘贴以下代码导入驱动包。
在此代码中:
(1)将您的代码添加到main包中,以便您可以独立执行它。
(2)导入 MySQL 驱动程序github.com/go-sql-driver/mysql。
导入驱动程序后,您将开始编写代码以访问数据库。
现在编写一些 Go 代码,让您使用数据库句柄访问数据库。
您将使用指向结构的指针sql.DB,它表示对特定数据库的访问。
编写代码
1、进入 main.go,在import您刚刚添加的代码下方,粘贴以下 Go 代码以创建数据库句柄。
在此代码中:
(3)使用 MySQL 驱动程序Config和FormatDSN类型以收集连接属性并将它们格式化为连接字符串的 DSN。
该Config结构使代码比连接字符串更容易阅读。
(4)调用sql.Open 初始化db变量,传递 FormatDSN。
(5)检查来自 的错误sql.Open。例如,如果您的数据库连接细节格式不正确,它可能会失败。
为了简化代码,您调用log.Fatal结束执行并将错误打印到控制台。在生产代码中,您会希望以更优雅的方式处理错误。
(6)调用DB.Ping以确认连接到数据库有效。在运行时, sql.Open可能不会立即连接,具体取决于驱动程序。您在Ping此处使用以确认 database/sql包可以在需要时连接。
(7)检查来自Ping的错误,以防连接失败。
(8)Ping如果连接成功,则打印一条消息。
文件的顶部现在应该如下所示:
3、保存 main.go。
1、开始跟踪 MySQL 驱动程序模块作为依赖项。
使用go get 添加 github.com/go-sql-driver/mysql 模块作为您自己模块的依赖项。使用点参数表示“获取当前目录中代码的依赖项”。
2、在命令提示符下,设置Go 程序使用的DBUSER和DBPASS环境变量。
在 Linux 或 Mac 上:
在 Windows 上:
3、在包含 main.go 的目录中的命令行中,通过键入go run来运行代码。
连接成功了!
接下来,您将查询一些数据。
go语言TCP连接池rocket049/connpool使用
安装:
go get -v -u github.com/rocket049/connpool
go get -v -u gitee.com/rocket049/connpool
rocket049/connpool 包是本人用go语言开发的,提供一个通用的TCP连接池,初始化参数包括最高连接数、超时秒数、连接函数,放回连接池的连接被重新取出时,如果已经超时,将会自动重新连接;如果没有超时,连接将被复用。
可调用的函数:
调用示例:
当前文章:go语言数据池,go语言操作数据库
文章位置:http://myzitong.com/article/hsecpo.html