最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

golang?基于?mysql?簡單實現(xiàn)分布式讀寫鎖

 更新時間:2022年09月26日 11:13:23   作者:二牛QAQ  
這篇文章主要介紹了golang?基于mysql簡單實現(xiàn)分布式讀寫鎖,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

業(yè)務(wù)場景

因為項目剛上線,目前暫不打算引入其他中間件,所以打算通過 mysql 來實現(xiàn)分布式讀寫鎖;而該業(yè)務(wù)場景也滿足分布式讀寫鎖的場景,抽象后的業(yè)務(wù)場景是:特定資源 X,可以執(zhí)行 2 種操作:讀操作和寫操作,2種操作需要滿足下面條件:

  • 執(zhí)行操作的機器分布式在不同的節(jié)點中,也就是分布式的;
  • 讀操作是共享的,也就是說同時可以有多個 goroutine 對資源 X 執(zhí)行讀操作;
  • 寫操作是互斥的,也就是說同一時刻只允許有一個 goroutine 對資源 X 執(zhí)行寫操作;
  • 讀操作和寫操作是互斥的,也就是說寫操作和讀操作不能同時存在

既然需要如此實現(xiàn),下面我們看下什么是分布式讀寫鎖。

什么是分布式讀寫鎖

大家對于鎖肯定不陌生,在 golang 中 sync.Mutex 鎖是常見的,一般用在單節(jié)點多 goroutine 中對資源的并發(fā)訪問;但是分布式場景下,單節(jié)點 sync.Mutex 加鎖的方式就會失去作用,于是人們?yōu)榱嗽诜植际江h(huán)境中實現(xiàn)對共享資源的互斥訪問,實現(xiàn)了各種分布式鎖。

而分布式讀寫鎖是比分布式鎖粒度更小的鎖,對業(yè)務(wù)場景的加鎖會更加靈活,其中分布式讀寫鎖也遵循讀寫鎖的原則:

  • 讀模式共享,寫模式互斥。
  • 它三種模式狀態(tài): 讀加鎖狀態(tài)、寫加鎖狀態(tài)、無鎖狀態(tài)。

分布式讀寫鎖的訪問原則與讀寫鎖類似,下面我們具體看下。

分布式讀寫鎖的訪問原則

以下列表為讀寫鎖(也就是分布式讀寫鎖)的讀寫訪問原則

當前鎖狀態(tài)讀鎖請求寫鎖請求
無鎖狀態(tài)可以可以
讀鎖狀態(tài)可以不可以
寫鎖狀態(tài)不可以不可以

讀鎖

  • 只有在無鎖和讀鎖下可以獲取讀鎖。
  • 讀鎖的模式下,任何請求讀鎖都可以。
  • 讀鎖的模式下, 請求寫鎖不可以,直到所有讀鎖解鎖,寫鎖才能獲取到鎖。

寫鎖

  • 只有在無鎖狀態(tài)下可以獲取寫鎖。
  • 寫鎖的模式下,任何請求讀鎖和寫鎖都阻塞,直到寫鎖解鎖。

具體實現(xiàn)

如果本地沒有 mysql 數(shù)據(jù)庫,可以通過這篇文章快速搭建: 如何使用 docker 搭建一個 mysql 服務(wù)

通過 gorm 連接 mysql

gorm 是一個 golang 的 orm 框架,可以使用它快速連接數(shù)據(jù)庫,具體代碼如下:

package main

import (
	"fmt"

	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)
var (
	db *gorm.DB

	dbUsername = "kele"
	dbPassword = "baishi2020"
	dbHost     = "127.0.0.1:7306"
	dbDatabase = "lingmo"

	stateReadLock  = "ReadLock"
	stateWriteLock = "WriteLock"
	stateUnlock    = "Unlock"
)

type RWLock struct {
	LockMark      string `gorm:"default:'Unlock'"`
	ReadLockCount uint32 `gorm:"default:0"`
	LockReason    string
}

type Stock struct {
	gorm.Model
	RWLock
	Count int64
}

func (Stock) TableName() string {
	return "stocks"
}

func init() {
	dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", dbUsername, dbPassword, dbHost, dbDatabase)

	mysqlConfig := mysql.Config{DSN: dsn}
	gormConfig := &gorm.Config{Logger: logger.Default.LogMode(logger.Info)}

	var err error
	if db, err = gorm.Open(mysql.New(mysqlConfig), gormConfig); err != nil {
		panic(err)
	}

	db.Set("db:table_options", "ENGINE = InnoDB DEFAULT CHARSET = utf8")

	// register tables
	if err = db.AutoMigrate(&Stock{}); err != nil {
		panic(err)
	}
}

func main() {
	if result := db.Model(&Stock{}).Save(&Stock{Model: gorm.Model{}, RWLock: RWLock{}, Count: 10}); result.Error != nil {
		panic(result.Error)
	}
}

