Skip to content

Sorted Set 操作

Redis Sorted Set 是有序集合,每个成员关联一个分数(score)。集合根据分数进行排序。

ZAdd - 添加有序集合成员

添加带分数的成员

vb
oRedis.ZAdd "mysortedset", 100, "member1"
oRedis.ZAdd "mysortedset", 200, "member2"
oRedis.ZAdd "mysortedset", 150, "member3"

注意:

  • 分数可以是整数或浮点数
  • 相同分数的成员按字典序排序
  • 如果成员已存在,会更新其分数

ZRange - 获取范围内的成员

获取所有成员(按分数升序)

vb
Dim vMembers As Variant
vMembers = oRedis.ZRange("mysortedset", 0, -1)

If IsArray(vMembers) Then
    Dim i As Long
    For i = 0 To UBound(vMembers)
        Debug.Print vMembers(i)
    Next
End If

获取带分数的成员

vb
vMembers = oRedis.ZRange("mysortedset", 0, -1, True)

If IsArray(vMembers) Then
    For i = 0 To UBound(vMembers) Step 2
        If i + 1 <= UBound(vMembers) Then
            Debug.Print vMembers(i) & ": " & vMembers(i + 1)
        End If
    Next
End If

获取前 N 名

vb
' 获取前 3 名(分数最低的 3 个)
vMembers = oRedis.ZRange("mysortedset", 0, 2)

获取最后 N 名

vb
' 获取最后 3 名(分数最高的 3 个)
vMembers = oRedis.ZRange("mysortedset", -3, -1)

ZRem - 删除有序集合成员

vb
' 删除单个成员
oRedis.ZRem "mysortedset", "member1"

' 删除多个成员
oRedis.ZRem "mysortedset", "member2", "member3"

ZCard - 获取有序集合成员数量

vb
Dim lCount As Long
lCount = oRedis.ZCard("mysortedset")
Debug.Print "有序集合成员数: " & lCount

应用场景

1. 排行榜

vb
Sub Leaderboard()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then Exit Sub

    Dim sLeaderboard As String
    sLeaderboard = "leaderboard"

    ' 添加玩家分数
    oRedis.ZAdd sLeaderboard, 1000, "玩家A"
    oRedis.ZAdd sLeaderboard, 1500, "玩家B"
    oRedis.ZAdd sLeaderboard, 800, "玩家C"
    oRedis.ZAdd sLeaderboard, 2000, "玩家D"
    oRedis.ZAdd sLeaderboard, 1200, "玩家E"

    ' 获取排行榜(按分数升序)
    Dim vRanking As Variant
    vRanking = oRedis.ZRange(sLeaderboard, 0, -1)

    Debug.Print "排行榜(升序):"
    Dim i As Long
    If IsArray(vRanking) Then
        For i = 0 To UBound(vRanking)
            Debug.Print "  " & (i + 1) & ". " & vRanking(i)
        Next
    End If

    ' 获取排行榜(带分数)
    vRanking = oRedis.ZRange(sLeaderboard, 0, -1, True)

    Debug.Print vbCrLf & "排行榜(带分数):"
    If IsArray(vRanking) Then
        For i = 0 To UBound(vRanking) Step 2
            If i + 1 <= UBound(vRanking) Then
                Debug.Print "  " & vRanking(i) & ": " & vRanking(i + 1) & " 分"
            End If
        Next
    End If

    ' 获取前 3 名
    vRanking = oRedis.ZRange(sLeaderboard, 0, 2, True)

    Debug.Print vbCrLf & "前 3 名:"
    If IsArray(vRanking) Then
        For i = 0 To UBound(vRanking) Step 2
            If i + 1 <= UBound(vRanking) Then
                Debug.Print "  第 " & (i / 2 + 1) & " 名: " & vRanking(i) & " (" & vRanking(i + 1) & " 分)"
            End If
        Next
    End If

    ' 更新玩家分数
    oRedis.ZAdd sLeaderboard, 2500, "玩家A"
    Debug.Print vbCrLf & "更新玩家A分数后:"

    vRanking = oRedis.ZRange(sLeaderboard, -1, -1, True)
    If IsArray(vRanking) Then
        Debug.Print "  第一名: " & vRanking(0) & " (" & vRanking(1) & " 分)"
    End If

    oRedis.DisConnect
End Sub

2. 时间轴

