2024-09-04

Redis 的过期策略和内存淘汰机制是为了保证 Redis 的高效和可控性。

过期策略

Redis 采用定时删除和惰性删除相结合的方式来处理键的过期。

  • 定时删除:Redis 每个 100ms 检查是否有过期的键,有的话就删除。
  • 惰性删除:当客户端访问一个键时,Redis 会检查这个键是否过期,如果过期就删除。

内存淘汰机制

当 Redis 的内存超过 maxmemory 设置的值时,会根据配置的淘汰策略(maxmemory-policy)来淘汰一些键。

  • noeviction:不进行淘汰,当内存不足时,新写入命令会报错。
  • allkeys-lru:当内存不足以写入数据时,使用最近最少使用算法(LRU)来淘汰键。
  • volatile-lru:只对设置了过期时间的键进行 LRU 淘汰。
  • allkeys-random:随机淘汰键。
  • volatile-random:随机从设置了过期时间的键中淘汰。
  • volatile-ttl:淘汰即将过期的键,优先淘汰 TTL 较短的键。

实例代码

设置键的过期时间:




SET key value EX 10

设置 maxmemory 和 淘汰策略:




# 设置最大内存为 2GB
CONFIG SET maxmemory 2gb

# 设置淘汰策略为 allkeys-lru
CONFIG SET maxmemory-policy allkeys-lru

查看当前的淘汰策略:




CONFIG GET maxmemory-policy

以上是 Redis 过期策略和内存淘汰机制的简要说明和实例代码。

2024-09-04

Redis 使用内存作为数据存储介质,并且设计了高效的数据结构,能够处理并发访问。Redis 提供了一种无锁的机制来处理并发操作,这是通过使用内存中的数据结构和单线程执行命令来实现的。

Redis 的无锁原子操作主要是通过以下几种机制实现的:

  1. 单线程模型:Redis 使用单个线程处理命令,因此可以保证操作的原子性。
  2. 内置的数据结构:Redis 提供了一系列的内置数据结构,如字符串、列表、集合、有序集合和哈希表,它们都是设计成原子操作的。
  3. 单命令执行:Redis 保证每个命令都是原子执行的,即使命令执行时间较长,也不会被其他命令打断。
  4. 使用事务:Redis 的事务可以确保一系列操作的原子性,要么所有命令都执行,要么都不执行。

例如,以下是一个 Redis 命令的简单示例,它演示了如何使用 INCR 命令以原子方式递增键的整数值:




# 假设 key 的值为 "10"

# 执行递增操作
INCR key

# 此时 key 的值会变为 "11"

在实际应用中,开发者可以利用这些机制来构建无锁的、可靠的并发访问机制。

2024-09-04

以下是一个简化的Docker Compose配置示例,用于部署一个包含Java、Nginx和Redis的应用:




version: '3'
 
services:
  javaapp:
    build:
      context: .
      dockerfile: Dockerfile-java
    ports:
      - "8080:8080"
 
  nginx:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./html:/usr/share/nginx/html
    depends_on:
      - javaapp
 
  redis:
    image: redis:latest
    ports:
      - "6379:6379"

在这个配置中,我们定义了三个服务:javaappnginxredisjavaapp 服务使用指定的Dockerfile构建一个Java应用镜像,并将应用端口8080映射到主机端口8080。nginx 服务使用Nginx官方镜像,将Nginx端口80映射到主机端口80,同时挂载Nginx配置文件和网页内容。redis 服务使用Redis官方镜像,并将Redis端口6379映射到主机端口6379。

注意:这个配置假设你的Java应用监听在8080端口,你有一个Dockerfile-java用于构建Java应用镜像,nginx.conf是你的Nginx配置文件,且你的静态网页位于./html目录下。

2024-09-04

报错解释:

RedisCommandExecutionException: ERR unknown command 'SENTINEL' 表示 Spring Boot 应用在尝试执行 Redis 的 Sentinel 命令时遇到了问题。因为 Redis 无法识别或者找不到名为 SENTINEL 的命令,这通常是因为以下几个原因:

  1. Redis 服务器没有配置为Sentinel模式。
  2. Redis客户端库可能不支持Sentinel命令。
  3. Redis服务器版本低于需要执行Sentinel命令的版本。

