golang操作连接数据库实现mysql事务示例

目录
  • mysql驱动
  • posgre驱动
  • 连接postgres
  • 连接mysql
  • 初始化连接
  • SetMaxOpenConns
  • SetMaxIdleConns
  • CRUD
    • 查询
    • 单行查询QueryRow
    • 多行查询Query-rows
  • 插入和更新和删除Exec
    • 影响的行数
    • 插入
    • 更新
    • 删除
  • MySQL预处理
    • 为什么要预处理?
    • Go实现MySQL预处理
  • SQL注入问题
  • Go实现MySQL事务
    • 什么是事务?
    • 事务的ACID
    • 事务相关方法
    • 事务示例

MySQL是业界常用的关系型数据库,本文介绍了database/sql库以及Go语言如何操作MySQL数据库。

mysql驱动

_ "github.com/go-sql-driver/mysql"

posgre驱动

_ "github.com/lib/pq"

连接postgres

Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的数据库驱动。使用database/sql包时必须注入(至少)一个数据库驱动。

我们常用的数据库基本上都有完整的第三方实现。例如:MySQL驱动

func Open(driverName, dataSourceName string) (*DB, error)
import (
	"database/sql"
	"fmt"
	_ "github.com/lib/pq"
)
Db, _ := sql.Open("postgres", "postgres://postgres:iLoveShark@127.0.0.1:3432/master?sslmode=disable&fallback_application_name=bikesvc")

连接mysql

Open打开一个dirverName指定的数据库,dataSourceName指定数据源,一般至少包括数据库文件名和其它连接必要的信息。

import (
	"database/sql"
	_ "github.com/go-sql-driver/mysql"
)
func main() {
   // DSN:Data Source Name
	dsn := "user:password@tcp(127.0.0.1:3306)/dbname"
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		panic(err)
	}
	defer db.Close()  // 注意这行代码要写在上面err判断的下面
}

初始化连接

Open函数可能只是验证其参数格式是否正确,实际上并不创建与数据库的连接。如果要检查数据源的名称是否真实有效,应该调用Ping方法。

返回的DB对象可以安全地被多个goroutine并发使用,并且维护其自己的空闲连接池。因此,Open函数应该仅被调用一次,很少需要关闭这个DB对象。

// 定义一个全局对象db
var db *sql.DB
// 定义一个初始化数据库的函数
func initDB() (err error) {
	// DSN:Data Source Name
	dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
	// 不会校验账号密码是否正确
	// 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	// 尝试与数据库建立连接(校验dsn是否正确)
	err = db.Ping()
	if err != nil {
		return err
	}
	return nil
}
func main() {
	err := initDB() // 调用输出化数据库的函数
	if err != nil {
		fmt.Printf("init db failed,err:%v\n", err)
		return
	}
}

其中sql.DB是表示连接的数据库对象(结构体实例),它保存了连接数据库相关的所有信息。它内部维护着一个具有零到多个底层连接的连接池,它可以安全地被多个goroutine同时使用。

SetMaxOpenConns

func (db *DB) SetMaxOpenConns(n int)

SetMaxOpenConns设置与数据库建立连接的最大数目。 如果n大于0且小于最大闲置连接数,会将最大闲置连接数减小到匹配最大开启连接数的限制。 如果n<=0,不会限制最大开启连接数,默认为0(无限制)。

SetMaxIdleConns

func (db *DB) SetMaxIdleConns(n int)

SetMaxIdleConns设置连接池中的最大闲置连接数。 如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制。 如果n<=0,不会保留闲置连接。

CRUD

查询

为了方便查询,我们事先定义好一个结构体来存储user表的数据。

type Info struct {
	Id      int    `json:"id"`
	Code    string `json:"code"`
	HwCode  string `json:"hw_code"`
	Name    string `json:"name"`
	Des     string `json:"des"`
	Created int64  `json:"created"`
	Updated int64  `json:"updated"`
	BrandId int64  `json:"brand_id"`
}

单行查询QueryRow

单行查询db.QueryRow()执行一次查询,并期望返回最多一行结果(即Row)。QueryRow总是返回非nil的值,直到返回值的Scan方法被调用时,才会返回被延迟的错误。(如:未找到结果)

func (db *DB) QueryRow(query string, args ...interface{}) *Row

具体示例代码:

func (o object) QueryInfo(id int) *Info {
	r := new(Info)
	var qstr string
	switch {
	case id > 0:
		qstr += fmt.Sprintf(" and id = %d", id)
	default:
		o.Log.Error("invalid param")
		return nil
	}
	sqlstr := `
	select
	coalesce(id,0) as id,
	coalesce(code,'') as code,
	coalesce(hw_code,'') as hw_code,
	coalesce(name,'') as name,
	coalesce(des,'') as des,
	coalesce(created,0) as created,
	coalesce(updated,0) as updated,
	coalesce(brand_id,0) as brand_id
	from test_table
	where 1=1
	`
	sqlstr += qstr
	err := o.DbRo.QueryRow(sqlstr).Scan(&r.Id, &r.Code, &r.HwCode, &r.Name, &r.Des, &r.Created, &r.Updated, &r.BrandId)
	if err != nil {
		fmt.Println(err)
		o.Log.Errorf("param=%d,sql=%s,err=%v", id, sqlstr, err)
		return nil
	}
	return r
}

多行查询Query-rows

多行查询db.Query()执行一次查询,返回多行结果(即Rows),一般用于执行select命令。参数args表示query中的占位参数。

func (db *DB) Query(query string, args ...interface{}) (*Rows, error)

具体示例代码:

func (o object) QueryList(brandId int) (list []Info) {
	list = make([]Info, 0)
	var qstr string
	switch {
	case brandId > 0:
		qstr += fmt.Sprintf(" and brand_id = %d", brandId)
	default:
		o.Log.Error("invalid param")
		return nil
	}
	sqlstr := `
	select
	coalesce(id,0) as id,
	coalesce(code,'') as code,
	coalesce(hw_code,'') as hw_code,
	coalesce(name,'') as name,
	coalesce(des,'') as des,
	coalesce(created,0) as created,
	coalesce(updated,0) as updated,
	coalesce(brand_id,0) as brand_id
	from bike_color
	where 1=1
	`
	sqlstr += qstr
	rows, err := o.DbRo.Query(sqlstr)
	if err != nil {
		o.Log.Error(err)
		return
	}
	defer rows.Close()
  // rows.Next()获取下一条结果
	for rows.Next() {
		var r = new(Info)
		err = rows.Scan(&r.Id, &r.Code, &r.HwCode, &r.Name, &r.Des, &r.Created, &r.Updated, &r.BrandId)
		if err != nil {
			o.Log.Error(err, brandId)
			continue
		}
		list = append(list, *r)
	}
	return
}

插入和更新和删除Exec

插入、更新和删除操作都使用Exec方法。

func (db *DB) Exec(query string, args ...interface{}) (Result, error)

Exec执行一次命令(包括查询、删除、更新、插入等),返回的Result是对已执行的SQL命令的总结。参数args表示query中的占位参数。

影响的行数

sqlStr := "update user set age=? where id = ?"
ret, _:= db.Exec(sqlStr, 39, 3)
n, _ := ret.RowsAffected() // 操作影响的行数

插入

具体插入数据示例代码如下:

func (o object) Insert(i *Info) (r int) {
	sqlstr := `insert into bike_model(name,ctrl_tmpl,batt_support,bike_class,pics,code,brand_id,hw_code)
	values('%v',%d,%d,%d,'%v','%v',%d,'%v')
	returning id`
	sqlstr = fmt.Sprintf(sqlstr, i.Name, i.CtrlTmpl, i.BattSupport, i.BikeClass, i.Pics, i.Code, i.BrandId, i.HwCode)
	fmt.Println(sqlstr)
	err := o.Db.QueryRow(sqlstr).Scan(&r)
	if err != nil {
		fmt.Println(err)
		o.Log.Errorf("param=%+v,sql=%s,err=%v", i, sqlstr, err)
		return
	}
	return
}

更新

具体更新数据示例代码如下:

func (o object) Update(i *Info) (r int) {
	sqlstr := fmt.Sprintf("update bike_model set ")
	if i.Id <= 0 {
		o.Log.Error("invalid param")
		return
	}
	if len(i.Name) > 0 {
		sqlstr += fmt.Sprintf("name = '%v',", i.Name)
	}
	if i.CtrlTmpl > 0 {
		sqlstr += fmt.Sprintf("ctrl_tmpl = '%d',", i.CtrlTmpl)
	}
	if i.BattSupport > 0 {
		sqlstr += fmt.Sprintf("batt_support = '%d',", i.BattSupport)
	}
	if i.BikeClass > 0 {
		sqlstr += fmt.Sprintf("bike_class = '%d',", i.BikeClass)
	}
	if len(i.Pics) > 0 {
		sqlstr += fmt.Sprintf("hw_code = '%v',", i.Pics)
	}
	if len(i.Code) > 0 {
		sqlstr += fmt.Sprintf("code = '%v',", i.Code)
	}
	if i.BrandId > 0 {
		sqlstr += fmt.Sprintf("brand_id = %d,", i.BrandId)
	}
	if len(i.HwCode) > 0 {
		sqlstr += fmt.Sprintf("hw_code = '%v',", i.HwCode)
	}
  // 拼接sql字符串
	sqlstr = strings.TrimRight(sqlstr, ",")
	sqlstr += fmt.Sprintf("where id = %d returning id", i.Id)
	err := o.Db.QueryRow(sqlstr).Scan(&r)
	if err != nil {
		o.Log.Errorf("param=%+v,sql=%s,err=%v", i, sqlstr, err)
		return
	}
	return
}

