场景: 基于redis实现限流, 每分钟限定用户只能访问100次, 如何实现?

在Redis中实现每分钟限制用户访问次数的限流策略,可以使用Redis的原子操作和过期时间来实现。这里提供一个基于Redis的简单限流实现思路,使用Redis的INCREXPIRE命令配合Lua脚本来确保操作的原子性:

-- Lua脚本
local key = "rate_limit:user_" .. ARGV[1] -- ARGV[1]为用户ID或其他唯一标识符
local limit = tonumber(ARGV[2]) -- ARGV[2]为每分钟允许的最大访问次数,这里是100

-- 通过INCR原子性地增加计数
local currentCount = redis.call("INCR", key)

-- 如果当前计数超过限制,则将计数重置为0,并返回错误提示
if currentCount > limit then
    redis.call("SET", key, 0) -- 可选,如果不希望保留计数,则重置为0
    return 0
end

-- 设置键的过期时间为1分钟,确保每分钟过后计数清零
redis.call("EXPIRE", key, 60)

return currentCount

在Go或者其他编程语言中调用这个Lua脚本,可以这样实现:

import (
    "github.com/go-redis/redis/v8"
    "errors"
)

func RateLimit(userId string, maxVisits int) (bool, error) {
    client := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "", // no password set
        DB:       0,  // use default DB
    })

    script := redis.NewScript(`
        local key = "rate_limit:user_" .. ARGV[1]
        local limit = tonumber(ARGV[2])

        local currentCount = redis.call("INCR", key)
        if currentCount > limit then
            redis.call("SET", key, 0) -- 可选
            return 0
        end

        redis.call("EXPIRE", key, 60)
        return currentCount
    `)

    var count int64
    err := script.Run(client, []string{userId}, maxVisits).Int64(&count)
    if err != nil {
        return false, err
    }

    if count == 0 {
        return false, errors.New("访问次数超过限制")
    }

    return true, nil
}

// 使用
ok, err := RateLimit("user123", 100)
if !ok {
    // 处理限流逻辑
}

这个实现的基本逻辑是:

  • 使用用户ID生成唯一的限流键(如rate_limit:user_123)。
  • 使用INCR命令原子性地增加用户访问计数。
  • 判断计数是否超过限制,如果超过则重置计数(可选)并返回错误。
  • 使用EXPIRE命令为该限流键设置1分钟的过期时间,以便每分钟过后自动清除计数,重新开始新的计数周期。

请注意,实际应用中可能需要根据具体业务需求和性能要求调整限流策略,例如使用滑动窗口限流算法或令牌桶算法。

mysql索引失效场景?

MySQL中索引失效(也称为索引未被优化器选择使用)的常见场景包括但不限于以下几种:

  1. 类型不匹配:当查询条件中的值类型与索引列定义的类型不一致时,可能导致索引失效。例如,索引列是VARCHAR类型,但查询条件中使用了INT类型的值,MySQL无法有效利用索引。

  2. 索引列使用函数或运算表达式:对索引列执行函数操作、算术运算或类型转换后进行查询,MySQL通常不会使用索引。例如,WHERE ABS(column) = valueWHERE column + 1 = value 这样的查询条件下,索引可能不会生效。

  3. 非最左前缀匹配原则:对于复合索引(联合索引),MySQL从左至右依次使用索引列进行查找。若查询条件未从索引最左侧列开始,或者中间跳过了索引列,那么后续索引列就无法发挥作用。比如,对于索引 (col1, col2, col3),查询 WHERE col2 = 'value' 将不能利用索引。

  4. OR连接条件:在查询条件中,如果使用OR连接两个条件,且这两个条件涉及到不同索引或者只有一个条件有索引时,MySQL可能决定不使用索引而进行全表扫描。不过InnoDB存储引擎从MySQL 5.6版本开始引入了索引合并优化功能,一定程度上改善了这类问题。

  5. 索引列上的范围查询(Range Condition):如果对索引列进行了范围查询(如WHERE col > value),那么对于此索引列后面的列,MySQL同样无法继续使用索引。

  6. IS NULL 或 IS NOT NULL 查询:对索引列使用 IS NULLIS NOT NULL 的判断,MySQL可能不会使用索引。

  7. LIKE模糊查询条件不当:LIKE语句的查询模式中,如果%通配符位于字符串开头(如LIKE '%text%'),由于MySQL无法利用索引进行前缀匹配,因此索引失效。但如果模式是LIKE 'text%',且索引是前缀索引或全文索引,则有可能利用索引。

  8. 索引列参与排序时的排序规则不匹配:如果查询涉及索引列排序,但是排序方式与索引建立时的排序规则不一致,也可能导致索引失效。

  9. 索引未覆盖查询所需的所有列:如果查询涉及到不在索引中的列,即使部分列上有索引,MySQL可能仍然会选择全表扫描而非仅仅使用索引。

  10. 索引基数低:对于基数非常小的列(如性别列,只有’M’和’F’两个值),MySQL查询优化器可能判断为使用索引并不划算,转而进行全表扫描。

  11. 查询结果集过大:当预计查询结果占总数据量比例较大时,MySQL可能认为全表扫描效率更高,从而不使用索引。

最后编辑: kuteng  文档更新时间: 2024-04-02 09:53   作者:kuteng