GO语言商业案例(十八):stream
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了GO语言商业案例(十八):stream相关的知识,希望对你有一定的参考价值。
参考技术A切换到新语言始终是一大步,尤其是当您的团队成员只有一个时有该语言的先前经验。现在,Stream 的主要编程语言从 Python 切换到了 Go。这篇文章将解释stream决定放弃 Python 并转向 Go 的一些原因。
Go 非常快。性能类似于 Java 或 C++。对于用例,Go 通常比 Python 快 40 倍。
对于许多应用程序来说,编程语言只是应用程序和数据库之间的粘合剂。语言本身的性能通常并不重要。然而,Stream 是一个API 提供商,为 700 家公司和超过 5 亿最终用户提供提要和聊天平台。多年来,我们一直在优化 Cassandra、PostgreSQL、Redis 等,但最终,您会达到所使用语言的极限。Python 是一门很棒的语言,但对于序列化/反序列化、排名和聚合等用例,它的性能相当缓慢。我们经常遇到性能问题,Cassandra 需要 1 毫秒来检索数据,而 Python 会花费接下来的 10 毫秒将其转换为对象。
看看我如何开始 Go 教程中的一小段 Go 代码。(这是一个很棒的教程,也是学习 Go 的一个很好的起点。)
如果您是 Go 新手,那么在阅读那个小代码片段时不会有太多让您感到惊讶的事情。它展示了多个赋值、数据结构、指针、格式和一个内置的 HTTP 库。当我第一次开始编程时,我一直喜欢使用 Python 更高级的功能。Python 允许您在编写代码时获得相当的创意。例如,您可以:
这些功能玩起来很有趣,但是,正如大多数程序员会同意的那样,在阅读别人的作品时,它们通常会使代码更难理解。Go 迫使你坚持基础。这使得阅读任何人的代码并立即了解发生了什么变得非常容易。 注意:当然,它实际上有多“容易”取决于您的用例。如果你想创建一个基本的 CRUD API,我仍然推荐 Django + DRF或 Rails。
作为一门语言,Go 试图让事情变得简单。它没有引入许多新概念。重点是创建一种非常快速且易于使用的简单语言。它唯一具有创新性的领域是 goroutine 和通道。(100% 正确CSP的概念始于 1977 年,所以这项创新更多是对旧思想的一种新方法。)Goroutines 是 Go 的轻量级线程方法,通道是 goroutines 之间通信的首选方式。Goroutines 的创建非常便宜,并且只需要几 KB 的额外内存。因为 Goroutine 非常轻量,所以有可能同时运行数百甚至数千个。您可以使用通道在 goroutine 之间进行通信。Go 运行时处理所有复杂性。goroutines 和基于通道的并发方法使得使用所有可用的 CPU 内核和处理并发 IO 变得非常容易——所有这些都不会使开发复杂化。与 Python/Java 相比,在 goroutine 上运行函数需要最少的样板代码。您只需在函数调用前加上关键字“go”:
Go 的并发方法很容易使用。与 Node 相比,这是一种有趣的方法,开发人员必须密切关注异步代码的处理方式。Go 中并发的另一个重要方面是竞争检测器。这样可以很容易地确定异步代码中是否存在任何竞争条件。
我们目前用 Go 编写的最大的微服务编译需要 4 秒。与以编译速度慢而闻名的 Java 和 C++ 等语言相比,Go 的快速编译时间是一项重大的生产力胜利。我喜欢在程序编译的时候摸鱼,但在我还记得代码应该做什么的同时完成事情会更好。
首先,让我们从显而易见的开始:与 C++ 和 Java 等旧语言相比,Go 开发人员的数量并不多。根据StackOverflow的数据, 38% 的开发人员知道 Java, 19.3% 的人知道 C++,只有 4.6% 的人知道 Go。GitHub 数据显示了类似的趋势:Go 比 Erlang、Scala 和 Elixir 等语言使用更广泛,但不如 Java 和 C++ 流行。幸运的是,Go 是一种非常简单易学的语言。它提供了您需要的基本功能,仅此而已。它引入的新概念是“延迟”声明和内置的并发管理与“goroutines”和通道。(对于纯粹主义者来说:Go 并不是第一种实现这些概念的语言,只是第一种使它们流行起来的语言。)任何加入团队的 Python、Elixir、C++、Scala 或 Java 开发人员都可以在一个月内在 Go 上发挥作用,因为它的简单性。与许多其他语言相比,我们发现组建 Go 开发人员团队更容易。如果您在博尔德和阿姆斯特丹等竞争激烈的生态系统中招聘人员,这是一项重要的优势。
对于我们这样规模的团队(约 20 人)来说,生态系统很重要。如果您必须重新发明每一个小功能,您根本无法为您的客户创造价值。Go 对我们使用的工具有很好的支持。实体库已经可用于 Redis、RabbitMQ、PostgreSQL、模板解析、任务调度、表达式解析和 RocksDB。与 Rust 或 Elixir 等其他较新的语言相比,Go 的生态系统是一个重大胜利。它当然不如 Java、Python 或 Node 之类的语言好,但它很可靠,而且对于许多基本需求,你会发现已经有高质量的包可用。
Gofmt 是一个很棒的命令行实用程序,内置在 Go 编译器中,用于格式化代码。就功能而言,它与 Python 的 autopep8 非常相似。我们大多数人并不真正喜欢争论制表符与空格。格式的一致性很重要,但实际的格式标准并不那么重要。Gofmt 通过使用一种正式的方式来格式化您的代码来避免所有这些讨论。
Go 对协议缓冲区和 gRPC 具有一流的支持。这两个工具非常适合构建需要通过 RPC 通信的微服务。您只需要编写一个清单,在其中定义可以进行的 RPC 调用以及它们采用的参数。然后从这个清单中自动生成服务器和客户端代码。生成的代码既快速又具有非常小的网络占用空间并且易于使用。从同一个清单中,您甚至可以为许多不同的语言生成客户端代码,例如 C++、Java、Python 和 Ruby。因此,内部流量不再有模棱两可的 REST 端点,您每次都必须编写几乎相同的客户端和服务器代码。.
Go 没有像 Rails 用于 Ruby、Django 用于 Python 或 Laravel 用于 php 那样的单一主导框架。这是 Go 社区内激烈争论的话题,因为许多人主张你不应该一开始就使用框架。我完全同意这对于某些用例是正确的。但是,如果有人想构建一个简单的 CRUD API,他们将更容易使用 Django/DJRF、Rails Laravel 或Phoenix。对于 Stream 的用例,我们更喜欢不使用框架。然而,对于许多希望提供简单 CRUD API 的新项目来说,缺乏主导框架将是一个严重的劣势。
Go 通过简单地从函数返回错误并期望调用代码来处理错误(或将其返回到调用堆栈)来处理错误。虽然这种方法有效,但很容易失去问题的范围,以确保您可以向用户提供有意义的错误。错误包通过允许您向错误添加上下文和堆栈跟踪来解决此问题。另一个问题是很容易忘记处理错误。像 errcheck 和 megacheck 这样的静态分析工具可以方便地避免犯这些错误。虽然这些变通办法效果很好,但感觉不太对劲。您希望该语言支持正确的错误处理。
Go 的包管理绝不是完美的。默认情况下,它无法指定特定版本的依赖项,也无法创建可重现的构建。Python、Node 和 Ruby 都有更好的包管理系统。但是,使用正确的工具,Go 的包管理工作得很好。您可以使用Dep来管理您的依赖项,以允许指定和固定版本。除此之外,我们还贡献了一个名为的开源工具VirtualGo,它可以更轻松地处理用 Go 编写的多个项目。
我们进行的一个有趣的实验是在 Python 中使用我们的排名提要功能并在 Go 中重写它。看看这个排名方法的例子:
Python 和 Go 代码都需要执行以下操作来支持这种排名方法:
开发 Python 版本的排名代码大约花了 3 天时间。这包括编写代码、单元测试和文档。接下来,我们花了大约 2 周的时间优化代码。其中一项优化是将分数表达式 (simple_gauss(time)*popularity) 转换为抽象语法树. 我们还实现了缓存逻辑,可以在未来的特定时间预先计算分数。相比之下,开发此代码的 Go 版本大约需要 4 天时间。性能不需要任何进一步的优化。因此,虽然 Python 的最初开发速度更快,但基于 Go 的版本最终需要我们团队的工作量大大减少。另外一个好处是,Go 代码的执行速度比我们高度优化的 Python 代码快大约 40 倍。现在,这只是我们通过切换到 Go 体验到的性能提升的一个示例。
与 Python 相比,我们系统的其他一些组件在 Go 中构建所需的时间要多得多。作为一个总体趋势,我们看到 开发 Go 代码需要更多的努力。但是,我们花更少的时间 优化 代码以提高性能。
我们评估的另一种语言是Elixir.。Elixir 建立在 Erlang 虚拟机之上。这是一种迷人的语言,我们之所以考虑它,是因为我们的一名团队成员在 Erlang 方面拥有丰富的经验。对于我们的用例,我们注意到 Go 的原始性能要好得多。Go 和 Elixir 都可以很好地服务数千个并发请求。但是,如果您查看单个请求的性能,Go 对于我们的用例来说要快得多。我们选择 Go 而不是 Elixir 的另一个原因是生态系统。对于我们需要的组件,Go 有更成熟的库,而在许多情况下,Elixir 库还没有准备好用于生产环境。培训/寻找开发人员使用 Elixir 也更加困难。这些原因使天平向 Go 倾斜。Elixir 的 Phoenix 框架看起来很棒,绝对值得一看。
Go 是一种非常高性能的语言,对并发有很好的支持。它几乎与 C++ 和 Java 等语言一样快。虽然与 Python 或 Ruby 相比,使用 Go 构建东西确实需要更多时间,但您将节省大量用于优化代码的时间。我们在Stream有一个小型开发团队,为超过 5 亿最终用户提供动力和聊天。Go 结合了 强大的生态系统 、新开发人员的 轻松入门、快速的性能 、对并发的 可靠支持和高效的编程环境 ,使其成为一个不错的选择。Stream 仍然在我们的仪表板、站点和机器学习中利用 Python 来提供个性化的订阅源. 我们不会很快与 Python 说再见,但今后所有性能密集型代码都将使用 Go 编写。我们新的聊天 API也完全用 Go 编写。
Go语言开发(十八)Go语言MySQL数据库操作
Go语言开发(十八)、Go语言MySQL数据库操作
一、MySQL数据库驱动
1、MySQL数据库驱动简介
Go语言官方没有实现MySQL数据库驱动,常用的开源MySQL数据库驱动实现如下:
(1)Go MySQL Driver
Go MySQL Driver支持database/sql接口,全部采用Go语言实现。
官方网站:
https://github.com/go-sql-driver/mysql/
(2)MyMySQL
MyMySQL支持database/sql接口,也支持自定义的接口,全部采用Go语言实现。
官方网站:
https://github.com/ziutek/mymysql
(3)GoMySQL
GoMySQL不支持database/sql接口,采用自定义接口,全部采用Go语言实现。
官方网站:
https://github.com/Philio/GoMySQL
2、Go-MySQL-Driver简介
Go-MySQL-Driver优点:
(1)维护比较好。
(2)完全支持database/sql接口。
(3)支持keepalive,保持长连接。
Go-MySQL-Driver安装如下:go get github.com/go-sql-driver/mysql
导入包:
import "database/sql"
import _ "github.com/go-sql-driver/mysql"
二、MySQL基本操作
1、MySQL数据库创建
登录MySQL数据库,创建数据库create database student default character set utf8;
2、sql常用方法
func Open(driverName, dataSourceName string) (*DB, error)
driverName参数为数据库驱动名称。
dataSourceName是连接参数,参数格式如下:
user:[email protected](host:port)/dbname?charset=utf8
func (db *DB) Prepare(query string) (*Stmt, error)
Prepare为后续查询或执行操作创建一个准备SQLfunc (s *Stmt) Exec(args ...interface{}) (Result, error)
使用给定参数执行准备的SQL语句func (s *Stmt) Query(args ...interface{}) (*Rows, error)
使用给定参数执行准备的SQL查询语句func (db *DB) Exec(query string, args ...interface{}) (Result, error)
执行SQL操作,query为SQL语句,可以接收可变参数,用于填充SQL语句的某些字段值。func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
执行SQL查询操作,可以接收多个参数
3、MySQL常用操作
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func errorHandler(err error) {
if err != nil {
fmt.Println(err.Error())
}
}
var (
CREATE_TABLE = "CREATE TABLE student(" +
"sid INT(10) NOT NULL AUTO_INCREMENT," +
"sname VARCHAR(64) NULL DEFAULT NULL," +
"age INT(10) DEFAULT NULL,PRIMARY KEY (sid))" +
"ENGINE=InnoDB DEFAULT CHARSET=utf8;"
)
// 建立数据库连接
func setupConnect() *sql.DB {
db, err := sql.Open("mysql", "root:[email protected](118.24.159.133:3306)/student?charset=utf8")
errorHandler(err)
return db
}
// 创建表
func CreateTable(db *sql.DB, sql string) {
_, err := db.Exec(sql)
errorHandler(err)
}
var INSERT_DATA = `INSERT INTO student(sid,sname,age) VALUES(?,?,?);`
// 插入数据
func Insert(db *sql.DB) {
db.Exec(INSERT_DATA, 1, "唐僧", 30)
}
var UPDATE_DATA = `UPDATE student SET age=28 WHERE sname="唐僧";`
// 修改数据
func Update(db *sql.DB) {
db.Exec(UPDATE_DATA)
}
var DELETE_DATA = `DELETE FROM student WHERE age>=30`
// 删除记录
func Delete(db *sql.DB) {
db.Exec(DELETE_DATA)
}
var DELETE_TABLE = `DROP TABLE student;`
// 删除表
func DeleteTable(db *sql.DB) {
db.Exec(DELETE_TABLE)
}
var QUERY_DATA = `SELECT * FROM student;`
// 查询数据
func Query(db *sql.DB) {
rows, err := db.Query(QUERY_DATA)
if err != nil {
fmt.Println(err)
}
for rows.Next() {
var name string
var id int
var age int
if err := rows.Scan(&id, &name, &age); err != nil {
fmt.Println(err)
}
fmt.Printf("%s is %d
", name, age)
}
}
func main() {
// 建立数据连接
db := setupConnect()
// 创建数据库表
CreateTable(db, CREATE_TABLE)
// 插入数据
Insert(db)
// 查询数据
Query(db)
// 删除数据
Delete(db)
// 插入数据
Insert(db)
// 修改数据
Update(db)
// 查询数据
Query(db)
// 删除表
DeleteTable(db)
// 关闭数据库连接
db.Close()
}
三、MySQL事务操作
1、事务常用方法
func (db *DB) Begin() (*Tx, error)
开启事务,从连接池中取出一个*TX
类型连接。使用TX类型连接可以进行回滚事务和提交事务。func (tx *Tx) Commit() error
提交事务func (tx *Tx) Rollback() error
回滚func (tx *Tx) Exec(query string, args ...interface{}) (Result, error)
执行SQL操作func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error)
执行SQL查询操作
2、事务示例
// 支持事务回滚机制的批量数据插入
func MultiInsert(db *sql.DB) {
// 批量数据插入
tx, err := db.Begin()
if err != nil {
fmt.Println(err)
}
values := [][]interface{}{{2, "孙悟空", 500}, {3, "猪八戒", 200}, {4, "沙悟净", 100}}
stmt, err := tx.Prepare("INSERT INTO student(sid,sname,age) VALUES(?,?,?);")
for _, val := range values {
_, err := stmt.Exec(val...)
if err != nil {
fmt.Printf("INSERT failed:%v", err)
tx.Rollback()
}
}
tx.Commit()
}
四、MySQL操作的效率分析
1、sql接口效率分析
func sql.Open(driverName, dataSourceName string) (*DB, error)
sql.Open返回一个DB对象,DB对象对于多个goroutines并发使用是安全的,DB对象内部封装了连接池。Open函数并没有创建连接,只是验证参数是否合法,然后开启一个单独goroutine去监听是否需要建立新的连接,当有请求建立新连接时就创建新连接。func (db *DB) Exec(query string, args ...interface{}) (Result, error)
执行不返回行(row)的查询,比如INSERT,UPDATE,DELETE
DB交给内部的exec方法负责查询。exec会首先调用DB内部的conn方法从连接池里面获得一个连接。然后检查内部的driver.Conn是否实现了Execer接口,如果实现了Execer接口,会调用Execer接口的Exec方法执行查询;否则调用Conn接口的Prepare方法负责查询。func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
用于查询,DB交给内部的query方法负责查询。query首先调用DB内部的conn方法从连接池里面获得一个连接,然后调用内部的queryConn方法负责查询。func (db *DB) Prepare(query string) (*Stmt, error)
返回一个Stmt。Stmt对象可以执行Exec,Query,QueryRow等操作。DB交给内部的prepare方法负责查询。prepare首先调用DB内部的conn方法从连接池里面获得一个连接,然后调用driverConn的prepareLocked方法负责查询。func (db *DB) Begin() (*Tx, error)
开启事务,返回Tx对象。调用Begin方法后,TX会与指定的连接绑定,一旦事务提交或者回滚,事务绑定的连接就还给DB的连接池。DB交给内部的begin方法负责处理。begin首先调用DB内部的conn方法从连接池里面获得一个连接,然后调用Conn接口的Begin方法获得一个TX。
进行MySQL数据库操作时,如果每次SQL操作都从DB对象的连接池中获取连接,则会在很大程度上损耗效率。因此,必须尽量在一个连接上执行SQL操作。
2、效率分析示例
package main
import (
"database/sql"
"fmt"
"strconv"
"time"
_ "github.com/go-sql-driver/mysql"
)
var db = &sql.DB{}
func init() {
db, _ = sql.Open("mysql", "root:[email protected](118.24.159.133:3306)/student?charset=utf8")
CREATE_TABLE := "CREATE TABLE student(" +
"sid INT(10) NOT NULL AUTO_INCREMENT," +
"sname VARCHAR(64) NULL DEFAULT NULL," +
"age INT(10) DEFAULT NULL,PRIMARY KEY (sid))" +
"ENGINE=InnoDB DEFAULT CHARSET=utf8;"
db.Exec(CREATE_TABLE)
}
func update() {
//方式1 update
start := time.Now()
for i := 1001; i <= 1100; i++ {
db.Exec("UPDATE student set age=? where sid=? ", i, i)
}
end := time.Now()
fmt.Println("db.Exec update total time:", end.Sub(start).Seconds())
//方式2 update
start = time.Now()
for i := 1101; i <= 1200; i++ {
stm, _ := db.Prepare("UPDATE student set age=? where sid=? ")
stm.Exec(i, i)
stm.Close()
}
end = time.Now()
fmt.Println("db.Prepare 释放连接 update total time:", end.Sub(start).Seconds())
//方式3 update
start = time.Now()
stm, _ := db.Prepare("UPDATE student set age=? where sid=?")
for i := 1201; i <= 1300; i++ {
stm.Exec(i, i)
}
stm.Close()
end = time.Now()
fmt.Println("db.Prepare 不释放连接 update total time:", end.Sub(start).Seconds())
//方式4 update
start = time.Now()
tx, _ := db.Begin()
for i := 1301; i <= 1400; i++ {
tx.Exec("UPDATE student set age=? where sid=?", i, i)
}
tx.Commit()
end = time.Now()
fmt.Println("tx.Exec 不释放连接 update total time:", end.Sub(start).Seconds())
//方式5 update
start = time.Now()
for i := 1401; i <= 1500; i++ {
tx, _ := db.Begin()
tx.Exec("UPDATE student set age=? where sid=?", i, i)
tx.Commit()
}
end = time.Now()
fmt.Println("tx.Exec 释放连接 update total time:", end.Sub(start).Seconds())
}
func delete() {
//方式1 delete
start := time.Now()
for i := 1001; i <= 1100; i++ {
db.Exec("DELETE FROM student WHERE sid=?", i)
}
end := time.Now()
fmt.Println("db.Exec delete total time:", end.Sub(start).Seconds())
//方式2 delete
start = time.Now()
for i := 1101; i <= 1200; i++ {
stm, _ := db.Prepare("DELETE FROM student WHERE sid=?")
stm.Exec(i)
stm.Close()
}
end = time.Now()
fmt.Println("db.Prepare 释放连接 delete total time:", end.Sub(start).Seconds())
//方式3 delete
start = time.Now()
stm, _ := db.Prepare("DELETE FROM student WHERE sid=?")
for i := 1201; i <= 1300; i++ {
stm.Exec(i)
}
stm.Close()
end = time.Now()
fmt.Println("db.Prepare 不释放连接 delete total time:", end.Sub(start).Seconds())
//方式4 delete
start = time.Now()
tx, _ := db.Begin()
for i := 1301; i <= 1400; i++ {
tx.Exec("DELETE FROM student WHERE sid=?", i)
}
tx.Commit()
end = time.Now()
fmt.Println("tx.Exec 不释放连接 delete total time:", end.Sub(start).Seconds())
//方式5 delete
start = time.Now()
for i := 1401; i <= 1500; i++ {
tx, _ := db.Begin()
tx.Exec("DELETE FROM student WHERE sid=?", i)
tx.Commit()
}
end = time.Now()
fmt.Println("tx.Exec 释放连接 delete total time:", end.Sub(start).Seconds())
}
func query() {
//方式1 query
start := time.Now()
rows, _ := db.Query("SELECT sid,sname FROM student")
defer rows.Close()
for rows.Next() {
var name string
var id int
if err := rows.Scan(&id, &name); err != nil {
fmt.Println(err)
}
}
end := time.Now()
fmt.Println("db.Query query total time:", end.Sub(start).Seconds())
//方式2 query
start = time.Now()
stm, _ := db.Prepare("SELECT sid,sname FROM student")
defer stm.Close()
rows, _ = stm.Query()
defer rows.Close()
for rows.Next() {
var name string
var id int
if err := rows.Scan(&id, &name); err != nil {
fmt.Println(err)
}
}
end = time.Now()
fmt.Println("db.Prepare query total time:", end.Sub(start).Seconds())
//方式3 query
start = time.Now()
tx, _ := db.Begin()
defer tx.Commit()
rows, _ = tx.Query("SELECT sid,sname FROM student")
defer rows.Close()
for rows.Next() {
var name string
var id int
if err := rows.Scan(&id, &name); err != nil {
fmt.Println(err)
}
}
end = time.Now()
fmt.Println("tx.Query query total time:", end.Sub(start).Seconds())
}
func insert() {
//方式1 insert
start := time.Now()
for i := 1001; i <= 1100; i++ {
//每次循环内部都会去连接池获取一个新的连接,效率低下
db.Exec("INSERT INTO student(sid,sname,age) values(?,?,?)", i, "student"+strconv.Itoa(i), i-1000)
}
end := time.Now()
fmt.Println("db.Exec insert total time:", end.Sub(start).Seconds())
//方式2 insert
start = time.Now()
for i := 1101; i <= 1200; i++ {
//Prepare函数每次循环内部都会去连接池获取一个新的连接,效率低下
stm, _ := db.Prepare("INSERT INTO student(sid,sname,age) values(?,?,?)")
stm.Exec(i, "student"+strconv.Itoa(i), i-1000)
stm.Close()
}
end = time.Now()
fmt.Println("db.Prepare 释放连接 insert total time:", end.Sub(start).Seconds())
//方式3 insert
start = time.Now()
stm, _ := db.Prepare("INSERT INTO student(sid,sname,age) values(?,?,?)")
for i := 1201; i <= 1300; i++ {
//Exec内部并没有去获取连接,为什么效率还是低呢?
stm.Exec(i, "user"+strconv.Itoa(i), i-1000)
}
stm.Close()
end = time.Now()
fmt.Println("db.Prepare 不释放连接 insert total time:", end.Sub(start).Seconds())
//方式4 insert
start = time.Now()
//Begin函数内部会去获取连接
tx, _ := db.Begin()
for i := 1301; i <= 1400; i++ {
//每次循环用的都是tx内部的连接,没有新建连接,效率高
tx.Exec("INSERT INTO student(sid,sname,age) values(?,?,?)", i, "user"+strconv.Itoa(i), i-1000)
}
//最后释放tx内部的连接
tx.Commit()
end = time.Now()
fmt.Println("tx.Exec 不释放连接 insert total time:", end.Sub(start).Seconds())
//方式5 insert
start = time.Now()
for i := 1401; i <= 1500; i++ {
//Begin函数每次循环内部都会去连接池获取一个新的连接,效率低下
tx, _ := db.Begin()
tx.Exec("INSERT INTO student(sid,sname,age) values(?,?,?)", i, "user"+strconv.Itoa(i), i-1000)
//Commit执行后释放连接
tx.Commit()
}
end = time.Now()
fmt.Println("tx.Exec 释放连接 insert total time:", end.Sub(start).Seconds())
}
func main() {
insert()
query()
update()
query()
delete()
}
// output:
// db.Exec insert total time: 2.069104068
// db.Prepare 释放连接 insert total time: 1.869348813
// db.Prepare 不释放连接 insert total time: 1.447833105
// tx.Exec 不释放连接 insert total time: 1.098540307
// tx.Exec 释放连接 insert total time: 3.465670469
// db.Query query total time: 0.005803479
// db.Prepare query total time: 0.010966584
// tx.Query query total time: 0.011800843
// db.Exec update total time: 2.117122871
// db.Prepare 释放连接 update total time: 2.132430998
// db.Prepare 不释放连接 update total time: 1.523685366
// tx.Exec 不释放连接 update total time: 1.346163272
// tx.Exec 释放连接 update total time: 3.129312377
// db.Query query total time: 0.00848425
// db.Prepare query total time: 0.013472261
// tx.Query query total time: 0.012418198
// db.Exec delete total time: 2.100008271
// db.Prepare 释放连接 delete total time: 1.9821439490000001
// db.Prepare 不释放连接 delete total time: 1.429259466
// tx.Exec 不释放连接 delete total time: 1.103143464
// tx.Exec 释放连接 delete total time: 2.863670582
从示例结果看,执行SQL操作时如果不释放连接,则效率比释放连接要高。
以上是关于GO语言商业案例(十八):stream的主要内容,如果未能解决你的问题,请参考以下文章
Golang✔️走进 Go 语言✔️ 第十八课 通道关闭 & 工作池
Golang✔️走进 Go 语言✔️ 第十八课 通道关闭 & 工作池