场景: 基于redis实现限流, 每分钟限定用户只能访问100次, 如何实现?
在Redis中实现每分钟限制用户访问次数的限流策略,可以使用Redis的原子操作和过期时间来实现。这里提供一个基于Redis的简单限流实现思路,使用Redis的INCR
和EXPIRE
命令配合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中索引失效(也称为索引未被优化器选择使用)的常见场景包括但不限于以下几种:
类型不匹配:当查询条件中的值类型与索引列定义的类型不一致时,可能导致索引失效。例如,索引列是VARCHAR类型,但查询条件中使用了INT类型的值,MySQL无法有效利用索引。
索引列使用函数或运算表达式:对索引列执行函数操作、算术运算或类型转换后进行查询,MySQL通常不会使用索引。例如,
WHERE ABS(column) = value
或WHERE column + 1 = value
这样的查询条件下,索引可能不会生效。非最左前缀匹配原则:对于复合索引(联合索引),MySQL从左至右依次使用索引列进行查找。若查询条件未从索引最左侧列开始,或者中间跳过了索引列,那么后续索引列就无法发挥作用。比如,对于索引
(col1, col2, col3)
,查询WHERE col2 = 'value'
将不能利用索引。OR连接条件:在查询条件中,如果使用OR连接两个条件,且这两个条件涉及到不同索引或者只有一个条件有索引时,MySQL可能决定不使用索引而进行全表扫描。不过InnoDB存储引擎从MySQL 5.6版本开始引入了索引合并优化功能,一定程度上改善了这类问题。
索引列上的范围查询(Range Condition):如果对索引列进行了范围查询(如
WHERE col > value
),那么对于此索引列后面的列,MySQL同样无法继续使用索引。IS NULL 或 IS NOT NULL 查询:对索引列使用
IS NULL
或IS NOT NULL
的判断,MySQL可能不会使用索引。LIKE模糊查询条件不当:LIKE语句的查询模式中,如果
%
通配符位于字符串开头(如LIKE '%text%'
),由于MySQL无法利用索引进行前缀匹配,因此索引失效。但如果模式是LIKE 'text%'
,且索引是前缀索引或全文索引,则有可能利用索引。索引列参与排序时的排序规则不匹配:如果查询涉及索引列排序,但是排序方式与索引建立时的排序规则不一致,也可能导致索引失效。
索引未覆盖查询所需的所有列:如果查询涉及到不在索引中的列,即使部分列上有索引,MySQL可能仍然会选择全表扫描而非仅仅使用索引。
索引基数低:对于基数非常小的列(如性别列,只有’M’和’F’两个值),MySQL查询优化器可能判断为使用索引并不划算,转而进行全表扫描。
查询结果集过大:当预计查询结果占总数据量比例较大时,MySQL可能认为全表扫描效率更高,从而不使用索引。