解决方法:

  1. 确认 Redis 服务器是否已经以 Sentinel 模式运行,并且相关配置文件(如 sentinel.conf)是否存在且正确配置。
  2. 检查你的 Spring Boot 应用中使用的 Redis 客户端库是否支持 Sentinel 命令。如果不支持,考虑更换为支持 Sentinel 功能的客户端库,如 Jedis 或 Lettuce。
  3. 确保你的 Redis 服务器版本至少是支持 Sentinel 命令的版本。如果不是,升级你的 Redis 服务器到一个支持的版本。
  4. 如果你的应用程序是通过连接池来使用 Redis,确保连接池配置正确,并且没有错误地配置为仅连接主节点而不是 Sentinel 实例。

在实施以上解决方案时,请确保遵循你的应用程序和 Redis 环境的具体需求和最佳实践。

2024-09-04

解释:

"Connection reset by peer" 错误通常表示与服务器的连接被对方重置,可能是因为服务器关闭了连接,或者连接由于网络问题丢失。而在尝试通过 Redis 进行操作时,遇到 "NOAUTH Authentication require" 错误,这说明 Redis 实例已经开启了身份验证,但是客户端在执行命令前没有通过 AUTH 命令提供正确的密码进行认证。

解决方法:

  1. 确认 Redis 配置文件中的 requirepass 设置,以知道正确的密码。
  2. 在你的应用程序中,在尝试执行任何命令之前,使用 AUTH 命令和正确的密码进行认证。例如,如果你使用的是 redis-cli,你可以这样做:

    
    
    
    redis-cli -a your_password

    或者在代码中,如果你使用的是像 StackExchange.Redis 这样的客户端库,确保在执行任何其他操作之前,使用以下代码进行认证:

    
    
    
    var redis = ConnectionMultiplexer.Connect("your_hostname");
    redis.GetDatabase().Authenticate("your_password");
  3. 如果你已经在代码中使用了 AUTH 命令,确保它是在尝试其他操作之前执行的。

确保你的应用程序在尝试执行其他命令之前,成功地通过了身份验证。如果问题依然存在,检查网络连接问题,确认没有防火墙或其他网络设备阻断了连接。

2024-09-04

Redis支持两种持久化方式:RDB(Redis DataBase)和AOF(Append Only File)。

  1. RDB:定时将内存中的数据快照保存到磁盘的一个压缩二进制文件中。可以配置定时任务来控制快照的频率。

    配置示例:

    
    
    
    save 900 1      # 900秒内至少1个键被修改则触发保存
    save 300 10     # 300秒内至少10个键被修改则触发保存
    save 60 10000   # 60秒内至少10000个键被修改则触发保存
    dbfilename dump.rdb  # RDB文件名
    dir ./  # RDB文件存储目录
  2. AOF:每个写命令都通过append操作保存到文件中。在服务重启时,通过重放这些命令来恢复数据。

    配置示例:

    
    
    
    appendonly yes  # 开启AOF
    appendfilename "appendonly.aof"  # AOF文件名
    appendfsync everysec  # 每秒同步一次至磁盘

可以同时使用RDB和AOF,以此来结合二者的优势。

注意:在实际生产环境中,应当根据数据的重要性和服务的可用性要求来选择合适的持久化策略。如果对数据持久性要求极高,应使用AOF,并配合适当的文件系统备份策略。如果对性能有较高要求,可以使用RDB,并适当调整保存快照的频率。

2024-09-04

在Spring Boot中,可以使用Redis的有序集合(zset)来实现滑动窗口限流。以下是一个简单的实现示例:




import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
 
import java.util.concurrent.TimeUnit;
 
@Component
public class SlidingWindowLimiter {
 
    @Autowired
    private StringRedisTemplate redisTemplate;
 
    private static final String KEY_PREFIX = "rate_limit:";
    private static final int WINDOW_SIZE = 10; // 滑动窗口的大小,单位是秒
    private static final long TIME_BUCKET_SIZE = 1000L; // 每个时间桶的毫秒数
 
