package main
import ( "fmt" "time"
"github.com/gomodule/redigo/redis" )
func initRedisPool() *redis.Pool { return &redis.Pool{ MaxIdle: 10, IdleTimeout: 240 * time.Second, Dial: func() (redis.Conn, error) { conn, err := redis.Dial("tcp", "127.0.0.1:6379") if err != nil { return nil, err } return conn, nil }, TestOnBorrow: func(c redis.Conn, t time.Time) error { _, err := c.Do("PING") return err }, } }
func acquireRedisLock(conn redis.Conn, lockKey string, lockValue string, expire time.Duration) bool { result, err := redis.String(conn.Do("SET", lockKey, lockValue, "NX", "EX", int(expire.Seconds()))) if err != nil || result != "OK" { return false } return true }
func releaseRedisLock(conn redis.Conn, lockKey string, lockValue string) error { script := ` if redis.call('GET', KEYS[1]) == ARGV[1] then return redis.call('DEL', KEYS[1]) else return 0 end ` _, err := redis.Int(conn.Do("EVAL", script, 1, lockKey, lockValue)) return err }
func deductStockWithRedisLock(redisPool *redis.Pool, db *sql.DB, goodsID int, num int) error { lockKey := fmt.Sprintf("stock_lock_%d", goodsID) lockValue := "unique_value_123" expire := 30 * time.Second
conn := redisPool.Get() defer conn.Close()
if !acquireRedisLock(conn, lockKey, lockValue, expire) { return fmt.Errorf("获取分布式锁失败,其他客户端正在操作") } defer releaseRedisLock(conn, lockKey, lockValue)
success, err := updateStockWithOptimisticLock(db, goodsID, num) if err != nil { return err } if !success { return fmt.Errorf("库存更新失败,版本号冲突") }
return nil }
func main() { redisPool := initRedisPool() defer redisPool.Close()
db, err := initDB() if err != nil { fmt.Printf("初始化数据库失败:%v\n", err) return } defer db.Close()
err = deductStockWithRedisLock(redisPool, db, 1, 1) if err != nil { fmt.Printf("扣减库存失败:%v\n", err) } else { fmt.Println("扣减库存成功") } }
|