面试题答案
一键面试设计思路
- 选择Go语言同步机制:
- 读多写少场景:如果是读多写少的情况,优先考虑
sync.RWMutex
。读操作使用RLock
和RUnlock
,写操作使用Lock
和Unlock
。示例伪代码如下:
- 读多写少场景:如果是读多写少的情况,优先考虑
var mu sync.RWMutex
var dataMap map[keyType]valueType
func read(key keyType) valueType {
mu.RLock()
defer mu.RUnlock()
return dataMap[key]
}
func write(key keyType, value valueType) {
mu.Lock()
defer mu.Unlock()
dataMap[key] = value
}
- **读写均衡或写多场景**:对于读写较为均衡或者写操作较多的场景,`sync.Map` 是个不错的选择。`sync.Map` 内部实现了更细粒度的锁机制,适合高并发读写。示例伪代码如下:
var dataMap sync.Map
func read(key interface{}) (interface{}, bool) {
return dataMap.Load(key)
}
func write(key interface{}, value interface{}) {
dataMap.Store(key, value)
}
- 节点间数据同步:
- 使用分布式一致性协议:如 Raft 或 Paxos。以 Raft 为例,每个节点有一个状态机,通过日志复制保证数据一致性。
- 流程图:
graph TD;
A[客户端发起写请求] --> B[Leader节点接收请求];
B --> C[Leader将日志条目追加到本地日志];
C --> D[Leader向Follower发送AppendEntries RPC];
D --> E[Follower将日志条目追加到本地日志并回复ACK];
E --> F[Leader收到大多数Follower的ACK];
F --> G[Leader将日志条目应用到状态机];
G --> H[Leader向客户端回复成功];
- 故障恢复:
- 节点故障检测:使用心跳机制,节点间定期发送心跳包。如果某个节点在一定时间内没有收到来自Leader的心跳,就认为Leader故障,触发选举。
- 重新选举:在 Raft 中,Follower 转为 Candidate 发起选举,收到大多数节点投票后成为新的 Leader。新 Leader 会继续处理日志复制和数据同步。示例伪代码如下:
// 节点结构体
type Node struct {
state string
leaderID int
// 其他字段...
}
func (n *Node) heartBeat() {
for {
if n.state == "Follower" {
// 尝试发送心跳给Leader
if!sendHeartbeat(n.leaderID) {
n.state = "Candidate"
// 发起选举
if winElection() {
n.state = "Leader"
}
}
}
time.Sleep(heartbeatInterval)
}
}
通过上述设计,可以构建一个在分布式环境下高并发读写且保证数据一致性和可靠性的并发安全Map。