2024-09-09

Redis 事务是一组命令的集合。事务同意多个命令在同一上下文中执行,保证事务内的命令按预期执行,中间不会插入其他命令。

Redis 事务主要通过 MULTIEXECDISCARDWATCH 命令来实现。

  1. MULTI 命令用于开启一个事务。它的返回值总是 OK
  2. EXEC 命令用于执行事务中的所有命令。
  3. DISCARD 命令用于取消事务,放弃执行事务中的所有命令。
  4. WATCH 命令用于监视一个或多个键,如果在事务执行之前键的值发生变化,则取消事务的执行。

以下是一个使用 Redis 事务的 Python 示例:




import redis
 
# 连接到本地Redis服务器
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 开启Redis事务
pipeline = r.pipeline()
 
# 将要在事务中执行的命令加入到事务中
pipeline.multi()
pipeline.set('key1', 'value1')
pipeline.set('key2', 'value2')
pipeline.set('key3', 'value3')
 
# 执行事务
pipeline.exec()
 
# 获取设置的值进行验证
print(r.get('key1')) # 输出:value1
print(r.get('key2')) # 输出:value2
print(r.get('key3')) # 输出:value3

在这个例子中,我们首先通过 pipeline 对象开启了一个事务,然后使用 multi 方法开始事务,并将三个 set 命令加入到事务中。最后,我们通过 exec 方法执行了这个事务。

注意:在实际使用中,应该确保 Redis 服务器的正常运行,并处理可能出现的异常。

2024-09-09



import redis
 
# 连接到Redis服务器
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
 
# 发布消息到channel
def publish_message(channel, message):
    redis_client.publish(channel, message)
 
# 订阅特定channel的消息
def subscribe_to_channel(channel):
    pubsub = redis_client.pubsub()
    pubsub.subscribe(channel)
    for message in pubsub.listen():
        if message['type'] == 'message':
            print(f"Received message: {message['data'].decode()}")
 
# 示例使用
channel_name = "my_channel"
message_to_publish = "Hello, Redis!"
 
# 发布消息
publish_message(channel_name, message_to_publish)
 
# 订阅消息
subscribe_to_channel(channel_name)

这段代码演示了如何使用redis-py库在Redis中发布和订阅消息。首先,我们连接到Redis服务器。然后,我们定义了publish_message函数来发布消息到指定的channel,以及subscribe_to_channel函数来订阅一个channel并打印接收到的消息。最后,我们使用示例展示了如何调用这些函数来发送和接收消息。

2024-09-09



# 设置Redis最大内存
maxmemory 2gb
 
# 设置内存淘汰策略
maxmemory-policy allkeys-lru
 
# 设置Redis的最大连接数
maxclients 10000
 
# 设置Redis的超时时间
timeout 300
 
# 开启Redis的持久化机制,使用RDB和AOF
save 900 1
save 300 10
save 60 10000
 
appendonly yes
appendfsync everysec
 
# 设置Redis的日志级别
loglevel notice
 
# 设置Redis的日志文件路径
logfile /var/log/redis/redis-server.log
 
# 设置Redis的数据库数量
databases 16
 
# 设置Redis的TCP连接的监听队列长度
tcp-backlog 511
 
# 设置Redis的最小空闲内存
min-slaves-to-write 3
min-slaves-max-lag 10

以上配置示例展示了如何设置Redis的最大内存、内存淘汰策略、最大连接数、超时时间、持久化策略、日志记录级别和路径、数据库数量、TCP连接队列长度、最小空闲内存等关键配置项,以优化Redis的性能。

2024-09-09

