2024-09-02

Redis 的 Set 类型是一个无序的字符串集合。它允许你存储一组不重复的字符串,并且提供了一些与之相关的操作。

以下是一些与 Redis Set 类型相关的操作:

  1. 添加元素到 Set:



sadd key value1 [value2]
  1. 获取 Set 中的所有元素:



smembers key
  1. 检查元素是否在 Set 中:



sismember key member
  1. 计算 Set 中的元素数量:



scard key
  1. 删除 Set 中的指定元素:



srem key member1 [member2]
  1. 随机从 Set 中移除元素并返回:



spop key
  1. 返回 Set 中的一个随机元素:



srandmember key
  1. 对两个 Set 进行交集、并集、差集操作:



sinter key1 [key2]
sunion key1 [key2]
sdiff key1 [key2]
  1. 将交集、并集、差集结果存储到指定的 Set 中:



sinterstore destination key1 [key2]
sunionstore destination key1 [key2]
sdiffstore destination key1 [key2]

这些命令提供了 Set 类型的基本操作,在实际开发中可以根据需要灵活使用。

2024-09-02

跳跃表(Skiplist)是一种可以替代平衡树的数据结构,它允许快速的插入、删除、查找操作,所有操作的平均时间复杂度都是O(logN)。在Redis中,跳跃表被广泛应用于有序集合数据类型(Sorted Set)的底层实现。

以下是一个简单的C语言实现,演示如何创建和使用一个跳跃表节点和跳跃表结构:




#include <stdio.h>
#include <stdlib.h>
 
typedef struct skiplistNode {
    int key;
    struct skiplistNode *backward;
    struct skiplistNode *next[];
} skiplistNode;
 
typedef struct skiplist {
    skiplistNode *header, *tail;
    int level;
} skiplist;
 
// 创建一个带有特定层数的新节点
skiplistNode *createNode(int key, int level) {
    skiplistNode *node = malloc(sizeof(skiplistNode) + level * sizeof(skiplistNode*));
    node->key = key;
    node->backward = NULL;
    for (int i = 0; i < level; i++) {
        node->next[i] = NULL;
    }
    return node;
}
 
// 初始化一个空的跳跃表
skiplist *initSkipList() {
    skiplist *list = malloc(sizeof(skiplist));
    list->header = createNode(0, 32); // 假设最大层数为32
    list->tail = NULL;
    list->level = 0;
    return list;
}
 
// 插入一个新的节点
void insert(skiplist *list, int key) {
    skiplistNode *update[32];
    skiplistNode *node = list->header;
    int level = list->level;
 
    for (int i = level; i >= 0; i--) {
        while (node->next[i] && node->next[i]->key < key) {
            node = node->next[i];
        }
        update[i] = node;
    }
 
    if (node->next[0] && node->next[0]->key == key) {
        // 键值已存在,不做操作
    } else {
        int newLevel = rand() % 32; // 假设随机函数返回值的范围用于决定新节点的层数
        if (newLevel > level) {
            for (int i = level + 1; i <= newLevel; i++) {
                update[i] = list->header;
            }
            level = newLevel;
        }
 
        skiplistNode *newNode = createNode(key, newLevel);
        for (int i = 0; i < newLevel; i++) {
            newNode->next[i] = update[i]->next[i];
            update[i]->next[i] = newNode;
 
            if (update[i] == list->tail) {
                list->tail = newNode;
            }
        }
 
        if (level > list->level) {
            list->level = level;
        }
    }
}
 
// 查找一个节点
skiplistNode *search(skiplist *list, int key) {
    skiplistNode *node = list->header;
    for (int i = list->level; i >= 0; i--) {
        while (node->next[i] && node->next[i]->key < key) {
            node = node->next[i];
        }
    }
    if (node->next[0] && node->next[0]->key == key) {
        return node->next[0];
    }
    return NULL;
}
 
// 删除一个节点
void deleteNo
2024-09-02

Redis的底层实现中,双向链表是一种常用的数据结构,它被广泛应用于组织数据集合,提供高性能的节点插入、删除和遍历操作。

在Redis中,双向链表被广泛用于实现复杂的数据结构,如列表键、发布/订阅机制、慢查询日志等。

以下是一个简单的双向链表实现的例子:




#include <stdio.h>
#include <stdlib.h>
 
// 定义链表节点结构体
typedef struct listNode {
    struct listNode *prev;
    struct listNode *next;
    void *value;
} listNode;
 
// 创建一个新的链表节点
listNode *listNodeCreate(void *value) {
    listNode *node = (listNode *)malloc(sizeof(listNode));
    node->prev = NULL;
    node->next = NULL;
    node->value = value;
    return node;
}
 
// 将节点插入到链表头部
void listAddNodeHead(listNode **list, listNode *node) {
    if (*list == NULL) {
        *list = node;
    } else {
        node->next = *list;
        (*list)->prev = node;
        *list = node;
    }
}
 
// 移除链表的头节点
void listDelNode(listNode **list, listNode *node) {
    if (node->prev == NULL) {
        *list = node->next;
    } else {
        node->prev->next = node->next;
    }
    
    if (node->next) {
        node->next->prev = node->prev;
    }
}
 
// 打印链表
void listPrint(listNode *list) {
    while (list != NULL) {
        printf("%d\n", *(int *)(list->value));
        list = list->next;
    }
}
 
int main() {
    listNode *list = NULL;
    listNode *node1 = listNodeCreate((void *)1);
    listNode *node2 = listNodeCreate((void *)2);
    listNode *node3 = listNodeCreate((void *)3);
 
    listAddNodeHead(&list, node1);
    listAddNodeHead(&list, node2);
    listAddNodeHead(&list, node3);
 
    printf("List content:\n");
    listPrint(list);
 
    listDelNode(&list, node2);
    printf("\nList after delete node2:\n");
    listPrint(list);
 
    return 0;
}

在这个例子中,我们定义了一个简单的双向链表节点结构体listNode,并实现了创建新节点、将节点插入到链表头部以及移除节点的函数。最后,在main函数中演示了如何使用这些函数来创建、修改和打印链表。

2024-09-02

跳跃表(skiplist)是Redis中的一种数据结构,它允许快速的插入、删除、查找操作,在实现上是一种可以替代平衡树的数据结构,在查询速度上有可比的优势。

以下是一个简单的C语言实现的跳跃表节点和跳跃表结构的示例代码:




#include <stdlib.h>
 
// 跳跃表节点结构体
typedef struct skiplistNode {
    int key;
    struct skiplistNode *backward;
    struct skiplistNode *next[];
} skiplistNode;
 
// 跳跃表结构体
typedef struct skiplist {
    skiplistNode *header;
    int level;
} skiplist;
 
// 创建一个新的跳跃表节点
skiplistNode *createNode(int key, int level) {
    skiplistNode *node = malloc(sizeof(skiplistNode) + level * sizeof(skiplistNode*));
    node->key = key;
    node->backward = NULL;
    for(int i = 0; i < level; i++) {
        node->next[i] = NULL;
    }
    return node;
}
 
// 初始化一个跳跃表
skiplist *initSkipList() {
    skiplist *list = malloc(sizeof(skiplist));
    list->header = createNode(0, 32); // 假设最大层数为32
    list->header->backward = NULL;
    list->level = 0;
    return list;
}
 
// 插入一个新的节点到跳跃表
void insert(skiplist *list, int key) {
    skiplistNode *update[32];
    skiplistNode *node = list->header;
    int level = list->level;
 
    for(int i = level; i >= 0; i--) {
        while(node->next[i] && node->next[i]->key < key) {
            node = node->next[i];
        }
        update[i] = node;
    }
 
    if(node->next[0] && node->next[0]->key == key) {
        // 如果键值已存在,不做操作
    } else {
        int newLevel = randomLevel(); // 假设randomLevel函数用于生成新节点的层数
        if(newLevel > level) {
            for(int i = level + 1; i <= newLevel; i++) {
                update[i] = list->header;
            }
            level = newLevel;
        }
 
        skiplistNode *newNode = createNode(key, newLevel);
        for(int i = 0; i < newLevel; i++) {
            newNode->next[i] = update[i]->next[i];
            update[i]->next[i] = newNode;
 
            newNode->backward = update[i];
            if(newNode->next[i]) {
                newNode->next[i]->backward = newNode;
            }
        }
 
        if(level > list->level) {
            list->level = level;
        }
    }
}
 