删除

具体删除数据的示例代码如下:

func (o object) Delete(id int) (r int) {
	sqlstr := `delete from bike_model where id = $1`
	res, err := o.Db.Exec(sqlstr, id)
	if err != nil {
		o.Log.Errorf("param=%d,sql=%s,err=%v", id, sqlstr, err)
		return
	}
  // 影响的行数
	ra, _ := res.RowsAffected()
	if ra != 1 {
		o.Log.Warnf("param=%d,sql=%s,rowsAftected=%d", id, sqlstr, ra)
		return int(ra)
	}
	return
}

MySQL预处理

普通SQL语句执行过程:

  • 客户端对SQL语句进行占位符替换得到完整的SQL语句。
  • 客户端发送完整SQL语句到MySQL服务端
  • MySQL服务端执行完整的SQL语句并将结果返回给客户端。

预处理执行过程:

  • 把SQL语句分成两部分,命令部分与数据部分。
  • 先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
  • 然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
  • MySQL服务端执行完整的SQL语句并将结果返回给客户端。

为什么要预处理?

  • 优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。
  • 避免SQL注入问题。

Go实现MySQL预处理

database/sql中使用下面的Prepare方法来实现预处理操作。

func (db *DB) Prepare(query string) (*Stmt, error)

Prepare方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。

查询操作的预处理示例代码如下:

// 预处理查询示例
func prepareQueryDemo() {
	sqlStr := "select id, name, age from user where id > ?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(0)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	// 循环读取结果集中的数据
	for rows.Next() {
		var u user
		err := rows.Scan(&u.id, &u.name, &u.age)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return
		}
		fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
	}
}

插入、更新和删除操作的预处理十分类似,这里以插入操作的预处理为例:

// 预处理插入示例
func prepareInsertDemo() {
	sqlStr := "insert into user(name, age) values (?,?)"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec("小王子", 18)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	_, err = stmt.Exec("沙河娜扎", 18)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	fmt.Println("insert success.")
}

SQL注入问题

我们任何时候都不应该自己拼接SQL语句!

这里我们演示一个自行拼接SQL语句的示例,编写一个根据name字段查询user表的函数如下:

// sql注入示例
func sqlInjectDemo(name string) {
	sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name)
	fmt.Printf("SQL:%s\n", sqlStr)
	var u user
	err := db.QueryRow(sqlStr).Scan(&u.id, &u.name, &u.age)
	if err != nil {
		fmt.Printf("exec failed, err:%v\n", err)
		return
	}
	fmt.Printf("user:%#v\n", u)
}

此时以下输入字符串都可以引发SQL注入问题:

sqlInjectDemo("xxx' or 1=1#")
sqlInjectDemo("xxx' union select * from user #")
sqlInjectDemo("xxx' and (select count(*) from user) <10 #")

补充:不同的数据库中,SQL语句使用的占位符语法不尽相同。

数据库 占位符语法
MySQL ?
PostgreSQL $1, ​​​​​​​$2等
SQLite ? 和$1
Oracle :name

Go实现MySQL事务

什么是事务?

事务:一个最小的不可再分的工作单元;通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元),同时这个完整的业务需要执行多次的DML(insert、update、delete)语句共同联合完成。A转账给B,这里面就需要执行两次update操作。

在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务。事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行。

事务的ACID