在CentOS 7上安装Redis 7.2.4,可以按照以下步骤进行:

  1. 首先,更新你的系统包索引并升级所有包:

    
    
    
    sudo yum update
  2. 安装编译相关工具:

    
    
    
    sudo yum install -y gcc make
  3. 下载Redis 7.2.4的源代码:

    
    
    
    wget http://download.redis.io/releases/redis-7.2.4.tar.gz
  4. 解压源代码:

    
    
    
    tar xzf redis-7.2.4.tar.gz
  5. 进入Redis源代码目录:

    
    
    
    cd redis-7.2.4
  6. 编译Redis:

    
    
    
    make
  7. 安装Redis到系统路径里(可选):

    
    
    
    sudo make install
  8. 如果你想要安装Redis的可执行文件到/usr/local/bin,确保你已经执行了上面的make install步骤。
  9. 启动Redis服务器:

    
    
    
    redis-server
  10. 你也可以通过指定配置文件来启动Redis服务器:

    
    
    
    redis-server /path/to/redis.conf
  11. 如果你想要Redis随系统启动,你可以使用systemd服务文件。创建一个名为redis.service的文件,内容如下:

    
    
    
    [Unit]
    Description=Redis In-Memory Data Store
    After=network.target
     
    [Service]
    User=redis
    Group=redis
    ExecStart=/usr/local/bin/redis-server /path/to/redis.conf
    ExecStop=/usr/local/bin/redis-cli shutdown
    Restart=always
     
    [Install]
    WantedBy=multi-user.target

    将此文件保存至/etc/systemd/system/redis.service,然后使用以下命令启用和启动Redis服务:

    
    
    
    sudo systemctl daemon-reload
    sudo systemctl enable redis
    sudo systemctl start redis

请注意,上述步骤可能需要根据你的系统配置和安全策略进行调整。特别是,建议设置密码,配置防火墙规则,以及使用非root用户运行Redis服务等安全建议。

2024-09-09

在解释这个问题之前,我们先来回忆一下,什么是双写一致性问题。

双写一致性问题是指,数据同时写入数据库和缓存时,由于并发或者其他原因导致数据不一致的问题。

以下是解决双写一致性问题的几种常见方法:

  1. 延时双删

在数据更新操作完成后,先删除缓存,然后再更新数据库,最后再删除缓存。这里的第三步是为了等待数据库更新操作完成,确保缓存中的数据是最新的。




def update_db_and_redis(key, new_value):
    delete_redis(key)  # 删除缓存
    update_db(key, new_value)  # 更新数据库
    time.sleep(1)  # 等待数据库操作完成
    delete_redis(key)  # 再次删除缓存
  1. 使用分布式锁

在并发情况下,可以使用分布式锁来确保同一时间只有一个操作可以对数据进行写操作。




def update_with_lock(key, new_value):
    acquire_lock(key)
    delete_redis(key)
    update_db(key, new_value)
    release_lock(key)
    delete_redis(key)
  1. 异步通知

通过消息队列或者其他机制,在数据更新后异步通知删除缓存。




def update_db_and_notify(key, new_value):
    delete_redis(key)
    update_db(key, new_value)
    send_message('delete_cache', key)  # 发送消息通知删除缓存
  1. 使用canal

canal 是一个基于 MySQL 数据库增量日志解析的开源工具,可以监控 MySQL 的 binlog,然后通过 binlog 来实现数据库和缓存的同步。




def update_db_with_canal(key, new_value):
    update_db(key, new_value)
    delete_redis(key)

以上方法都是在不同的场景下使用的,具体使用哪一种方法,需要根据实际的业务场景和需求来决定。

2024-09-09

SSRF(Server-Side Request Forgery,服务器端请求伪造)是一种攻击技术,它允许攻击者利用服务器发送任意的请求到内部系统。在这种情况下,攻击者可能会利用SSRF漏洞来尝试未授权访问目标服务器上的Redis服务。如果Redis没有设置适当的保护措施,攻击者可以通过发送特制的请求获取到服务器的敏感文件,进而实现未授权的文件读取或getshell。

解决这个问题通常涉及以下几个方面:

  1. 更新Redis到最新版本,因为一些新版本可能已经内置了防御措施。
  2. 配置Redis,使其不监听外部接口或者限制可访问的IP地址。
  3. 如果Redis需要从外部访问,使用防火墙规则来限制哪些服务可以连接到Redis服务器。
  4. 对于SSRF漏洞,应该修复或避免使用SSRF功能,或者在服务器上实施更严格的输入验证。

