聊聊gorm的IsolationLevel

1,383 阅读2分钟

本文主要研究一下gorm的IsolationLevel

IsolationLevel

/usr/local/go/src/database/sql/sql.go

// IsolationLevel is the transaction isolation level used in TxOptions.
type IsolationLevel int

// Various isolation levels that drivers may support in BeginTx.
// If a driver does not support a given isolation level an error may be returned.
//
// See https://en.wikipedia.org/wiki/Isolation_(database_systems)#Isolation_levels.
const (
	LevelDefault IsolationLevel = iota
	LevelReadUncommitted
	LevelReadCommitted
	LevelWriteCommitted
	LevelRepeatableRead
	LevelSnapshot
	LevelSerializable
	LevelLinearizable
)

// String returns the name of the transaction isolation level.
func (i IsolationLevel) String() string {
	switch i {
	case LevelDefault:
		return "Default"
	case LevelReadUncommitted:
		return "Read Uncommitted"
	case LevelReadCommitted:
		return "Read Committed"
	case LevelWriteCommitted:
		return "Write Committed"
	case LevelRepeatableRead:
		return "Repeatable Read"
	case LevelSnapshot:
		return "Snapshot"
	case LevelSerializable:
		return "Serializable"
	case LevelLinearizable:
		return "Linearizable"
	default:
		return "IsolationLevel(" + strconv.Itoa(int(i)) + ")"
	}
}

golang定义了IsolationLevel,分别为LevelDefault、LevelReadUncommitted、LevelReadCommitted、LevelWriteCommitted、LevelRepeatableRead、LevelSnapshot、LevelSerializable、LevelLinearizable

TxOptions

/usr/local/go/src/database/sql/sql.go

type TxOptions struct {
	// Isolation is the transaction isolation level.
	// If zero, the driver or database's default level is used.
	Isolation IsolationLevel
	ReadOnly  bool
}

sql.TxOptions定义了Isolation、ReadOnly属性

Transaction

gorm.io/gorm@v1.20.10/finisher_api.go

// Transaction start a transaction as a block, return error will rollback, otherwise to commit.
func (db *DB) Transaction(fc func(tx *DB) error, opts ...*sql.TxOptions) (err error) {
	panicked := true

	if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer != nil {
		// nested transaction
		if !db.DisableNestedTransaction {
			err = db.SavePoint(fmt.Sprintf("sp%p", fc)).Error
			defer func() {
				// Make sure to rollback when panic, Block error or Commit error
				if panicked || err != nil {
					db.RollbackTo(fmt.Sprintf("sp%p", fc))
				}
			}()
		}

		if err == nil {
			err = fc(db.Session(&Session{}))
		}
	} else {
		tx := db.Begin(opts...)

		defer func() {
			// Make sure to rollback when panic, Block error or Commit error
			if panicked || err != nil {
				tx.Rollback()
			}
		}()

		if err = tx.Error; err == nil {
			err = fc(tx)
		}

		if err == nil {
			err = tx.Commit().Error
		}
	}

	panicked = false
	return
}

gorm的Transaction方法提供了*sql.TxOptions参数,可以用于设置Isolation,它最后传递给具体的driver

BeginTx

github.com/go-sql-driver/mysql@v1.5.0/connection.go

// BeginTx implements driver.ConnBeginTx interface
func (mc *mysqlConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
	if err := mc.watchCancel(ctx); err != nil {
		return nil, err
	}
	defer mc.finish()

	if sql.IsolationLevel(opts.Isolation) != sql.LevelDefault {
		level, err := mapIsolationLevel(opts.Isolation)
		if err != nil {
			return nil, err
		}
		err = mc.exec("SET TRANSACTION ISOLATION LEVEL " + level)
		if err != nil {
			return nil, err
		}
	}

	return mc.begin(opts.ReadOnly)
}

BeginTx会判断若指定的隔离级别不是default则会执行mapIsolationLevel映射到mysql支持的隔离级别,然后执行SET TRANSACTION ISOLATION LEVEL来变更隔离级别

mapIsolationLevel

github.com/go-sql-driver/mysql@v1.5.0/utils.go

func mapIsolationLevel(level driver.IsolationLevel) (string, error) {
	switch sql.IsolationLevel(level) {
	case sql.LevelRepeatableRead:
		return "REPEATABLE READ", nil
	case sql.LevelReadCommitted:
		return "READ COMMITTED", nil
	case sql.LevelReadUncommitted:
		return "READ UNCOMMITTED", nil
	case sql.LevelSerializable:
		return "SERIALIZABLE", nil
	default:
		return "", fmt.Errorf("mysql: unsupported isolation level: %v", level)
	}
}

mapIsolationLevel映射golang sql定义的IsolationLevel到mysql支持的隔离级别

小结

golang定义了IsolationLevel,分别为LevelDefault、LevelReadUncommitted、LevelReadCommitted、LevelWriteCommitted、LevelRepeatableRead、LevelSnapshot、LevelSerializable、LevelLinearizable;gorm的Transaction方法提供了*sql.TxOptions参数,可以用于设置Isolation,它最后传递给具体的driver;BeginTx会判断若指定的隔离级别不是default则会执行mapIsolationLevel映射到mysql支持的隔离级别,然后执行SET TRANSACTION ISOLATION LEVEL来变更隔离级别。

doc