通常事务必须满足4个条件(ACID):原子性(Atomicity,或称不可分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久性(Durability)。

条件 解释
原子性 一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
一致性 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
隔离性 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。
持久性 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

事务相关方法

Go语言中使用以下三个方法实现MySQL中的事务操作。 开始事务

func (db *DB) Begin() (*Tx, error)

提交事务

func (tx *Tx) Commit() error

回滚事务

func (tx *Tx) Rollback() error

事务示例

下面的代码演示了一个简单的事务操作,该事物操作能够确保两次更新操作要么同时成功要么同时失败,不会存在中间状态。

// 事务操作示例
func transactionDemo() {
	tx, err := db.Begin() // 开启事务
	if err != nil {
		if tx != nil {
			tx.Rollback() // 回滚
		}
		fmt.Printf("begin trans failed, err:%v\n", err)
		return
	}
	sqlStr1 := "Update user set age=30 where id=?"
	ret1, err := tx.Exec(sqlStr1, 2)
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("exec sql1 failed, err:%v\n", err)
		return
	}
	affRow1, err := ret1.RowsAffected()
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n", err)
		return
	}
	sqlStr2 := "Update user set age=40 where id=?"
	ret2, err := tx.Exec(sqlStr2, 3)
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("exec sql2 failed, err:%v\n", err)
		return
	}
	affRow2, err := ret2.RowsAffected()
	if err != nil {
		tx.Rollback() // 回滚
		fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n", err)
		return
	}
	fmt.Println(affRow1, affRow2)
	if affRow1 == 1 && affRow2 == 1 {
		fmt.Println("事务提交啦...")
		tx.Commit() // 提交事务
	} else {
		tx.Rollback()
		fmt.Println("事务回滚啦...")
	}
	fmt.Println("exec trans success!")
}

以上就是golong操作连接数据库实现mysql事务示例的详细内容,更多关于golong操作实现mysql事务的资料请关注我们其它相关文章!

(0)

