思路首先我们需要先将目标文件导出成csv格式，然后下载到指定目录，然后执行导入mysql语句将csv文件导入。本例将展示将云数据导入到mysql的示例，以及导入过程中遇到的坑。//将云端数据下载到本地 func upload(url string) string { if url == "" { return "" } //先将读取的文件内容存到本地文件 client := &http.Client{} Sugar.Info("4") reqest, err := http.NewRequest("GET", url, nil) reqest.Header.Add("Content-Type", "application/octet-stream") if err != nil { Sugar.Error(err, "返回结果失败") } //处理返回结果 resp, err := client.Do(reqest) if err != nil { Sugar.Error(err, "处理返回结果") } defer resp.Body.Close() //fmt.Println(resp) body, err := ioutil.ReadAll(resp.Body) if err != nil { Sugar.Error(err, "读取失败") } err = ioutil.WriteFile("data.csv", body, 0644) if err != nil { Sugar.Error(err, "存储失败") } return "yes" }这时我们已将把数据下栽到了本地，接下来我们需要将数据上传至mysqllocal:本地数据'/usr/local/data.csv':是文件的路径CHARACTER SET utf8mb4:设置字符集为utf8mb4fields terminated by ','：字段与字段之间用“,”相隔，lines terminated by 'n'：每行已n作为结束的标志ignore 1 lines;`：无视第一行数据，因为第一行是字段，而不是具体的数据特别注意字符集是非常重要的，当你数据中存在一些非utf8的数据时会导入失败，我们需要根据不同情况选择不同字符集//在上传之前我们需要将所要上传的文件注册一下，否则不能够上传成功 mysql.RegisterLocalFile("/usr/local/data.csv") //然后我们写sql语句，在此所用的包是"github.com/go-sql-driver/mysql" sqls0 := `load data local infile '/usr/local/data.csv' into table totalnum CHARACTER SET utf8mb4 fields terminated by ',' optionally enclosed by '"' escaped by '\"' lines terminated by '\n' ignore 1 lines;`然后我们执行sql语句 smt, err := db.Prepare(sqls0) if err != nil { Sugar.Error(err, "创建表预处理失败") } _, err = smt.Exec() if err != nil { Sugar.Error(err, "表中添加信息失败") } else { Sugar.Info("清空表数据") }此时我们就可以将我们的云数据导入到我们的mysql表中了
简介InnoDB在MySQL 5.5版本代替MyISAM成为默认的存储引擎，这也是Oracle收购Sun之后发布的首个版本。InnoDB采用MVCC（多版本并发控制）来支持高并发，并且实现了四个标准的隔离级别，默认级别是REPEAABLE READ（可重复读），并且通过间隙锁（next-key locking）策略防止幻读的出现。InnoDB表是基于聚簇索引建立的，对于主键查询有很高的性能。这篇博客重点记录一下MVCC和聚簇索引。MVCCMVCC（多版本并发控制）可以认为是行级锁的一个变种，但是它在很多情况下避免了加锁操作，因此开销更低。InnoDB的MVCC，是通过在每行记录后面保存两个隐藏的列来实现的。这两个列保存了行的创建时间和删除时间，但存储的并不是实际的时间值，而是系统版本号。版本号随着事务的操作逐渐递增，通过将当前版本号与版本号比较来判断操作是否合法。具体的操作如下。SELECTInnoDB会根据以下两个条件检查每行记录：InnoDB只查找版本号早于当前事务版本的数据行（这就保证了此行数据要么是事务开始之前已经存在的，要么是事务自身插入或者修改过的）。行的删除要么未定义，要么大于当前版本号（意味着有一个并发事务把这条数据删除了）。这可以确保事务读取到的行，在事务开始之前未被删除。只有符合上述两个条件的记录，才能返回作为查询结果。INSERTInnoDB为新插入的每一行数据保存当前系统版本号作为行版本号。DELETEInnoDB为删除的每一行数据保存当前系统版本号作为行删除标识。UPDATE注意此时的UPDATE命令实际上是执行了“DELETE + INSERT”。将旧行的删除版本号设置为当前版本号，并将新行INSERT同时设置创建版本号为当前版本号。另外从网上一篇博客上看到，由于旧数据并不真正的删除，所以必须对这些数据进行清理，innodb会开启一个后台线程执行清理工作，具体的规则是将删除版本号小于当前系统版本的行删除，这个过程叫做purge。聚簇索引在《数据库原理》里面，对聚簇索引的解释是:聚簇索引的顺序就是数据的物理存储顺序。而在InnoDB中的聚簇索引实际上在同一个结构中保存了B-Tree索引和数据行。在聚簇索引中，数据行和相邻的键值紧凑的存储在一起，因为无法同时把数据行存在两个不同的地方，所以一个表只能有一个聚簇索引。表中行的物理顺序和索引中行的物理顺序是相同的，在创建任何非聚簇索引之前创建聚簇索引，这是因为聚簇索引改变了表中行的物理顺序，数据行按照一定的顺序排列，并且自动维护这个顺序。聚簇索引默认是主键，如果表中没有定义主键，InnoDB 会选择一个唯一且非空的索引代替。一般来说使用int型自增变量作为主键方便排序并且默认会在索引树的末尾增加主键值，对索引树的结构影响最小以上只是聚簇索引的一些基本知识，具体实现鸽一篇博客。怎样选择存储引擎MySQL有很多种类的引擎。在大部分情况下，InnoDB都是正确的选择，支持行级锁、事务、事务安全等。但是如果是查询占了服务的大多数时间时可以考虑MyISAM，像是日志等。不过相比于选择存储引擎，我们更应该考虑避免在服务中混合使用存储引擎。如果混合使用了支持事务和不支持事务的表，在正常提交时不会有什么问题，但如果该事务需要回滚，非事务的表的操作变更就无法撤销，这会导致数据库数据处于不一致状态。
目的在我们后端写项目时，数据一般都是保存在MySQL数据库里。但是有时我们不能只是简单的把数据保存在MySQL数据库里（删库跑路）。在这里我们就要经常备份一下我们的数据库或者是数据库里面的某一个表。Navicat在这里我们使用的是MySQL可视化工具里最棒的navicat，所以先安装：Navicat下载连接先点击左上角的连接，然后连接名是自己随便取的，主机名填想要连接数据库的IP，自己用一般默认为localhost，用户名和密码填自己MySQL的用户名和密码。Go代码实现导入包import ( "io/ioutil" "log" "os/exec" "time" )在这里我们要使用一个平常很少使用的包os/exex, exec包执行外部命令，详情可以去参考Go语言标准文档。全代码package main import ( "io/ioutil" "log" "os/exec" "time" ) func main() { BackupMySqlDb("localhost", "3306", "root", "123456", "school", "", "E:/") } /** * * 备份MySql数据库 * @param host: 数据库地址: localhost * @param port: 端口: 3306 * @param user: 用户名: root * @param password: 密码: root * @param databaseName: 需要被分的数据库名: test * @param tableName: 需要备份的表名: user * @param sqlPath: 备份SQL存储路径: D:/backup/test/ * @return backupPath * */ func BackupMySqlDb(host, port, user, password, databaseName, tableName, sqlPath string) (error,string) { //定义Cmd结构体对象指针 var cmd *exec.Cmd //在这里如果没有传输表名，那么将会备份整个数据库，否则将只备份自己传入的表 if tableName == "" { cmd = exec.Command("mysqldump", "--opt", "-h"+host, "-P"+port, "-u"+user, "-p"+password, databaseName) } else { cmd = exec.Command("mysqldump", "--opt", "-h"+host, "-P"+port, "-u"+user, "-p"+password, databaseName, tableName) } //StdinPipe方法返回一个在命令Start后与命令标准输入关联的管道。 stdout, err := cmd.StdoutPipe() if err != nil { log.Println(err) return err,"" } if err := cmd.Start(); err != nil { log.Println(err) return err,"" } bytes, err := ioutil.ReadAll(stdout) if err != nil { log.Println(err) return err,"" } //获得一个当前的时间戳 now := time.Now().Format("20060102150405") var backupPath string //设置我们备份文件的名字 if tableName == "" { backupPath = sqlPath+databaseName+"_"+now+".sql" } else { backupPath = sqlPath+databaseName+"_"+tableName+"_"+now+".sql" } //写入文件并设置文件权限 err = ioutil.WriteFile(backupPath, bytes, 0644) if err != nil { log.Println(err) return err,"" } return nil,backupPath } 示例上述代码以school数据库为例，由于表名为空所以在这里是备份一整个数据库，运行程序即可然后会在我们设置的路径下找到我们代码生成的sql文件运行sql文件右键点击我们的数据库或者表里面有个运行SQL文件，然后选中自己生成的SQL文件即可，但是导入SQL文件会把相同表名的表数据替换，所以导入时一般选择一个额外的数据库显示。
1.下载安装go操作mysql的驱动包1.1下载安装当下比较流行的Go-SQL-Driver/MySQL下载： go get github.com/Go-SQL-Driver/MySQL安装： go install github.com/Go-SQL-Driver/MySQL1.2导入mysql驱动包import ( "database/sql" _"github.com/Go-SQL-Driver/MySQL" )这下准备工作就完成了！2.操作数据库2.1连接数据库Open函数：db, err := sql.Open("mysql", "用户名:密码@tcp(IP:端口)/数据库?charset=utf8")例如：db, err := sql.Open("mysql","root:111111@tcp(127.0.0.1:3306)/userinfo?charset=utf8")2.2创建表CREATE TABLE `posts` ( `id` int(20) UNSIGNED NOT NULL AUTO_INCREMENT , `content` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL , `author` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL , PRIMARY KEY (`id`), UNIQUE INDEX `id` (`id`) USING BTREE ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci AUTO_INCREMENT=4 ROW_FORMAT=COMPACT ;查看创建好的表结构2.3事务//在操作数据库之前执行， db.Begin() //例： tx, err := db.Begin() //保存到数据库： （有对数据库进行改动的时候必须要提交） err := tx.Commit() //回滚： err := tx.Rollback() //注意设置事务以后操作数据库就不是db了，而是 tx2.4增加一条记录直接使用Exec函数添加res, err := tx.Exec("insert into posts (content, author) values (?, ?)", "我是一行文本","machooo")首先使用Prepare获得stmt，然后调用Exec添加stmt, err := tx.Prepare("insert into posts (content, author) values (?, ?)")res, err := stmt.Exec("我是一行文本","machooo") 另一个经常用到的功能，获得刚刚添加数据的自增IDid, err := res.LastInsertId()代码截图2.5删除一条记录删除和上面的增加语法一样，只是把其中的INSERT语句改为DELETE语句2.6获取一条记录直接使用QueryRow()函数获取(这里直接写入了post结构体中)err = tx.QueryRow("select id, content, author from posts where id = ?", id).Scan(&post.Id, &post.Content,&post.Author) 代码截图 2.7查询多条记录，并遍历Query 获取数据，for xxx.Next() 遍历数据代码截图 2.8修改一条记录修改和上面的增加语法一样，只是把其中的INSERT语句改为UPDATE语句本文部分相关代码package main import ( "fmt" "database/sql" _ "github.com/Go-SQL-Driver/MySQL" ) var Db *sql.DB // connect to the Db func init() { var err error //Open函数的真正作用是设置好链接数据库所需的各个结构，并以惰性的方式，等真正需要时才建立相应的链接。 Db, err = sql.Open("mysql", "root:111111@tcp(127.0.0.1:3306)/userinfo?charset=utf8") if err != nil { panic(err) } } // Get a single post func retrieve(id int) (post Post, err error) { post = Post{} tx, _ := Db.Begin() err = tx.QueryRow("select id, content, author from posts where id = ?", id).Scan(&post.Id, &post.Content, &post.Author) return } func getall() (err error) { tx, _ := Db.Begin() rows, err := tx.Query("select id, content, author from posts") var id int var content, author string for rows.Next() { if err := rows.Scan(&id, &content, &author); err != nil { fmt.Println(err) } fmt.Println(id) fmt.Println(content) fmt.Println(author) } return } // Create a new post func (post *Post) create() (err error) { tx, err := Db.Begin() statement := "insert into posts (content, author) values (?, ?) " stmt, err := tx.Prepare(statement) if err != nil { return } defer stmt.Close() _, err = stmt.Exec(post.Content, post.Author) tx.Commit() return } // Update a post func (post *Post) update() (err error) { tx, _ := Db.Begin() _, err = tx.Exec("update posts set content = ?, author = ? where id = ?", post.Content, post.Author, post.Id) tx.Commit() return } // Delete a post func (post *Post) delete() (err error) { tx, _ := Db.Begin() _, err = tx.Exec("delete from posts where id = ?", post.Id) tx.Commit() return }
事务，就是把一堆事情绑在一起做，都成功了才算完成，否则就恢复之前的样子 举例：银行ATM取钱，扣款成功后突然停电了，吐钱的操作还没做完，这时候ATM机就要恢复到没取钱时候的状态，否则钱扣了还没拿到手，这多难受呀，所以这就需要用到事务，如果是扣款成功后突然停电，此时应该回滚所有的事务，撤消扣款。事务是应用程序中一系列严密的操作，所有操作必须成功完成，否则在每个操作中所做的所有更改都会被撤消。也就是事务具有原子性，一个事务中的一系列的操作要么全部成功，要么一个都不做。 事务的结束有两种，当事务中的所有步骤全部成功执行时，事务提交。如果其中一个步骤失败，将发生回滚操作，撤消之前到事务开始时的所有操作。事务所具有的特点A.原子性：原子是自然界中最小的颗粒，具有不可再分的特点 事物中的所有操作可以看作是一个原子(事务是数据库的逻辑工作单位)，要么全部执行，要么全不执行.B.一致性：事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态.因此当数据库只包含成功事务提交的结果时，就说数据库处于一致性状态.如果数据库系统 运行中发生故障，有些事务尚未完成就被迫中断，这些未完成事务对数据库所做的修改有一部分已写入物理数据库，这时数据库就处于一种不正确的状态，或者说是 不一致的状态 .C.隔离性：隔离性指各个事务的操作是互不干扰的，即一个事务内部的操作及使用的数据对其它并发事务是隔离的，并发执行的各 个事务之间不能互相干扰.D.持久性：也称永久性，指一个事务一旦提交，它对数据库中的数据的改变就应该是永久性的.接下来的其它操作或故障不应该对其执行结果有任何影响.首先我们要开启事务Tx, _ := Db.Begin()中间的是mysql操作error := Tx.Commit()这期间可写若干mysql操作若期间发生问题就回滚一下tx.Rollback()fmt.println("操作失败")具体代码实现：package main import ( "database/sql" "fmt" _ "mysql" ) func shiwu(id int, username string, password int, Db *sql.DB) error { Tx, _ := Db.Begin() stmt, _ := Db.Prepare("INSERT INTO Student(Id,Username,Password) VALUES(?,?,?)") result, _ := stmt.Exec(id, username, password) fmt.Println(result) stmt1, _ := Db.Prepare("DELETE FROM post WHERE id=?") result, _ = stmt1.Exec(6) error := Tx.Commit() return error } func main() { var Db *sql.DB Db, err := sql.Open("mysql", "root:123456@tcp(192.168.24.131:3306)/practise") if err != nil { panic(err.Error()) } else { err = shiwu(6, "yuchao", 456789, Db) fmt.Println(err) } }
go如何操作mysql中的datetime类型最近项目中用到了关于数据库中存取时间类型的字段，于是我百度了一下在go语言中与mysql中相匹配的字段类型，很容易我查到了time.Time与mysql中的datetime相对应，毫不犹豫的就是用了，代码中我是这样写的type UserLend struct { UserID string `json:"userID"` Bookcode string `json:"bookcode"` Lenddate time.Time `json:"lenddate"` Returndate time.Time `json:"returndate"` Status int `json:"status"` Bookisbn string `json:"bookisbn"` }那么怎么获取到该字段呢？我想既然可以直接匹配那打印起来也应该没啥问题（我觉得会自动赋值到time.Time类型），于是我这样写：var oldlenddate time.Time d.QueryRow("select lenddate from lendbook where userid=? and bookcode = ?", userid, code).Scan(&oldlenddate) fmt.Println(oldlenddate)输出结果是这样的：0001-01-01 00:00:00 +0000 UTC然后我百度了一下，果然大家都遇到了这个问题原因是因为对于_datetime_ 默认会_使用_UTC时间，然后如果说需要获得中国的时间只需要在打开数据库的时候加上charset=utf8&loc=Asia%2FShanghai&parseTime=true果然最终可以实现成功获取了结果是这样：2019-06-11 11:56:58 +0800 CST最后补充一点关于time.Time的知识：**在time包中有这样一个结构体：type Location struct { // 内含隐藏或非导出字段 }Location代表一个（关联到某个时间点的）地点，以及该地点所在的时区。var Local *Location = &localLocLocal代表系统本地，对应本地时区。var UTC *Location = &utcLocUTC代表通用协调时间，对应零时区。func LoadLocationfunc LoadLocation(name string) (*Location, error)LoadLocation返回使用给定的名字创建的Location。如果name是""或"UTC"，返回UTC；如果name是"Local"，返回Local；否则name应该是IANA时区数据库里有记录的地点名（该数据库记录了地点和对应的时区），如"America/New_York"。LoadLocation函数需要的时区数据库可能不是所有系统都提供，特别是非Unix系统。此时LoadLocation会查找环境变量ZONEINFO指定目录或解压该变量指定的zip文件（如果有该环境变量）；然后查找Unix系统的惯例时区数据安装位置，最后查找$GOROOT/lib/time/zoneinfo.zip。