首先我們定義了一個庫存表 stocks,并且在其中添加三個和讀寫鎖相關(guān)的字段,三個字段的含義如下:

  • LockMark: 表示某條數(shù)據(jù)加鎖的狀態(tài),只能是讀鎖、寫鎖、無鎖狀態(tài)中的一種。
  • ReadLockCount: 首先讀模式是共享的,意味著可以有多個 goroutine 并發(fā)訪問,而 ReadLockCount 字段則記錄當前并發(fā)訪問的 goroutine 數(shù)量。
  • LockReason: 記錄當前加鎖的原因;讀鎖是最新的 goroutine 的 lockReason,寫鎖則是寫鎖 goroutine 的 lockReason。

其余則是一些 gorm 連接 mysql 邏輯,這里不再多贅述。

實現(xiàn)讀鎖模式

具體代碼如下:

func (s Stock) RLock(db *gorm.DB, lockReason string) error {
	condition := "(id = ?) AND (lock_mark != ?)"
	fields := map[string]interface{}{
		"lock_mark":       stateReadLock,
		"read_lock_count": gorm.Expr("read_lock_count + ?", 1),
		"lock_reason":     lockReason,
	}

	result := db.Model(&Stock{}).Where(condition, s.ID, stateWriteLock).Updates(fields)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return errors.New("failed to rlock Stock, RowsAffected=0")
	}

	return nil
}

func (s Stock) RUnlock(db *gorm.DB, UnLockReason string) error {
	sql := fmt.Sprintf(`UPDATE stocks SET read_lock_count=if(read_lock_count>0,read_lock_count-1,0), lock_mark=if(read_lock_count<1, 'Unlock', 'ReadLock'),lock_reason ='%s' where id= %d and lock_mark='%s'`, UnLockReason, s.ID, stateReadLock)
	result := db.Exec(sql)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return errors.New("failed to RUnlock Stock, RowsAffected=0")
	}

	return nil
}

func main() {
	if result := db.Model(&Stock{}).Save(&Stock{Model: gorm.Model{}, RWLock: RWLock{}, Count: 10}); result.Error != nil {
		panic(result.Error)
	}

	s := &Stock{Model: gorm.Model{ID: 1}}
	if result := db.Model(s).First(s); result.Error != nil {
		panic(result.Error)
	}
	if err := s.RLock(db, "readLock_reason_1"); err != nil {
		panic(err)
	}
	if err := s.RLock(db, "readLock_reason_2"); err != nil {
		panic(err)
	}

	if err := s.RUnlock(db, "readLock_unlock_1"); err != nil {
		panic(err)
	}
	if err := s.RUnlock(db, "readLock_unlock_2"); err != nil {
		panic(err)
	}
}

執(zhí)行以上代碼是可以正常運行的, 下面我們分析下:

  • 讀鎖的 sql 語句如下,只要在非寫鎖狀態(tài)下就能加讀鎖。
UPDATE `stocks` SET `lock_mark` = 'ReadLock', `lock_reason` = 'readLock_reason_1', `read_lock_count` = read_lock_count + 1, `updated_at` = '2022-09-25 14:58:45.693' WHERE (( id = 1 ) 
AND ( lock_mark != 'WriteLock' )) 
AND `stocks`.`deleted_at` IS NULL
  • 解讀鎖的 sql 語句如下,只有在讀鎖狀態(tài)下才能解讀鎖,另外還要更新 read_lock_count 和 lock_reason 字段。
UPDATE stocks 
SET read_lock_count =
IF
    ( read_lock_count > 0, read_lock_count - 1, 0 ),
    lock_mark =
IF
    ( read_lock_count < 1, 'Unlock', 'ReadLock' ),
    lock_reason = 'readLock_unlock_1' 
WHERE
    id = 1 
    AND lock_mark = 'ReadLock'

實現(xiàn)寫鎖模式

具體代碼如下:

func (s Stock) WLock(db *gorm.DB, lockReason string) error {
	condition := "(id = ?) AND (lock_mark = ?)"
	fields := map[string]interface{}{
		"lock_mark":       stateWriteLock,
		"read_lock_count": 0,
		"lock_reason":     lockReason,
	}
	result := db.Model(&Stock{}).Where(condition, s.ID, stateUnlock).Updates(fields)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return errors.New("failed to WLock Stock, RowsAffected=0")
	}

	return nil
}

func (s Stock) WUnlock(db *gorm.DB, UnLockReason string) error {
	condition := "(id = ?) AND (lock_mark = ?)"
	fields := map[string]interface{}{
		"lock_mark":       stateUnlock,
		"read_lock_count": 0,
		"lock_reason":     UnLockReason,
	}

	result := db.Model(&Stock{}).Where(condition, s.ID, stateWriteLock).Updates(fields)
	if result.Error != nil {
		return result.Error
	}
	if result.RowsAffected == 0 {
		return errors.New("failed to WUnlock Stock, RowsAffected=0")
	}

	return nil
}

func main() {
	s := &Stock{Model: gorm.Model{ID: 1}}
	if result := db.Model(s).First(s); result.Error != nil {
		panic(result.Error)
	}
	if err := s.WLock(db, "writeLock_reason_1"); err != nil {
		panic(err)
	}
	if err := s.WUnlock(db, "unWriteLock_reason_1"); err != nil {
		panic(err)
	}
}

