事务操作
Redis 事务可以一次执行多个命令,保证原子性。事务中的命令要么全部执行,要么全部不执行。
Multi - 开始事务
vb
' 开始事务
oRedis.MultiExec - 提交事务
vb
' 提交事务并获取执行结果
Dim vResults As Variant
vResults = oRedis.Exec()
If IsArray(vResults) Then
Dim i As Long
For i = 0 To UBound(vResults)
Debug.Print vResults(i)
Next
End IfDiscard - 取消事务
vb
' 取消事务
oRedis.Discard基本使用
简单事务示例
vb
Sub SimpleTransaction()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then
Debug.Print "连接失败: " & oRedis.LastError
Exit Sub
End If
' 开始事务
oRedis.Multi
' 执行多个命令(不会被立即执行)
oRedis.Set_ "key1", "value1"
oRedis.Set_ "key2", "value2"
oRedis.Incr "counter"
' 提交事务
Dim vResults As Variant
vResults = oRedis.Exec()
Debug.Print "事务执行结果:"
If IsArray(vResults) Then
For i = 0 To UBound(vResults)
Debug.Print " 操作 " & (i + 1) & " 结果: " & vResults(i)
Next
End If
oRedis.DisConnect
End Sub应用场景
1. 原子性计数
vb
Sub AtomicCounter()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 初始化计数器
oRedis.Set_ "counter", "0"
Debug.Print "初始计数: " & oRedis.Get_("counter")
' 开始事务
oRedis.Multi
' 执行多个自增操作
oRedis.Incr "counter"
oRedis.Incr "counter"
oRedis.Incr "counter"
' 提交事务
Dim vResults As Variant
vResults = oRedis.Exec()
Debug.Print vbCrLf & "事务执行结果:"
If IsArray(vResults) Then
For i = 0 To UBound(vResults)
Debug.Print " 操作 " & (i + 1) & " 结果: " & vResults(i)
Next
End If
Debug.Print "最终计数: " & oRedis.Get_("counter")
oRedis.DisConnect
End Sub2. 银行转账
vb
Sub BankTransfer()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 初始化账户余额
oRedis.Set_ "account:A", "1000"
oRedis.Set_ "account:B", "500"
Debug.Print "转账前:"
Debug.Print " 账户A: " & oRedis.Get_("account:A")
Debug.Print " 账户B: " & oRedis.Get_("account:B")
Dim lAmount As Long
lAmount = 200
' 开始事务
oRedis.Multi
' 扣除 A 账户
oRedis.DecrBy "account:A", lAmount
' 增加 B 账户
oRedis.IncrBy "account:B", lAmount
' 提交事务
Dim vResults As Variant
vResults = oRedis.Exec()
Debug.Print vbCrLf & "转账后:"
Debug.Print " 账户A: " & oRedis.Get_("account:A")
Debug.Print " 账户B: " & oRedis.Get_("account:B")
oRedis.DisConnect
End Sub3. 批量更新
vb
Sub BatchUpdate()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 开始事务
oRedis.Multi
' 批量设置多个键
oRedis.Set_ "user:1", "张三"
oRedis.Set_ "user:2", "李四"
oRedis.Set_ "user:3", "王五"
oRedis.Set_ "user:4", "赵六"
oRedis.Set_ "user:5", "钱七"
' 批量设置过期时间
oRedis.Expire "user:1", 3600
oRedis.Expire "user:2", 3600
oRedis.Expire "user:3", 3600
oRedis.Expire "user:4", 3600
oRedis.Expire "user:5", 3600
' 提交事务
Dim vResults As Variant
vResults = oRedis.Exec()
Debug.Print "批量更新完成,共执行 " & (UBound(vResults) + 1) & " 个操作"
oRedis.DisConnect
End Sub4. 条件更新
vb
Sub ConditionalUpdate()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 初始化库存
oRedis.Set_ "product:1001:stock", "10"
Debug.Print "初始库存: " & oRedis.Get_("product:1001:stock")
Dim lBuyAmount As Long
lBuyAmount = 3
' 检查库存是否足够
Dim lCurrentStock As Long
lCurrentStock = CLng(oRedis.Get_("product:1001:stock"))
If lCurrentStock >= lBuyAmount Then
' 库存充足,开始事务
oRedis.Multi
' 减少库存
oRedis.DecrBy "product:1001:stock", lBuyAmount
' 增加销量
oRedis.IncrBy "product:1001:sold", lBuyAmount
' 记录销售日志
oRedis.RPush "sales:log", "售出 " & lBuyAmount & " 件商品"
' 提交事务
Dim vResults As Variant
vResults = oRedis.Exec()
Debug.Print vbCrLf & "购买成功!"
Debug.Print "剩余库存: " & oRedis.Get_("product:1001:stock")
Debug.Print "累计销量: " & oRedis.Get_("product:1001:sold")
Else
Debug.Print "库存不足,无法购买"
End If
oRedis.DisConnect
End Sub5. 数据一致性检查
vb
Sub DataConsistency()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 初始化数据
oRedis.HSet "order:1001", "amount", "100"
oRedis.HSet "order:1001", "status", "pending"
oRedis.HSet "order:1001", "user", "user:123"
oRedis.HSet "user:123", "balance", "500"
' 开始事务
oRedis.Multi
' 获取订单金额
oRedis.HGet "order:1001", "amount"
' 获取用户余额
oRedis.HGet "user:123", "balance"
' 执行事务获取数据
Dim vResults As Variant
vResults = oRedis.Exec()
' 检查余额是否足够
If IsArray(vResults) And UBound(vResults) >= 1 Then
Dim lOrderAmount As Long
Dim lUserBalance As Long
lOrderAmount = CLng(vResults(0))
lUserBalance = CLng(vResults(1))
If lUserBalance >= lOrderAmount Then
' 余额充足,执行支付
oRedis.Multi
' 扣除用户余额
oRedis.HSet "user:123", "balance", CStr(lUserBalance - lOrderAmount)
' 更新订单状态
oRedis.HSet "order:1001", "status", "paid"
' 记录支付日志
oRedis.RPush "payment:log", "订单 1001 支付 " & lOrderAmount
' 提交事务
vResults = oRedis.Exec()
Debug.Print "支付成功"
Debug.Print "剩余余额: " & oRedis.HGet("user:123", "balance")
Debug.Print "订单状态: " & oRedis.HGet("order:1001", "status")
Else
Debug.Print "余额不足,支付失败"
End If
End If
oRedis.DisConnect
End Sub6. 事务取消
vb
Sub CancelTransaction()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 初始化数据
oRedis.Set_ "counter", "10"
Debug.Print "初始值: " & oRedis.Get_("counter")
' 开始事务
oRedis.Multi
' 执行多个操作
oRedis.Incr "counter"
oRedis.Incr "counter"
oRedis.Incr "counter"
' 取消事务
oRedis.Discard
Debug.Print "事务取消后的值: " & oRedis.Get_("counter")
oRedis.DisConnect
End Sub事务特性
1. 原子性
事务中的命令要么全部执行,要么全部不执行:
vb
Sub AtomicityExample()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
oRedis.Multi
' 这些命令要么全部成功,要么全部失败
oRedis.Set_ "key1", "value1"
oRedis.Set_ "key2", "value2"
oRedis.Set_ "key3", "value3"
oRedis.Exec
oRedis.DisConnect
End Sub2. 隔离性
事务执行期间,其他客户端看不到中间状态:
vb
Sub IsolationExample()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then Exit Sub
' 设置初始值
oRedis.Set_ "balance", "100"
' 开始事务
oRedis.Multi
' 减少余额(此时其他客户端看到的仍然是 100)
oRedis.Decr "balance"
' 在这里可以做一些其他操作...
' 提交事务(此时其他客户端才能看到变化)
oRedis.Exec
oRedis.DisConnect
End Sub注意事项
- 事务不回滚:Redis 事务不支持回滚,即使某个命令失败,其他命令仍会执行
- 错误处理:需要在执行事务前验证参数
- 性能考虑:事务会阻塞其他操作,避免长时间运行的事务
- 嵌套事务:Redis 不支持嵌套事务
- 乐观锁:可以使用
WATCH命令实现乐观锁(需要扩展实现)
完整示例
vb
Sub Example_Transaction()
Dim oRedis As New cRedisClient
If Not oRedis.Connect() Then
Debug.Print "连接失败: " & oRedis.LastError
Exit Sub
End If
' 初始化计数器
oRedis.Set_ "counter", "0"
Debug.Print "初始计数: " & oRedis.Get_("counter")
' 开始事务
oRedis.Multi
' 执行多个自增操作
oRedis.Incr "counter"
oRedis.Incr "counter"
oRedis.Incr "counter"
' 提交事务
Dim vResults As Variant
vResults = oRedis.Exec()
Debug.Print "事务执行结果:"
If IsArray(vResults) Then
For i = 0 To UBound(vResults)
Debug.Print " 操作 " & (i + 1) & " 结果: " & vResults(i)
Next
End If
Debug.Print "最终计数: " & oRedis.Get_("counter")
oRedis.DisConnect
End Sub事务 vs 单条命令
| 场景 | 推荐方式 | 原因 |
|---|---|---|
| 单个操作 | 单条命令 | 简单直接 |
| 多个相关操作 | 事务 | 保证原子性 |
| 不相关的操作 | 单条命令 | 减少阻塞 |
| 需要中间结果 | 分步执行 | 更灵活 |
| 批量更新 | 事务或批量命令 | 提高性能 |