vb
Sub Timeline()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then Exit Sub

    Dim sTimeline As String
    sTimeline = "timeline:events"

    ' 添加事件(使用时间戳作为分数)
    oRedis.ZAdd sTimeline, 1640995200, "事件1: 项目启动"
    oRedis.ZAdd sTimeline, 1640995800, "事件2: 需求分析"
    oRedis.ZAdd sTimeline, 1640996400, "事件3: 设计完成"
    oRedis.ZAdd sTimeline, 1640997000, "事件4: 开始开发"
    oRedis.ZAdd sTimeline, 1640997600, "事件5: 测试阶段"

    ' 获取按时间排序的事件
    Dim vEvents As Variant
    vEvents = oRedis.ZRange(sTimeline, 0, -1, True)

    Debug.Print "时间轴:"
    Dim i As Long
    If IsArray(vEvents) Then
        For i = 0 To UBound(vEvents) Step 2
            If i + 1 <= UBound(vEvents) Then
                Dim lTimestamp As Long
                lTimestamp = CLng(vEvents(i + 1))
                Debug.Print "  " & FormatDateTime(lTimestamp) & " - " & vEvents(i)
            End If
        Next
    End If

    oRedis.DisConnect
End Sub

Function FormatDateTime(ByVal lTimestamp As Long) As String
    ' 将 Unix 时间戳转换为日期时间字符串
    Dim dtDate As Date
    dtDate = DateAdd("s", lTimestamp, #1/1/1970#)
    FormatDateTime = Format(dtDate, "yyyy-mm-dd hh:nn:ss")
End Function

3. 延迟队列

vb
Sub DelayedQueue()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then Exit Sub

    Dim sQueue As String
    sQueue = "queue:delayed"

    ' 添加任务(使用执行时间作为分数)
    Dim lNow As Long
    lNow = Timer  ' 当前秒数(简化)

    oRedis.ZAdd sQueue, lNow + 60, "任务1: 1分钟后执行"
    oRedis.ZAdd sQueue, lNow + 120, "任务2: 2分钟后执行"
    oRedis.ZAdd sQueue, lNow + 30, "任务3: 30秒后执行"

    ' 模拟时间流逝
    Dim i As Long
    For i = 1 To 5
        lNow = Timer

        ' 获取到期的任务
        Dim vTasks As Variant
        vTasks = oRedis.ZRange(sQueue, 0, 0, True)

        If IsArray(vTasks) And UBound(vTasks) >= 1 Then
            Dim lTaskTime As Long
            lTaskTime = CLng(vTasks(1))

            If lTaskTime <= lNow Then
                Debug.Print "执行任务: " & vTasks(0)
                oRedis.ZRem sQueue, vTasks(0)
            End If
        End If

        ' 等待一秒
        Application.Wait (Now + TimeValue("0:00:01"))
    Next

    oRedis.DisConnect
End Sub

4. 热度排行

vb
Sub HotArticles()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then Exit Sub

    Dim sHotKey As String
    sHotKey = "hot:articles"

    ' 模拟文章浏览量
    oRedis.ZAdd sHotKey, 1000, "article:1"
    oRedis.ZAdd sHotKey, 2500, "article:2"
    oRedis.ZAdd sHotKey, 800, "article:3"
    oRedis.ZAdd sHotKey, 3000, "article:4"
    oRedis.ZAdd sHotKey, 1500, "article:5"

    ' 获取热门文章 TOP 10
    Dim vHotArticles As Variant
    vHotArticles = oRedis.ZRange(sHotKey, -10, -1, True)

    Debug.Print "热门文章 TOP 10:"
    Dim i As Long, lIndex As Long
    lIndex = 1

    If IsArray(vHotArticles) Then
        For i = UBound(vHotArticles) To 0 Step -2
            If i - 1 >= 0 Then
                Debug.Print "  " & lIndex & ". " & vHotArticles(i - 1) & " (" & vHotArticles(i) & " 浏览)"
                lIndex = lIndex + 1
            End If
        Next
    End If

    ' 文章被浏览,增加热度
    Dim sArticle As String
    sArticle = "article:1"

    ' 获取当前分数
    Dim lCurrentScore As Long
    lCurrentScore = CLng(oRedis.Get_(sArticle))
    lCurrentScore = lCurrentScore + 1

    ' 更新分数
    oRedis.ZAdd sHotKey, lCurrentScore, sArticle
    Debug.Print vbCrLf & "文章 " & sArticle & " 浏览后热度更新"

    oRedis.DisConnect
End Sub

5. 优先级队列

vb
Sub PriorityQueue()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then Exit Sub

    Dim sQueue As String
    sQueue = "queue:priority"

    ' 添加任务(使用优先级作为分数,优先级越高分数越小)
    oRedis.ZAdd sQueue, 1, "紧急任务: 修复生产环境bug"
    oRedis.ZAdd sQueue, 3, "普通任务: 编写文档"
    oRedis.ZAdd sQueue, 2, "重要任务: 代码审查"
    oRedis.ZAdd sQueue, 1, "紧急任务: 数据库维护"

    ' 获取最高优先级任务
    Dim vTask As Variant
    vTask = oRedis.ZRange(sQueue, 0, 0)

    If IsArray(vTask) Then
        Debug.Print "最高优先级任务: " & vTask(0)
    End If

    ' 处理任务
    Debug.Print vbCrLf & "处理顺序:"
    While oRedis.ZCard(sQueue) > 0
        vTask = oRedis.ZRange(sQueue, 0, 0)
        If IsArray(vTask) Then
            Debug.Print "  " & vTask(0)
            oRedis.ZRem sQueue, vTask(0)
        End If
    Wend

    oRedis.DisConnect
End Sub

6. 评分系统

vb
Sub RatingSystem()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then Exit Sub

    Dim sProductId As String
    sProductId = "product:1001"

    ' 用户评分
    oRedis.ZAdd sProductId & ":ratings", 5, "user:1"
    oRedis.ZAdd sProductId & ":ratings", 4, "user:2"
    oRedis.ZAdd sProductId & ":ratings", 5, "user:3"
    oRedis.ZAdd sProductId & ":ratings", 3, "user:4"
    oRedis.ZAdd sProductId & ":ratings", 4, "user:5"

    ' 获取所有评分
    Dim vRatings As Variant
    vRatings = oRedis.ZRange(sProductId & ":ratings", 0, -1, True)

    Debug.Print "所有评分:"
    Dim i As Long
    Dim dSum As Double
    dSum = 0

    If IsArray(vRatings) Then
        For i = 0 To UBound(vRatings) Step 2
            If i + 1 <= UBound(vRatings) Then
                Dim lRating As Long
                lRating = CLng(vRatings(i + 1))
                Debug.Print "  " & vRatings(i) & ": " & lRating & " 星"
                dSum = dSum + lRating
            End If
        Next
    End If

    ' 计算平均评分
    Dim lCount As Long
    lCount = oRedis.ZCard(sProductId & ":ratings")
    Dim dAverage As Double
    dAverage = dSum / lCount

    Debug.Print vbCrLf & "平均评分: " & Format(dAverage, "0.00") & " 星 (" & lCount & " 人评分)"

    oRedis.DisConnect
End Sub

完整示例

vb
Sub Example_SortedSets()
    Dim oRedis As New cRedisClient

    If Not oRedis.Connect() Then
        Debug.Print "连接失败: " & oRedis.LastError
        Exit Sub
    End If

    ' 使用 Sorted Set 存储排行榜
    oRedis.ZAdd "leaderboard", 1000, "玩家A"
    oRedis.ZAdd "leaderboard", 1500, "玩家B"
    oRedis.ZAdd "leaderboard", 800, "玩家C"
    oRedis.ZAdd "leaderboard", 2000, "玩家D"

    Debug.Print "排行榜(按分数升序):"
    Dim vPlayers As Variant
    vPlayers = oRedis.ZRange("leaderboard", 0, -1)
    Dim i As Long
    If IsArray(vPlayers) Then
        For i = 0 To UBound(vPlayers)
            Debug.Print "  " & (i + 1) & ". " & vPlayers(i)
        Next
    End If

    Debug.Print vbCrLf & "排行榜(带分数):"
    vPlayers = oRedis.ZRange("leaderboard", 0, -1, True)
    If IsArray(vPlayers) Then
        For i = 0 To UBound(vPlayers) Step 2
            If i + 1 <= UBound(vPlayers) Then
                Debug.Print "  " & vPlayers(i) & ": " & vPlayers(i + 1) & " 分"
            End If
        Next
    End If

    ' 获取有序集合成员数量
    Debug.Print vbCrLf & "玩家数量: " & oRedis.ZCard("leaderboard")

    oRedis.DisConnect
End Sub

Sorted Set 特性

  1. 有序:根据分数自动排序
  2. 唯一:成员唯一,但分数可以相同
  3. 可更新:可以更新成员的分数
  4. 范围查询:支持按分数范围查询
  5. 高性能:插入和查询都是 O(log N) 复杂度

使用建议

  1. 排行榜:使用分数存储评分,ZRange 获取排名
  2. 时间线:使用时间戳作为分数
  3. 优先级队列:使用优先级作为分数
  4. 范围查询:可以查询分数在特定范围内的成员

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