如果你是开发者,以下是一个简单的示例代码来避免SSRF攻击:




import requests
 
def fetch_url(url):
    # 只允许请求本地地址
    if not url.startswith('http://localhost') and not url.startswith('http://127.0.0.1'):
        return "Invalid URL"
    
    response = requests.get(url)
    return response.text
 
# 使用示例
url = "http://example.com"
content = fetch_url(url)
print(content)

在这个示例中,我们检查URL是否以http://localhosthttp://127.0.0.1开头,只有当URL符合这个条件时才会进行请求。这样可以防止SSRF攻击利用服务端发起的请求进行攻击。

2024-09-09

Redis主从复制是一种数据复制方式,它可以确保主数据库(Master)和从数据库(Slave)之间的数据一致性。主数据库可以进行读写操作,当写操作导致数据变化时,这些变化会以日志的形式发送给从数据库。从数据库会应用这些变化,保持与主数据库的数据同步。

以下是配置Redis主从复制的基本步骤:

  1. 配置主数据库(Master):

    redis.conf文件中,注释或移除slaveof指令。

  2. 配置从数据库(Slave):

    redis.conf文件中,使用slaveof指令指定主数据库的IP和端口。

    
    
    
    slaveof <master-ip> <master-port>

    如果需要,还可以配置从数据库只读(slave-read-only yes)。

  3. 重启Redis服务使配置生效。

示例配置:

Master redis.conf:




# No special configuration for Master required

Slave redis.conf:




slaveof <master-ip> <master-port>
slave-read-only yes

在实际操作中,替换<master-ip><master-port>为实际的主数据库的IP地址和端口号。

主从复制可以通过Redis的命令行接口进行快速配置,不需要修改配置文件。例如,可以在运行的Slave Redis服务器上执行:




redis-cli SLAVEOF <master-ip> <master-port>

要取消从数据库的复制状态,可以在从数据库执行:




redis-cli SLAVEOF NO ONE

这些是主从复制的基本概念和配置方法,实际应用中可能需要考虑更多因素,如身份验证、哨兵模式等。

2024-09-09

Redis 是一个开源的使用 C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。

以下是 Redis 的一些主要知识点:

  1. 数据类型:Redis 支持字符串、列表、集合、有序集合、哈希表等数据类型。
  2. 持久化:Redis 提供 RDB 和 AOF 两种持久化方式,可以将数据保存到磁盘以防止数据丢失。
  3. 复制:Redis 支持主从复制,可以实现数据的多副本存储。
  4. 过期删除策略:Redis 采用惰性删除和定时删除两种策略来删除过期的键。
  5. 内存管理:Redis 使用了高效的内存管理机制来避免内存碎片问题。
  6. 事务:Redis 的事务可以一次性执行多条命令,并保证他们按照顺序执行,中间不会插入其他命令。
  7. 发布订阅:Redis 提供发布订阅功能,可以用于消息的广播。
  8. 分片:Redis 支持分片(Cluster),可以将数据分布在多个 Redis 节点上。
  9. 主要配置:包括最大内存设置(maxmemory),数据持久化策略,安全设置(如密码,SSL/TLS),高可用和集群配置等。
  10. 性能测试:Redis 提供了性能测试工具 redis-benchmark,可以测试 Redis 在不同条件下的性能。

示例代码(使用 Python 的 redis 库):




import redis
 
# 连接到 Redis
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 设置键值对
r.set('key', 'value')
 
# 获取键的值
value = r.get('key')
print(value)
 
# 列表操作
r.lpush('mylist', 'value1')
r.lpush('mylist', 'value2')
print(r.lrange('mylist', 0, -1))  # 打印列表元素
 
# 集合操作
r.sadd('myset', 'value1')
r.sadd('myset', 'value2')
print(r.smembers('myset'))  # 打印集合元素
 
