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为后续查询或执行操作创建一个准备SQL
func (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\n", 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操作时如果不释放连接,则效率比释放连接要高。

原文地址:http://blog.51cto.com/9291927/2344802

时间: 2024-10-24 06:37:54

Go语言开发(十八)、Go语言MySQL数据库操作的相关文章

求推荐go语言开发工具及go语言应该以哪种目录结构组织代码?

go语言的开发工具推荐? go语言开发普通程序及开发web程序的时候,应该以哪种目录结构组织代码? 求推荐go语言开发工具及go语言应该以哪种目录结构组织代码? >> golang 这个答案描述的挺清楚的:http://www.goodpm.net/postreply/golang/1010000008839459/求推荐go语言开发工具及go语言应该以哪种目录结构组织代码.html

R语言使用RMySQL连接及读写Mysql数据库 测试通过

R语言使用RMySQL连接及读写Mysql数据库 简单说下安装过程,一般不会有问题,重点是RMySQL的使用方式. 系统环境说明 Redhat系统:Linux 460-42.6.32-431.29.2.el6.x86_64 系统编码:LANG=zh_CN.UTF-8(中文UTF-8格式) MySQL版本:mysql  Ver 14.14 Distrib 5.1.73, forredhat-linux-gnu (x86_64) using readline 5.1   安装mysql 1.    

第二百七十五节,MySQL数据库安装和介绍

MySQL数据库安装 一.概述 1.什么是数据库 ? 答:数据的仓库,称其为数据库 2.什么是 MySQL.Oracle.SQLite.Access.MS SQL Server等 ? 答:他们均是一种软件,都有两个主要的功能: a. 将数据保存到文件或内存 b. 接收特定的命令,然后对文件进行相应的操作 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司.MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDB

攻城狮在路上(叁)Linux(十八)--- 文件系统的简单操作

本篇仅作为补漏. 一.查看磁盘和目录的容量:df  du df:列出文件系统的整体磁盘使用量. du:评估文件系统的磁盘使用量(常用于评估目录所占容量) 二.连接文件:ln 1.hard link:硬连接或实际连接 ln /etc/crontab crontab1; <== 创建硬连接 硬连接不创建文件(虽然使用ls命令时可以看到一个新的文件,但是两者的inode一致),仅是在目录block中增加一个文件名-inode的映射而已. 限制: A.不能跨文件系统. B.不能连接到目录(因为复杂度较高

PHP移动互联网开发笔记(6)——MySQL数据库基础回顾

最近看了一些架构方面的资料,但是发现基础知识都不怎么牢固,接下来的一段时间,我会定期总结基础知识. 一.数据类型 1.整型 数据类型 存储空间 说明 取值范围 TINYINT 1字节 非常小的整数 带符号值:-128~127 无符号值:0~255 SMALLINT 2字节 较小的整数 带符号值:-32768~32767 无符号值:0~65535 MEDIUMNT 3字节 中等大小的整数 带符号值:-8388608~8388607 无符号值:0~16777215 INT 4字节 标准整数 带符号值

Java进阶(二十五)Java连接mysql数据库(底层实现)

Java进阶(二十五)Java连接mysql数据库(底层实现) 前言 很长时间没有系统的使用java做项目了.现在需要使用java完成一个实验,其中涉及到java连接数据库.让自己来写,记忆中已无从搜索.特将之前使用的方法做一简单的总结.也能够在底层理解一下连接数据库的具体步骤. 实现 首先需要导入相关的jar包,我使用的为:mysql-connector-java-5.1.7-bin.jar. 下面来看一下我所使用的数据库连接方法类: MysqlUtil.java package cn.edu

Python/MySQL(四、MySQL数据库操作)

Python/MySQL(四.MySQL数据库操作) 一.数据库条件语句: 1 case when id>9 then ture else false 二.三元运算: 1 if(isnull(xx)0,1) 三.上下连表: select id,name from ta1 union 天然去重(检测上边的表和下边的表行内完全一样就只显示一行内容) select num,sname from tb2 ========================================== select

MySql数据库操作

MySql数据库操作 –登录MySql:--– Windows环境进入cmd后输入mysql –h localhost –u root –p,再输入密码就可以启动mysql;其中localhost是mysql服务器所在的ip如果是本机可以用localhost. –数据库相关操作--– create database db_name(数据库名称)建名为db_name的数据库 show databases查看已经存在的数据库 drop database db_name删除名为db_name的数据库

Python进行MySQL数据库操作

最近开始玩Python,慢慢开始喜欢上它了,以前都是用shell来实现一些自动化或者监控的操作,现在用Python来实现,感觉更棒,Python是一门很强大的面向对象语言,所以作为一个运维DBA或者运维来说,都应该学会用Python来提高工作效率.下面简单的介绍一下Python DB API MySQLdb 使用Python DB API访问数据库的流程图: 在Centos下安装MySQLdb模板(为了方便演显,我用yum安装,也是最快最省事的安装): yum install MySQL-pyt