面试题答案
一键面试常见分布式事务模式
- TCC(Try - Confirm - Cancel)
- 原理:将事务处理过程分为三个阶段。Try阶段主要是对业务资源进行检测和预留;Confirm阶段在Try成功后,真正执行提交操作;Cancel阶段在Try成功但Confirm失败时,对Try阶段预留的资源进行释放。
- 优点:性能较好,因为Try阶段只是预留资源,而非真正执行事务,并且能快速响应。适用于对一致性要求较高且并发量较大的场景。
- 缺点:开发成本高,每个服务都需要实现Try、Confirm和Cancel三个接口。对业务侵入性强,需要业务代码紧密配合。而且如果Confirm或Cancel操作失败,可能需要人工干预。
- Saga模式
- 原理:将一个长事务分解为多个本地短事务,每个短事务都有对应的补偿事务。当某个步骤失败时,按照相反顺序调用补偿事务来撤销已执行的操作。
- 优点:对业务侵入性相对较小,只需要为每个本地事务编写补偿逻辑。适合业务流程长且复杂的场景,能保证最终一致性。
- 缺点:执行时间较长,因为是顺序执行各个本地事务。在某些情况下,如果补偿事务执行失败,可能导致数据不一致问题更难处理。
在Go语言中运用Saga模式实现跨服务事务管理
- 定义本地事务和补偿事务
// 定义一个简单的本地事务操作
type LocalTransaction struct {
Name string
Do func() error
Undo func() error
}
- 执行Saga流程
func ExecuteSaga(transactions []LocalTransaction) error {
for i, tx := range transactions {
err := tx.Do()
if err != nil {
// 如果某个事务失败,执行已执行事务的补偿事务
for j := i - 1; j >= 0; j-- {
undoErr := transactions[j].Undo()
if undoErr != nil {
// 记录补偿事务执行失败的错误
// 这里可以考虑采用更复杂的错误处理机制,如重试等
return undoErr
}
}
return err
}
}
return nil
}
- 示例使用
func main() {
// 模拟两个服务的本地事务
service1Tx := LocalTransaction{
Name: "Service1Tx",
Do: func() error {
// 执行Service1的业务逻辑,比如数据库插入操作
// 这里简化为打印日志
fmt.Println("Service1 Do operation")
return nil
},
Undo: func() error {
// 执行Service1的补偿逻辑,比如数据库删除操作
// 这里简化为打印日志
fmt.Println("Service1 Undo operation")
return nil
},
}
service2Tx := LocalTransaction{
Name: "Service2Tx",
Do: func() error {
// 执行Service2的业务逻辑,比如数据库插入操作
// 这里简化为打印日志
fmt.Println("Service2 Do operation")
return nil
},
Undo: func() error {
// 执行Service2的补偿逻辑,比如数据库删除操作
// 这里简化为打印日志
fmt.Println("Service2 Undo operation")
return nil
},
}
sagaTransactions := []LocalTransaction{service1Tx, service2Tx}
err := ExecuteSaga(sagaTransactions)
if err != nil {
fmt.Printf("Saga execution failed: %v\n", err)
} else {
fmt.Println("Saga execution successful")
}
}
在实际应用中,本地事务的Do
和Undo
方法会涉及与不同数据库或持久化存储的交互,并且可能需要考虑网络通信、重试机制等问题以确保事务的可靠性和一致性。