面试题答案
一键面试设计思路
- 本地锁的使用场景:在本地应用程序层面,读写互斥锁(
sync.RWMutex
)主要用于保护对本地数据结构(如内存中的缓存副本)的读写操作。读操作可以并发进行,但写操作需要独占锁,以确保数据一致性。 - 分布式锁的使用场景:对于分布式缓存(如Redis),需要使用分布式锁来协调不同微服务实例之间对共享数据的访问。当一个实例需要对缓存中的数据进行写操作时,它必须先获取分布式锁。
- 协同工作方式:本地锁和分布式锁需要协同工作,以避免数据一致性问题。在读操作时,首先尝试从本地缓存获取数据。如果数据不存在,则获取分布式读锁(可以使用Redis的SETNX等操作模拟),从分布式缓存获取数据,并更新本地缓存。在写操作时,先获取分布式写锁,更新分布式缓存,然后释放锁,同时更新本地缓存。
关键技术点
- 分布式锁实现:使用Redis的原子操作(如SETNX命令)来实现分布式锁。确保锁的获取和释放是原子性的,以避免竞态条件。
- 锁的超时处理:设置合理的锁超时时间,防止因某个实例异常导致锁永远无法释放。
- 缓存更新策略:在更新分布式缓存后,及时更新本地缓存,以保持数据一致性。
关键代码片段示例
package main
import (
"context"
"fmt"
"github.com/go-redis/redis/v8"
"sync"
"time"
)
var (
rdb *redis.Client
ctx = context.Background()
mu sync.RWMutex
)
// 获取分布式锁
func acquireDistributedLock(lockKey string, lockValue string, expiration time.Duration) bool {
success, err := rdb.SetNX(ctx, lockKey, lockValue, expiration).Result()
if err != nil {
fmt.Println("Failed to acquire distributed lock:", err)
return false
}
return success
}
// 释放分布式锁
func releaseDistributedLock(lockKey string, lockValue string) {
_, err := rdb.Del(ctx, lockKey).Result()
if err != nil {
fmt.Println("Failed to release distributed lock:", err)
}
}
func readData(key string) string {
mu.RLock()
if data, ok := localCache[key]; ok {
mu.RUnlock()
return data
}
mu.RUnlock()
// 获取分布式读锁
lockKey := "read:" + key
lockValue := fmt.Sprintf("%d", time.Now().UnixNano())
if acquireDistributedLock(lockKey, lockValue, 5*time.Second) {
defer releaseDistributedLock(lockKey, lockValue)
data, err := rdb.Get(ctx, key).Result()
if err != nil {
fmt.Println("Failed to read from Redis:", err)
return ""
}
mu.Lock()
localCache[key] = data
mu.Unlock()
return data
}
return ""
}
func writeData(key, value string) {
// 获取分布式写锁
lockKey := "write:" + key
lockValue := fmt.Sprintf("%d", time.Now().UnixNano())
if acquireDistributedLock(lockKey, lockValue, 5*time.Second) {
defer releaseDistributedLock(lockKey, lockValue)
err := rdb.Set(ctx, key, value, 0).Err()
if err != nil {
fmt.Println("Failed to write to Redis:", err)
return
}
mu.Lock()
localCache[key] = value
mu.Unlock()
}
}
var localCache = make(map[string]string)
func main() {
rdb = redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "",
DB: 0,
})
writeData("testKey", "testValue")
fmt.Println("Read data:", readData("testKey"))
}
以上代码展示了如何在Go中通过本地读写互斥锁和基于Redis的分布式锁协同工作,以实现数据一致性和高性能的数据访问。