亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

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

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

業(yè)務場景

因為項目剛上線,目前暫不打算引入其他中間件,所以打算通過 mysql 來實現(xiàn)分布式讀寫鎖;而該業(yè)務場景也滿足分布式讀寫鎖的場景,抽象后的業(yè)務場景是:特定資源 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è)務場景的加鎖會更加靈活,其中分布式讀寫鎖也遵循讀寫鎖的原則:

  • 讀模式共享,寫模式互斥。
  • 它三種模式狀態(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 服務

通過 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)較為簡單,因此也有一定的應用場景,

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

相關(guān)文章

  • Go語言判斷文件或文件夾是否存在的方法

    Go語言判斷文件或文件夾是否存在的方法

    這篇文章主要介紹了Go語言判斷文件或文件夾是否存在的方法,結(jié)合具體實例形式對比分析了Go語言針對文件與目錄判斷的操作技巧與相關(guān)注意事項,需要的朋友可以參考下
    2017-05-05
  • 利用GoLang?Fiber進行高性能Web開發(fā)實例詳解

    利用GoLang?Fiber進行高性能Web開發(fā)實例詳解

    這篇文章主要為大家介紹了利用GoLang?Fiber進行高性能Web開發(fā)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2024-01-01
  • 淺析Go常量為什么只支持基本數(shù)據(jù)類型

    淺析Go常量為什么只支持基本數(shù)據(jù)類型

    這篇文章主要來和大家一起討論一下Golang中常量為什么只支持基本數(shù)據(jù)類型,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-09-09
  • go語言反射的基礎(chǔ)教程示例

    go語言反射的基礎(chǔ)教程示例

    這篇文章主要為大家介紹了go語言反射的基礎(chǔ)教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • 解決Golang中g(shù)oroutine執(zhí)行速度的問題

    解決Golang中g(shù)oroutine執(zhí)行速度的問題

    這篇文章主要介紹了解決Golang中g(shù)oroutine執(zhí)行速度的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • Golang應用程序性能優(yōu)化技巧分享

    Golang應用程序性能優(yōu)化技巧分享

    隨著科技的進步,人人都想要快速的應用,這就需要優(yōu)化您的應用程序性能。本文為大家整理了一些Golang應用程序性能優(yōu)化的技巧,希望對大家有所幫助
    2023-04-04
  • 通過案例詳細聊聊Go語言的變量與常量

    通過案例詳細聊聊Go語言的變量與常量

    在任何一門現(xiàn)代的高級語言中,變量和常量都是它非?;A(chǔ)的程序結(jié)構(gòu)的組成部分,下面這篇文章主要給大家介紹了關(guān)于如何通過案例詳細聊聊Go語言的變量與常量的相關(guān)資料,需要的朋友可以參考下
    2023-03-03
  • Go中JSON解析時tag的使用

    Go中JSON解析時tag的使用

    本文主要介紹了Go中JSON解析時tag的使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Go?實現(xiàn)?WebSockets和什么是?WebSockets

    Go?實現(xiàn)?WebSockets和什么是?WebSockets

    這篇文章主要介紹了Go?實現(xiàn)?WebSockets和什么是?WebSockets,WebSockets?是構(gòu)建實時應用程序的第一大解決方案,在線游戲、即時通訊、跟蹤應用程序等,下文相關(guān)內(nèi)容介紹需要的小伙伴可以參考一下
    2022-04-04
  • 關(guān)于golang 字符串 int uint int64 uint64 互轉(zhuǎn)問題

    關(guān)于golang 字符串 int uint int64 uint64&

    這篇文章主要介紹了golang 字符串 int uint int64 uint64 互轉(zhuǎn),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-01-01

最新評論