執(zhí)行以上代碼也是可以運行,下面是分析結(jié)果

  • 寫鎖的 sql 語句如下,只有在無鎖狀態(tài)下才能加鎖成功
UPDATE `stocks` SET `lock_mark` = 'WriteLock', `lock_reason` = 'writeLock_reason_1', `read_lock_count` = 0, `updated_at` = '2022-09-25 15:06:10.71' WHERE (( id = 1 ) 
AND ( lock_mark = 'Unlock' )) 
AND `stocks`.`deleted_at` IS NULL
  • 解寫鎖的 sql 語句如下,只有在寫鎖狀態(tài)下才能解寫鎖
UPDATE `stocks` SET `lock_mark` = 'Unlock', `lock_reason` = 'unWriteLock_reason_1', `read_lock_count` = 0, `updated_at` = '2022-09-25 15:06:10.719' WHERE (( id = 1 ) 
AND ( lock_mark = 'WriteLock' )) 
AND `stocks`.`deleted_at` IS NULL

總結(jié)

分布式讀寫鎖的實現(xiàn)有多種方式,也可以通過 etcd、redisson 的方式進行實現(xiàn),而本文著重說明可通過 mysql 來實現(xiàn),這種方式的優(yōu)勢在于不必引入額外的組件且實現(xiàn)較為簡單,因此也有一定的應(yīng)用場景,

到此這篇關(guān)于golang 基于 mysql 簡單實現(xiàn)分布式讀寫鎖的文章就介紹到這了,更多相關(guān)golang 讀寫鎖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang的空標識符理解

    golang的空標識符理解

    今天小編就為大家分享一篇關(guān)于golang的空標識符理解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • Go素數(shù)篩選分析詳解

    Go素數(shù)篩選分析詳解

    學(xué)習(xí)Go語言的過程中,遇到素數(shù)篩選的問題。這是一個經(jīng)典的并發(fā)編程問題,是某大佬的代碼,短短幾行代碼就實現(xiàn)了素數(shù)篩選,這篇文章主要介紹了Go素數(shù)篩選分析,需要的朋友可以參考下
    2022-10-10
  • Go語言小白入門刷題打印輸出沙漏

    Go語言小白入門刷題打印輸出沙漏

    這篇文章主要介紹了Go語言刷題打印輸出沙漏的示例過程詳解,非常適合剛?cè)腴TGo語言的小白學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11
  • Golang?PHP?數(shù)據(jù)綁定示例分析

    Golang?PHP?數(shù)據(jù)綁定示例分析

    這篇文章主要為大家介紹了Golang?PHP?數(shù)據(jù)綁定示例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Go語言實現(xiàn)聊天小工具的示例代碼

    Go語言實現(xiàn)聊天小工具的示例代碼

    這篇文章主要為大家詳細介紹了如何利用Go語言實現(xiàn)聊天小工具,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 深入理解Go Gin框架中間件的實現(xiàn)原理

    深入理解Go Gin框架中間件的實現(xiàn)原理

    在Go Gin框架中,中間件是一種在請求處理過程中插入的功能模塊,它可以用于處理請求的前置和后置邏輯,例如認證、日志記錄、錯誤處理等,本文將給大家介紹一下Go Gin框架中間件的實現(xiàn)原理,需要的朋友可以參考下
    2023-09-09
  • Gin與Mysql實現(xiàn)簡單Restful風格API實戰(zhàn)示例詳解

    Gin與Mysql實現(xiàn)簡單Restful風格API實戰(zhàn)示例詳解

    這篇文章主要為大家介紹了Gin與Mysql實現(xiàn)簡單Restful風格API示例詳解,有需要的朋友可以借鑒參考下希望能夠有所幫助,祝大家多多進步
    2021-11-11
  • golang log4go的日志輸出優(yōu)化詳解

    golang log4go的日志輸出優(yōu)化詳解

    log4go源于google的一項log工程,但官方已經(jīng)停止維護更新,下面這篇文章主要給大家介紹了關(guān)于golang log4go的日志輸出優(yōu)化的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-12-12
  • golang之資源釋放/異常錯誤處理解析

    golang之資源釋放/異常錯誤處理解析

    這篇文章主要為大家介紹了golang之資源釋放/異常錯誤處理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • golang實現(xiàn)簡單rpc調(diào)用過程解析

    golang實現(xiàn)簡單rpc調(diào)用過程解析

    這篇文章主要介紹了golang實現(xiàn)簡單rpc調(diào)用,包括RPC具體實現(xiàn)結(jié)合實例代碼給大家講解的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-05-05

最新評論

法库县| 上蔡县| 获嘉县| 邵阳县| 和平区| 仙桃市| 葫芦岛市| 庆阳市| 齐齐哈尔市| 武城县| 阳春市| 丰城市| 泗洪县| 阿勒泰市| 大埔区| 都匀市| 靖边县| 仙游县| 耒阳市| 丽水市| 五莲县| 江永县| 仁布县| 七台河市| 正宁县| 会理县| 措勤县| 车险| 饶阳县| 阳泉市| 始兴县| 赣州市| 博爱县| 南投县| 乌鲁木齐市| 平武县| 和静县| 青岛市| 修文县| 吉水县| 名山县|