    public boolean isAllowed(String key, int limit) {
        long now = System.currentTimeMillis();
        ZSetOperations<String, String> zSetOps = redisTemplate.opsForZSet();
 
        // 移除过期的元素
        zSetOps.removeRangeByScore(KEY_PREFIX + key, 0, now - WINDOW_SIZE * 1000);
 
        // 添加新的元素并设置过期时间
        String value = String.valueOf(now);
        zSetOps.add(KEY_PREFIX + key, value, now);
        redisTemplate.expire(KEY_PREFIX + key, WINDOW_SIZE, TimeUnit.SECONDS);
 
        // 计算当前窗口的访问次数
        long count = zSetOps.count(KEY_PREFIX + key, now - WINDOW_SIZE * 1000, now);
 
        // 如果超过限制,则不允许通过
        return count < limit;
    }
}

使用方法:




@Autowired
private SlidingWindowLimiter limiter;
 
public void someMethod() {
    String key = "resource_key"; // 需要限流的资源键
    int limit = 100; // 时间窗口内允许的最大请求次数
 
    if (limiter.isAllowed(key, limit)) {
        // 如果允许通过,则执行业务逻辑
    } else {
        // 如果不允许通过,则返回错误或执行其他操作
    }
}

这段代码定义了一个SlidingWindowLimiter组件,它使用Redis的有序集合(zset)来跟踪每个键的访问次数,并且在每次请求时检查是否超过了限制。滑动窗口的大小是10秒,并且每次请求都会更新这个窗口。如果请求次数超过了限定的限制,则不允许通过。这个实现可以很容易地集成到Spring Boot应用程序中,并用于限制对特定资源的访问频率。

2024-09-04



# 导入必要的模块
import redis
 
# 连接到Redis服务器
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 设置键值对
r.set('key', 'value')
 
# 获取并打印键对应的值
value = r.get('key')
print(value)
 
# 使用Redis的列表(list)功能
r.lpush('mylist', 'element1')
r.lpush('mylist', 'element2')
 
# 获取并打印列表中的元素
mylist = r.lrange('mylist', 0, -1)
print(mylist)
 
# 使用Redis的散列(hash)功能
r.hset('myhash', 'field1', 'value1')
r.hset('myhash', 'field2', 'value2')
 
# 获取并打印散列中的字段和值
myhash = r.hgetall('myhash')
print(myhash)
 
# 使用Redis的集合(set)功能
r.sadd('myset', 'member1')
r.sadd('myset', 'member2')
 
# 获取并打印集合中的成员
myset = r.smembers('myset')
print(myset)
 
# 使用Redis的有序集合(sorted set)功能
r.zadd('myzset', {'member1': 1, 'member2': 2})
 
# 获取并打印有序集合中的成员和分数
myzset = r.zrange('myzset', 0, -1, withscores=True)
print(myzset)

这段代码展示了如何使用Python的redis模块来连接Redis服务器,并执行基本的Redis命令,包括设置键值对、操作列表、散列、集合和有序集合。代码中的每一个Redis命令都有详细的注释,方便理解。

2024-09-04

Redis支持的数据类型包括字符串(String), 列表(List), 集合(Set), 有序集合(Sorted Set), 哈希(Hash)等。

  1. 字符串(String)

    字符串是最基本的数据类型,可以用来存储字符串、整数或者浮点数。

常用命令:




SET key value       # 设置字符串值
GET key            # 获取字符串值
INCR key           # 将字符串值增加1
DECR key           # 将字符串值减少1
INCRBY key increment # 将字符串值增加指定的整数
DECRBY key decrement # 将字符串值减少指定的整数
  1. 列表(List)

    列表用于存储有序的字符串列表,可以从两端进行插入和删除。

常用命令:




LPUSH key value1 [value2] # 在列表头部插入一个或多个值
RPUSH key value1 [value2] # 在列表尾部插入一个或多个值
LRANGE key start stop     # 获取列表指定范围内的元素
LPOP key                 # 移出并获取列表的第一个元素
RPOP key                 # 移出并获取列表的最后一个元素
  1. 集合(Set)

    集合是无序的字符串集合,不允许有重复元素。

