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 服务器进行交互。

2024-09-04

Redis中集合(Set)的底层实现可以是intset或者hashtable。

  1. intset:当集合中的所有元素都是整数并且元素数量不多时,Redis会使用整数集合(intset)作为集合的底层实现。
  2. hashtable:当集合的元素不满足intset的条件时,Redis会使用哈希表(hashtable)作为集合的底层实现。

例如,以下是使用Redis命令操作集合的示例:




# 创建一个名为myset的集合,并添加几个元素
> SADD myset 1 "two" 3.0

# 查看myset的内容
> SMEMBERS myset
1) "1"
2) "two"
3) "3.0"

# 查看myset的底层实现
> OBJECT ENCODING myset
"intset" # 假设元素都是整数或能转换为整数

# 继续添加一个非整数元素
> SADD myset "four"

# 查看myset的底层实现变化
> OBJECT ENCODING myset
"hashtable" # 当集合包含不可以转换为整数的元素时,底层实现变为哈希表

以上命令展示了如何在Redis中创建一个集合,如何查看集合的内容,以及如何查看集合的底层实现。当我们向集合中添加非整数元素"four"时,集合的底层实现由原来的intset变为hashtable。

2024-09-04

复现Redis未授权访问远程代码执行漏洞(CVE-2015-3208,也称为CNVD-2015-07557)的步骤如下:

  1. 使用Docker运行Vulfocus的Redis未授权访问环境。
  2. 利用Redis未授权访问漏洞执行任意命令。

以下是一个简单的复现示例:

首先,确保你有Docker环境。

然后,运行以下命令来启动一个包含漏洞的Docker容器:




docker run -d --name cve-2015-3208 vulhub/redis:unauthorized-rce

容器启动后,漏洞利用通常需要发送一个特制的请求到Redis服务。你可以使用curl或者nc来进行测试。

例如,使用curl




curl -X POST -d '*2\r\n$6\r\nconfig\r\n$3\r\nget\r\n$8\r\ndir\r\n*1\r\n$2\r\n*\r\n*1\r\n$1\r\n1\r\n' http://your-ip:6379

替换http://your-ip:6379为实际的Redis服务地址。

如果Redis未授权访问漏洞存在,执行上述命令后,你将能够获取服务器上的目录信息。

请注意,在实际的渗透测试中,你应该遵守相关法律法规,并在获得授权的情况下进行测试。不要对未经授权的系统执行渗透测试或任何可能违反其网络安全政策的活动。

2024-09-04

Redis 支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合),zset(有序集合)。

以下是各种数据类型的基本操作命令:

  1. string(字符串)



# 设置键值
SET key value
 
# 获取键值
GET key
 
# 删除键值
DEL key
  1. hash(哈希)



# 设置哈希字段
HSET hash_key field value
 
# 获取哈希字段
HGET hash_key field
 
# 删除哈希字段
HDEL hash_key field
  1. list(列表)



# 在列表左侧插入元素
LPUSH list_key value
 
# 在列表右侧插入元素
RPUSH list_key value
 
# 获取列表元素
LRANGE list_key start stop
 
# 删除列表元素
LREM list_key count value
  1. set(集合)



# 添加元素到集合
SADD set_key member
 
# 获取集合所有成员
SMEMBERS set_key
 
# 删除集合中的元素
SREM set_key member
  1. zset(有序集合)



# 添加成员到集合,并设置分数
ZADD zset_key score member
 
# 获取集合所有成员及分数
ZRANGE zset_key start stop [WITHSCORES]
 
# 删除集合中的成员
ZREM zset_key member

以上命令均为基本命令,Redis 还支持更复杂的操作,如事务、锁等,需要根据具体场景使用。

2024-09-04

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

以下是Redis中一些常用的命令和数据结构:

  1. 常用命令:

    • SET:为一个键设置值。
    • GET:获取一个键的值。
    • DEL:删除一个键。
    • EXPIRE:为一个键设置过期时间。
    • EXISTS:检查一个键是否存在。
    • KEYS:查找符合给定模式的键。
    • PING:测试与服务器的连接是否仍然有效。
  2. 数据结构:

    • 字符串(String):可以存储任何类型的字符串,包括二进制、JSON、序列化对象等。
    • 列表(List):按照插入顺序保存数据,可以在头部或尾部添加或删除元素。
    • 集合(Set):无序的字符串集合,不允许重复。
    • 有序集合(Sorted Set):字符串成员与浮点数分数相关联,成员是唯一的,分数可以重复。
    • 哈希(Hash):包含键值对的无序散列表。
    • 位图(Bitmap):用于处理二进制数据,可以对位进行设置、清除和切换操作。
    • 超级字符串(HyperLogLog):用于计算集合的基数,适合用于数据统计。
  3. 使用示例:

    
    
    
    # 设置键值对
    SET key "value"
     
    # 获取键的值
    GET key
     
    # 删除键
    DEL key
     
    # 设置键的过期时间
    EXPIRE key 60
     
    # 检查键是否存在
    EXISTS key
     
    # 查找所有符合模式的键
    KEYS *
     
    # 测试与服务器的连接
    PING

以上是Redis的基本概念和常用命令,实际应用中可以根据需求选择合适的数据结构和命令。

2024-09-04



import redis
import time
import uuid
 
class RedisLock:
    def __init__(self, client: redis.Redis, lock_name: str, lock_timeout: int = 10):
        self.client = client
        self.lock_name = lock_name
        self.lock_timeout = lock_timeout
 
    def acquire_lock(self):
        identifier = str(uuid.uuid4())
        end = time.time() + self.lock_timeout
        while time.time() < end:
            if self.client.set(self.lock_name, identifier, ex=self.lock_timeout, nx=True):
                return identifier
            time.sleep(0.001)
        return False
 
    def release_lock(self, identifier):
        pipe = self.client.pipeline(True)
        while True:
            try:
                pipe.watch(self.lock_name)
                if pipe.get(self.lock_name) == identifier:
                    pipe.multi()
                    pipe.delete(self.lock_name)
                    pipe.execute()
                    return True
                pipe.unwatch()
                break
            except redis.exceptions.WatchError:
                pass
        return False
 
# 使用示例
client = redis.Redis(host='localhost', port=6379, db=0)
lock = RedisLock(client, "my_lock")
identifier = lock.acquire_lock()
if identifier:
    try:
        # 在这里执行需要互斥访问的代码
        print("Lock acquired and executed code")
    finally:
        lock.release_lock(identifier)
else:
    print("Could not acquire lock")

这段代码实现了一个基于Redis的分布式锁,使用Python语言和redis-py库。它提供了一种方法来避免多个服务实例同时写入数据或执行特定操作。锁通过一个唯一标识符来释放,确保只有获得锁的服务实例能够执行操作。这是构建微服务时常用的一种技术,以确保数据的一致性和系统的可靠性。