// 查找一个节点
skiplistNode *search(skiplist *list, int key) {
    skiplistNode *node = list->header;
 
2024-09-02

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

Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

Redis的9种数据结构如下:

  1. String
  2. List
  3. Set
  4. Sorted Set
  5. Hash
  6. Bitmap
  7. HyperLogLog
  8. Geo
  9. Stream

下面我们来详细解析一下这9种数据结构:

  1. String

String是Redis最基本的类型,你可以理解成与Memcached一模一个的类型,它是二进制安全的,可以存储任何数据,包括图片等。

  1. List

List是一个双向链表,可以从两端进行push和pop操作。

  1. Set

Set是一个无序的集合,集合中的元素都是唯一的。

  1. Sorted Set

Sorted Set是一个有序的集合,每个元素都会关联一个浮点数的分数,通过这个分数来进行排序。

  1. Hash

Hash是一个键值对集合,是一个string类型的field和value的映射表,适合存储对象。

  1. Bitmap

Bitmap是一个非常大的位数组,可以用来做位级别的操作,如计数、状态变更等。

  1. HyperLogLog

HyperLogLog是用来做基数统计的算法,适用于大数据量的基数统计,它只需要很少的内存。

  1. Geo

Geo是用来处理地理位置信息的,可以进行地理位置的查询,比如:距离查询,经纬度的查询等。

  1. Stream

Stream是一个新的数据类型,它是按照时间顺序存储消息的,可以用来实现类似于Kafka的功能,可以用来实现消息队列、事件流等功能。

以上就是Redis的9种数据结构,每种数据结构都有自己的特点和应用场景,可以根据实际需求选择合适的数据结构。

2024-09-02

在这个关于使用Redis管理登录令牌的实战篇中,我们将实现一个简单的用户登录系统,用户可以通过短信验证码登录。以下是实现的核心函数:




import redis
import uuid
 
# 假设这是一个发送短信验证码的函数
def send_sms_code(phone_number):
    code = str(uuid.uuid4())[:4]  # 生成一个随机验证码
    # 存储验证码到Redis,设置过期时间为5分钟
    r.setex(phone_number, 5 * 60, code)
    # 假设这里发送短信,发送验证码
    # send_sms(phone_number, code)
    return True
 
# 验证短信验证码
def verify_sms_code(phone_number, code):
    stored_code = r.get(phone_number)
    if stored_code is None:
        return False  # 验证码不存在
    if stored_code.decode('utf-8') == code:
        return True  # 验证码匹配
    return False  # 验证码不匹配
 
# 初始化Redis连接
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 测试函数
# send_sms_code("13812345678")  # 假设发送成功
# print(verify_sms_code("13812345678", "1234"))  # 假设用户输入验证码进行验证

这段代码展示了如何生成和存储短信验证码,以及如何验证用户输入的验证码。在实际应用中,你需要替换发送短信的部分,并且要确保验证码的生命周期(这里设置为5分钟)适合你的应用场景。此外,你还需要处理并发登录请求和验证码的安全性问题。

2024-09-02

Redisson提供了一系列的分布式数据结构,这些数据结构都有对应的Java接口,并且它们都是线程安全的。下面是一些常用的Redisson数据结构以及它们的应用场景:

  1. RLock:分布式可重入锁

    • 应用场景:用作分布式锁,确保多个节点不会并发写同一份数据。
  2. RSemaphore:分布式信号量

    • 应用场景:控制并发访问的数量,比如数据库连接池。
  3. RAtomicLong:分布式原子长整型

    • 应用场景:计数器、序列生成器。
  4. RTopic:分布式发布/订阅

    • 应用场景:实现消息发布和订阅。
  5. RQueue:分布式队列

    • 应用场景:消息队列,生产者消费者模型。
  6. RDeque:分布式双端队列

    • 应用场景:有序元素的队列,如排行榜。
  7. RMap:分布式Map

    • 应用场景:分布式缓存。
  8. RSortedSet:分布式有序集合

    • 应用场景:排序功能,如排行榜。

示例代码(创建RLock实例):




Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:6379");
 
RedissonClient redisson = Redisson.create(config);
 
RLock lock = redisson.getLock("anyLock");
// 获取锁
lock.lock();
try {
    // 业务逻辑
} finally {
    // 释放锁
    lock.unlock();
}
 
// 或者使用try-with-resources自动释放锁
try (RLock lock = redisson.getLock("anyLock")) {
    // 业务逻辑
}

以上代码展示了如何使用Redisson获取一个分布式锁并执行业务逻辑,确保在多线程环境下数据的一致性与正确性。

2024-09-02

Redis是一种开源的内存中数据结构存储系统,可以用作数据库、缓存和消息中间件。以下是关于Redis的一些关键概念的概述和示例代码:

  1. Redis数据结构:Redis支持多种数据结构,如字符串(String), 哈希表(Hash), 列表(List), 集合(Set), 有序集合(Sorted Set)等。



# 字符串
redis.set('key', 'value')
value = redis.get('key')
 
# 哈希表
redis.hset('hash_key', 'field', 'value')
value = redis.hget('hash_key', 'field')
 
# 列表
redis.rpush('list_key', 'value1')
redis.rpush('list_key', 'value2')
values = redis.lrange('list_key', 0, -1)
 
# 集合
redis.sadd('set_key', 'member1')
redis.sadd('set_key', 'member2')
members = redis.smembers('set_key')
 
# 有序集合
redis.zadd('zset_key', {'member1': 1, 'member2': 2})
members = redis.zrange('zset_key', 0, -1)
  1. Redis高性能:Redis使用了单线程模型进行事件驱动,能够有效利用CPU,并保证高性能。



# 通常情况下,Redis的性能足够高,无需额外优化
  1. Redis持久化:Redis支持RDB和AOF两种持久化机制,可以将数据保存到磁盘上防止数据丢失。



# 在redis.conf中配置持久化
# RDB
save 900 1      # 900秒内至少1个键被修改则触发保存
save 300 10     # 300秒内至少10个键被修改则触发保存
save 60 10000   # 60秒内至少10000个键被修改则触发保存
 
# AOF
appendonly yes  # 开启AOF持久化
appendfsync everysec  # 每秒同步到磁盘
  1. Redis主从复制:Redis支持主从复制,可以实现数据的多副本存储,提供数据备份和高可用性。



# 在redis.conf中配置主从复制
slaveof <master-ip> <master-port>
  1. Redis集群:Redis Cluster是Redis的分布式解决方案,可以自动分配数据到不同的节点上。



# 集群配置较为复杂,通常通过Redis的命令行工具或者配置文件进行设置
  1. Redis缓存淘汰:Redis使用LRU或TTL(Time to Live)算法来管理和淘汰缓存数据。



# 设置键的过期时间
redis.expire('key', 3600)  # 设置键在3600秒后过期

这些概念涵盖了Redis的基本知识点,并提供了相关的配置和操作示例。在实际应用中,可能还需要考虑如何优化Redis的性能,配置合理的内存大小,处理好内存溢出问题等。

2024-09-02

Redis是一种开源的内存中数据结构存储系统,可以用作数据库、缓存和消息中间件。以下是一些关于Redis的常见问题和解答:

  1. Redis的数据结构有哪些?

Redis支持的数据结构包括字符串(String), 哈希表(Hash), 列表(List), 集合(Set), 有序集合(Sorted Set), 位图(Bitmap), HyperLogLog和流(Stream)。

  1. Redis分布式锁的实现方式?

Redis分布式锁通常使用SETNX命令(或在Redis 2.6.12以上版本中使用SET key value EX max-lock-time NX),这是因为SETNX只在键不存在时才设置值,即获得锁。解锁时使用DEL命令。

示例代码:




import redis
 
def acquire_lock(conn, lock_name):
    identifier = str(uuid.uuid4())
    end = time.time() + 10 # 10秒超时
    while time.time() < end:
        if conn.setnx(lock_name, identifier):
            return identifier
        time.sleep(0.001)
 
def release_lock(conn, lock_name, identifier):
    pipe = conn.pipeline(True)
    while True:
        try:
            pipe.watch(lock_name)
            if pipe.get(lock_name) == identifier:
                pipe.multi()
                pipe.delete(lock_name)
                pipe.execute()
                return True
            pipe.unwatch()
            break
        except redis.exceptions.WatchError:
            pass
    return False
  1. Redis的持久化策略有哪些?

Redis支持两种持久化策略:RDB(默认)和AOF。

  • RDB:定时将内存中的数据快照保存到磁盘的一个压缩二进制文件中。
  • AOF:保存Redis服务器所执行的所有写操作命令到文本文件。
  1. Redis的内存淘汰策略有哪些?
  • noeviction:不进行内存淘汰。
  • allkeys-lru:当内存不足以容纳更多数据时,使用最近最少使用算法进行淘汰。
  • volatile-lru:只对设置了过期时间的键进行最近最少使用算法。
  • allkeys-random:随机淘汰键。
  • volatile-random:随机淘汰设置了过期时间的键。
  • volatile-ttl:淘汰即将过期的键。
  1. Redis的性能优化有哪些?
  • 使用批量操作减少网络往返时间。
  • 使用管道(pipeline)进行批量操作。
  • 使用SCAN命令代替KEYS命令,避免大量占用主线程。
  • 适当使用SORT, SORT BY, GET, DEL等命令优化集合操作。
  • 使用Redis的集群功能分散负载。
  • 配置合适的maxmemory和内存淘汰策略。
  1. Redis的事务是什么?

Redis的事务可以一次执行多个命令,它可以保证一系列命令的原子性,要么全部执行,要么全部不执行。

  1. Redis的发布/订阅机制是什么?

Redis的发布/订阅机制允许客户端订阅一个或多个频道,当发布者向频道发布消息时,订阅者会收到消息。

  1. Redis的Geo是什么?

Redis 3.2版本开始支持地理位置功能,可以将用

2024-09-02

跳表(skiplist)是一种可以替代平衡树的数据结构,它允许快速的插入、删除、查找操作,所有操作的平均时间复杂度都是O(logN)。

Redis中的跳表用于有序集合数据类型(Sorted Set)的实现。

以下是一个简单的C语言实现的跳表节点和跳表结构的示例:




#include <stdlib.h>
 
// 跳表节点结构体
typedef struct skiplistNode {
    double key;         // 键值
    void *value;        // 值
    struct skiplistNode *backward;  // 后退指针
    struct skiplistLevel {
        struct skiplistNode *forward;  // 前进指针
        unsigned int span;             // 跳跃的长度
    } level[];
} skiplistNode;
 
// 跳表结构体
typedef struct skiplist {
    struct skiplistNode *header, *tail;  // 头尾节点指针
    unsigned long length;               // 节点数量
    int level;                          // 最大层数
} skiplist;
 
// 创建一个跳表节点
skiplistNode *createNode(int level, double key, void *value) {
    skiplistNode *node = malloc(sizeof(skiplistNode) + level * sizeof(skiplistNode));
    node->key = key;
    node->value = value;
    return node;
}
 
// 初始化一个跳表
skiplist *initSkipList() {
    int level = 1;  // 起始层数
    skiplistNode *node = createNode(level, 0, NULL); // 创建头节点
    skiplist *list = malloc(sizeof(skiplist));
    list->header = list->tail = node;
    list->length = 0;
    list->level = level;
    return list;
}
 
// 插入操作示例
void insert(skiplist *list, double key, void *value) {
    skiplistNode *update[64], *node;
    int i, level;
 
    // 找到所有层次的更新节点,同时确保node为空
    node = list->header;
    for (i = list->level - 1; i >= 0; i--) {
        while (node->level[i].forward && node->level[i].forward->key < key) {
            node = node->level[i].forward;
        }
        update[i] = node;
    }
 
    // 随机生成层数
    level = randomLevel();  // 实现随机层数的函数
    if (level > list->level) {
        for (i = list->level; i < level; i++) {
            update[i] = list->header;
        }
        list->level = level;
    }
 
    // 创建新节点
    node = createNode(level, key, value);
 
    // 将新节点链接到跳表
    for (i = 0; i < level; i++) {
        node->level[i].forward = update[i]->level[i].forward;
        update[i]->level[i].forward = node;
 
        // 更新前后节点指针
        if (node->level[i].forward) {
            node->level[i].span = node->level[i].forward->level[i].span - (node->key > node->level[i].forward->key);
        } else {
            node->level[i].span = list->length - (update[i] == list->header);
        }
        if (update[i] == list->header) {
            list->header->level[i].span = list->length + 1;
        } else {
            up