# 哈希操作
r.hset('myhash', 'field1', 'value1')
print(r.hgetall('myhash'))  # 打印哈希表内容
 
# 过期时间设置
r.setex('mykey', 10, 'value')  # 设置键值对和过期时间(10秒)
 
# 事务操作
pipeline = r.pipeline()
pipeline.set('key1', 'value1')
pipeline.set('key2', 'value2')
pipeline.execute()
 
# 发布订阅
pubsub = r.pubsub()
pubsub.subscribe('mychannel')
pubsub.publish('mychannel', 'hello')

以上代码展示了如何使用 Python 的 redis 库来连接 Redis,执行基本的数据类型操作,设置键的过期时间,使用事务,以及发布订阅消息。

2024-09-09

以下是一个简化的解决方案,用于在Docker环境中搭建Redis哨兵模式。

首先,创建一个名为 docker-compose.yml 的文件,内容如下:




version: '3'
 
services:
  redis-master:
    image: redis:6.0.9
    ports:
      - "6379:6379"
 
  redis-slave-1:
    image: redis:6.0.9
    command: redis-server --slaveof redis-master 6379
    depends_on:
      - redis-master
 
  redis-slave-2:
    image: redis:6.0.9
    command: redis-server --slaveof redis-master 6379
    depends_on:
      - redis-master
 
  redis-sentinel-1:
    image: redis:6.0.9
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
      - ./sentinel.conf:/etc/redis/sentinel.conf
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2
 
  redis-sentinel-2:
    image: redis:6.0.9
    command: redis-sentinel /etc/redis/sentinel.conf
    volumes:
      - ./sentinel.conf:/etc/redis/sentinel.conf
    depends_on:
      - redis-master
      - redis-slave-1
      - redis-slave-2

然后,在同一目录下创建哨兵配置文件 sentinel.conf,内容如下:




sentinel monitor mymaster redis-master 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000

最后,通过以下命令启动服务:




docker-compose up -d

这将会启动一个主节点,两个从节点和两个哨兵。哨兵会监控主节点,并在主节点宕机时自动进行故障转移。

2024-09-09

Redis缓存雪崩是指在同一时间内,大量的key同时失效,导致大量的查询请求直接打到数据库,给数据库带来巨大压力,可以称为“瞬时数据库负载增加,引起系统响应变慢甚至宕机”。

预防措施

  1. 设置随机的过期时间:给每个key的过期时间加上一个随机值,避免同时失效。
  2. 使用锁或队列:更新数据库操作时使用分布式锁或者使用队列来控制访问量。
  3. 数据预热:启动时预先加载热点数据到缓存中。

应对措施

  1. 缓存数据预热:启动时预先加载热点数据到缓存中。
  2. 设置缓存的高可用性:使用Redis Sentinel或Redis Cluster。
  3. 实现缓存缓冲区:当缓存失效时,通过加锁或队列来控制写数据库的操作,避免直接压力数据库。
  4. 服务降级:在缓存失效后,可以暂时提供静态或默认响应,而不是直接报错。

解决方案




// 示例代码,使用Redis锁来控制缓存更新操作
public String getWithLock(Jedis jedis, String key, String lockKey, String defaultValue) {
    String value = jedis.get(key);
    if (value == null) {
        // 使用分布式锁,避免多线程时重复更新缓存
        if (jedis.setnx(lockKey, "1") == 1) {
            // 获取锁成功,设置锁的过期时间,避免死锁
            jedis.pexpire(lockKey, 1000);
            try {
                // 重新从数据库获取数据,并更新到缓存
                value = db.get(key);
                jedis.set(key, value);
                jedis.expire(key, 300); // 设置缓存的过期时间
            } finally {
                // 释放锁
                jedis.del(lockKey);
            }
        } else {
            // 其他线程在更新缓存,等待一段时间后重试
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            getWithLock(jedis, key, lockKey, defaultValue); // 递归调用
        }
    }
    return value != null ? value : defaultValue;
}

在实际应用中,还需要根据具体的业务场景来调整和优化解决方案。