相关推荐

  • golang实现mysql数据库备份的操作方法

    背景 navicat是mysql可视化工具中最棒的,但是,在处理视图的导入导出方面,它是按照视图名称的字母顺序来处理的,若视图存在依赖,在导入过程中就会报错.前面已经用python写了一个,但在使用过程中,遇到xfffd编码,python的pymysql会直接崩溃.发现golang没有这个问题,正好用go重写,来熟悉golang. 一些关键点 map & json,在处理主键与外键信息时,需要用到json数据结构来存储中间结果,因为要灵活处理,在golang中只能用map[string]inte

  • 在golang中操作mysql数据库的实现代码

    前言 Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能: •sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作. •sql.DB 为我们管理数据库连接池 需要注意的是,sql.DB表示操作数据库的抽象访问接口,而非一个数据库连接对象;它可以根据driver打开关闭数据库连接,管理连接池.正在使用的连接被标记为繁忙,用完后回到连接池等待下次使用.所以,如果你没有把连接释放回连接池,

  • Golang操作MySql数据库的完整步骤记录

    前言 MySQL是业界常用的关系型数据库,在平时开发中会经常与MySql数据库打交道,所以在接下来将介绍怎么使用Go语言操作MySql数据库. 下载MySql连接驱动 Go语言中的database/sql包提供了保证SQL或类SQL数据库的泛用接口,并不提供具体的数据库驱动.使用database/sql包时必须注入(至少)一个数据库驱动. 我们常用的数据库基本上都有完整的第三方实现.比如:MySQL驱动 **下载依赖** go get -u github.com/go-sql-driver/my

  • golang中连接mysql数据库

    golang中连接mysql数据库,需要使用一个第三方类库github.com/go-sql-driver/mysql,在这个类库中就实现了mysql的连接池,并且只需要设置两个参数就可以实现 一般连接mysql首先需要调用sql.Open函数,但是此时并没有真正的去连接mysql,而是只创建了一个Db的对象而已.当执行Query或者是Exec方法时,才会去真正的连接数据库. 默认情况下.每次执行sql语句,都会创建一条tcp连接,执行结束就会断掉连接,但是会保留两条连接闲置.当下次再执行 sq

  • Golang中如何对MySQL进行操作详解

    前言 Golang官方并没有提供数据库驱动,但通过database/sql/driver包来提供了实现驱动的标准接口.可以在Github上找到很多开源的驱动. 其中go-sql-driver/mysql是一个比较推荐的驱动,其完全支持database/sql接口. 使用这个驱动, 在项目里import进: import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) 在正式使用database/sql包之前

  • golang 通过ssh代理连接mysql的操作

    我就废话不多说了,大家还是直接看代码吧~ package main import ( "bytes" "context" "database/sql" "errors" "fmt" "github.com/go-sql-driver/mysql" "golang.org/x/crypto/ssh" "io" "io/ioutil"

  • golang操作连接数据库实现mysql事务示例

    目录 mysql驱动 posgre驱动 连接postgres 连接mysql 初始化连接 SetMaxOpenConns SetMaxIdleConns CRUD 查询 单行查询QueryRow 多行查询Query-rows 插入和更新和删除Exec 影响的行数 插入 更新 删除 MySQL预处理 为什么要预处理? Go实现MySQL预处理 SQL注入问题 Go实现MySQL事务 什么是事务? 事务的ACID 事务相关方法 事务示例 MySQL是业界常用的关系型数据库,本文介绍了database

  • python连接mysql并提交mysql事务示例

    复制代码 代码如下: # -*- coding: utf-8 -*-import sysimport MySQLdbreload(sys)sys.setdefaultencoding('utf-8')class DB(object): def __init__(self,host='127.0.0.1',port=3306,user='root',passwd='123',database=''):  self.__host=host  self.__port=port  self.__user

  • Mysql事务操作失败如何解决

    Mysql事务操作失败如何解决 事务的原子性 :事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做 . 要实现事务的原子性,单单靠一条commit或是rollback命令还是不行的,因为例如commit命令它只是将一个事务中执行成功的DML语句提交给数据库里.如果要实现事务的原子性,则就需要commit和rollback命令配合上程序上的一个业务逻辑才能可以,具体业务逻辑代码如下示例代码: 1.现象 程序中打开了事务进行插入,但是没有commit,表中的数据已经存在,就是回滚也不

  • PHP操作MySQL事务实例

    本文实例讲述了PHP操作MySQL事务的方法,分享给大家供大家参考.具体方法如下: 一般来说,事务都应该具备ACID特征.所谓ACID是Atomic(原子性),Consistent(一致性),Isolated(隔离性),Durable(持续性)四个词的首字母所写,下面以"银行转帐"为例来分别说明一下它们的含义: ① 原子性:组成事务处理的语句形成了一个逻辑单元,不能只执行其中的一部分.换句话说,事务是不可分割的最小单元.比如:银行转帐过程中,必须同时从一个帐户减去转帐金额,并加到另一个

  • golang如何优雅的编写事务代码示例

    前言 新手程序员大概有如下特点 if嵌套经常超过3层.经常出现重复代码.单个函数代码特别长. 只会crud,对语言特性和语言的边界不了解. 不懂面向对象原则和设计模式,以为copy代码就算学会了,常见的是代码职责不明确或者写出万能类 不知道数据结构和算法的重要性,以为靠硬件能解决运行慢的问题 架构不懂,搭建框架不会,搭建环境不会,使用的软件底层原理一问三不知 其实吧,很多人干了很多年,看似是老手,平时工作看似很忙,其实做的都是最简单的活. 这就像去锻炼,有的人每天练的很积极,准时打卡,频繁发朋友

  • mysql事务管理操作详解

    本文实例讲述了mysql事务管理操作.分享给大家供大家参考,具体如下: 本文内容: 什么是事务管理 事务管理操作 回滚点 默认的事务管理 首发日期:2018-04-18 什么是事务管理: 可以把一系列要执行的操作称为事务,而事务管理就是管理这些操作要么完全执行,要么完全不执行(很经典的一个例子是:A要给B转钱,首先A的钱减少了,但是突然的数据库断电了,导致无法给B加钱,然后由于丢失数据,B不承认收到A的钱:在这里事务就是确保加钱和减钱两个都完全执行或完全不执行,如果加钱失败,那么不会发生减钱).

  • golang操作rocketmq的示例代码

    下载 go get github.com/apache/rocketmq-client-go/v2 代码 func main() { // 1. 创建主题 //CreateTopic("test-04", 10909) // 2. 生产者向主题中发送消息 //SendSyncMessage("hello world0002") // 3. 消费者订阅主题并消费 SubscribeMessage() } func CreateTopic(topicName strin

  • MySQL事务的SavePoint简介及操作

    目录 什么是SavePoint 语法 重要操作 什么是SavePoint SavePoint是数据库事务中的一个概念, 可以将整个事务切割为不同的小事务, 可以选择将状态回滚到某个小事务发生时的样子, 语法 定义保存点的语法如下: SAVEPOINT 保存点的名称; 当我们想回滚到某个保存点时,可以使用下边的语句. ROLLBACK [WORK] TO 保存点的名称 删除保存点 RELEASE SAVEPOINT 保存点名称; 重要操作 开启事务: begin ....DML语句.... 设置保

  • golang连接redis库及基本操作示例过程

    目录 Redis介绍 Redis支持的数据结构 Redis应用场景 准备Redis环境 go-redis库 安装 连接 V8新版本相关 连接Redis哨兵模式 连接Redis集群 基本使用 HVals set/get示例 zset示例 根据前缀获取Key 执行自定义命令 按通配符删除key Pipeline 事务 Watch Redis介绍 Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上.除此之外,通过复制.持久化

随机推荐