常用命令:




SADD key member1 [member2] # 向集合添加一个或多个成员
SMEMBERS key             # 获取集合中的所有成员
SREM key member1 [member2] # 移除集合中的一个或多个成员
  1. 有序集合(Sorted Set)

    有序集合是字符串成员的集合,每个成员都关联着一个分数。成员是唯一的,但分数可以重复。

常用命令:




ZADD key score1 member1 [score2 member2] # 向有序集合添加一个或多个成员,或者更新已存在成员的分数
ZRANGE key start stop [WITHSCORES]      # 获取有序集合指定范围内的成员
ZREM key member [member ...]             # 移除有序集合中的一个或多个成员
  1. 哈希(Hash)

    哈希是键值对的集合,Redis中的哈希值是字段和值的映射。

常用命令:




HSET key field value # 将哈希表中的字段的值设为 value
HGET key field      # 获取存储在哈希表中的字段的值
HGETALL key        # 获取在哈希表中指定key的所有字段和值
HDEL key field1 [field2] # 删除一个或多个哈希表字段

以上是各种数据类型的基本操作,在实际应用中,可以根据业务需求选择合适的数据类型和相关操作。例如,可以使用List存储用户的消息列表,Set存储用户的好友列表,Sorted Set存储用户的排行榜,Hash存储用户的属性信息等。

2024-09-04

Redis 的 Hash 数据结构可以存储键值对集合,与 Python 中的字典类似。Redis 的 Hash 实际上是字典的字典,外层字典的 key 是用户定义的名字,内层字典存储实际的键值对数据。

在 Redis 中,一个 Hash 可以通过以下命令操作:

  • HSET key field value:设置字段的值。
  • HGET key field:获取字段的值。
  • HMSET key field1 value1 [field2 value2]:同时设置多个字段的值。
  • HMGET key field1 [field2]:获取多个字段的值。
  • HGETALL key:获取在 Hash 中的所有字段和值。
  • HKEYS key:获取 Hash 中的所有字段名。
  • HVALS key:获取 Hash 中的所有值。
  • HEXISTS key field:检查字段是否存在。
  • HSETNX key field value:只有当字段不存在时,设置字段的值。
  • HINCRBY key field increment:将字段的值增加指定的整数。
  • HDEL key field1 [field2]:删除一个或多个字段。

以下是使用 Redis 命令操作 Hash 的 Python 示例代码:




import redis
 
# 连接到 Redis
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 设置 Hash 字段
r.hset('myhash', 'field1', 'value1')
 
# 获取 Hash 字段
value = r.hget('myhash', 'field1')
print(value.decode('utf-8'))  # 输出: value1
 
# 设置多个 Hash 字段
r.hmset('myhash', {'field2': 'value2', 'field3': 'value3'})
 
# 获取多个 Hash 字段
values = r.hmget('myhash', 'field2', 'field3')
print([value.decode('utf-8') for value in values])  # 输出: ['value2', 'value3']
 
# 获取 Hash 中的所有字段和值
all_fields_and_values = r.hgetall('myhash')
print({key.decode('utf-8'): value.decode('utf-8') for key, value in all_fields_and_values.items()})
 
# 获取 Hash 中的所有字段
fields = r.hkeys('myhash')
print([field.decode('utf-8') for field in fields])
 
# 获取 Hash 中的所有值
values = r.hvals('myhash')
print([value.decode('utf-8') for value in values])
 
# 检查字段是否存在
exists = r.hexists('myhash', 'field1')
print(exists)  # 输出: True
 
# 只有当字段不存在时,设置字段的值
r.hsetnx('myhash', 'field1', 'new_value1')
 
# 将字段的值增加
r.hincrby('myhash', 'field3', 5)
 
# 删除一个或多个字段
r.hdel('myhash', 'field1', 'field2')

以上代码演示了如何在 Python 中使用 Redis 的 Hash 数据结构。需要注意的是,在实际应用中,你可能需要使用 Redis 的客户端库,如 redis-py,来与 Redis 服务器进行交互。