Skip to content

事务操作

Redis 事务可以一次执行多个命令,保证原子性。事务中的命令要么全部执行,要么全部不执行。

Multi - 开始事务

vb
' 开始事务
oRedis.Multi

Exec - 提交事务

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 If

Discard - 取消事务

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 Sub

2. 银行转账

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 Sub

3. 批量更新

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 Sub

4. 条件更新

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 Sub

5. 数据一致性检查

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 Sub

6. 事务取消

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 Sub

2. 隔离性

事务执行期间,其他客户端看不到中间状态:

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

注意事项

  1. 事务不回滚:Redis 事务不支持回滚,即使某个命令失败,其他命令仍会执行
  2. 错误处理:需要在执行事务前验证参数
  3. 性能考虑:事务会阻塞其他操作,避免长时间运行的事务
  4. 嵌套事务:Redis 不支持嵌套事务
  5. 乐观锁:可以使用 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 单条命令

场景推荐方式原因
单个操作单条命令简单直接
多个相关操作事务保证原子性
不相关的操作单条命令减少阻塞
需要中间结果分步执行更灵活
批量更新事务或批量命令提高性能

VB6及其LOGO版权为微软公司所有