- 多实用工具 - Redis 是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis 本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。
2)Redis 的安装
这一步比较简单,你可以在网上搜到许多满意的教程,这里就不再赘述。
给一个菜鸟教程的安装教程用作参考:https://www.runoob.com/redis/redis-install.html
3) 测试本地 Redis 性能
当你安装完成之后,你可以先执行 redis-server
让 Redis 启动起来,然后运行命令 redis- benchmark -n 100000 -q
来检测本地同时执行 10 万个请求时的性能:
当然不同电脑之间由于各方面的原因会存在性能差距,这个测试您可以权当是一种「乐趣」就好。
2.Redis五种基本数据结构
Redis有 5 种基础数据结构,它们分别是:string(字符串)、list(列表)、hash(字典)、set(集合) 和zset(有序集合)。这 5 种是 Redis 相关知识中最基础、最重要的部分,下面我们结合源码以及一些实践来给大家分别讲解一下。
注意:
每种数据结构都有自己底层的内部编码实现,而且是多种实现,这样Redis会在合适的场景选择合适的内部编码。
可以看到每种数据结构都有两种以上的内部编码实现,例如string数据结构就包含了raw、int和embstr三种内部编码。
同时,有些内部编码可以作为多种外部数据结构的内部实现,例如ziplist就是hash、list和zset共有的内部编码。
1)字符串 string
Redis 中的字符串是一种 动态字符串,这意味着使用者可以修改,它的底层实现有点类似于 Java 中的ArrayList,有一个字符数组,从源码的 sds.h/sdshdr 文件 中可以看到 Redis 底层对于字符串的定义SDS,即 Simple Dynamic String
结构:
/* Note: sdshdr5 is never used, we just access the flags byte directly.
- However is here to document the layout of type 5 SDS strings. /
struct attribute ((packed)) sdshdr5 {
unsigned char flags; / 3 lsb of type, and 5 msb of string length /
char buf[];
};
struct attribute ((packed)) sdshdr8 {
uint8_t len; / used /
uint8_t alloc; / excluding the header and null terminator /
unsigned char flags; / 3 lsb of type, 5 unused bits /
char buf[];
};
struct attribute ((packed)) sdshdr16 {
uint16_t len; / used /
uint16_t alloc; / excluding the header and null terminator /
unsigned char flags; / 3 lsb of type, 5 unused bits /
char buf[];
};
struct attribute ((packed)) sdshdr32 {
uint32_t len; / used /
uint32_t alloc; / excluding the header and null terminator /
unsigned char flags; / 3 lsb of type, 5 unused bits /
char buf[];
};
struct attribute ((packed)) sdshdr64 {
uint64_t len; / used /
uint64_t alloc; / excluding the header and null terminator /
unsigned char flags; / 3 lsb of type, 5 unused bits */
char buf[];
};
你会发现同样一组结构 Redis 使用泛型定义了好多次,为什么不直接使用 int 类型呢?
因为当字符串比较短的时候,len 和 alloc 可以使用 byte 和 short 来表示,Redis 为了对内存做极致的优化,不同长度的字符串使用不同的结构体来表示。
①、SDS 与 C 字符串的区别
为什么不考虑直接使用 C 语言的字符串呢?因为 C 语言这种简单的字符串表示方式 不符合 Redis 对字符串在安全性、效率以及功能方面的要求。我们知道,C 语言使用了一个长度为 N+1 的字符数组来表示长度为 N 的字符串,并且字符数组最后一个元素总是 '\0'
。(下图就展示了 C 语言中值为 “Redis” 的一 个字符数组)
这样简单的数据结构可能会造成以下一些问题:
- 获取字符串长度为 O(N) 级别的操作 → 因为 C 不保存数组的长度,每次都需要遍历一遍整个数组;
- 不能很好的杜绝 缓冲区溢出/内存泄漏 的问题 → 跟上述问题原因一样,如果执行拼接 or 缩短字符串的操作,如果操作不当就很容易造成上述问题;
- C 字符串 只能保存文本数据 → 因为 C 语言中的字符串必须符合某种编码(比如 ASCII),例如中间出现的 ‘\0’ 可能会被判定为提前结束的字符串而识别不了;
我们以追加字符串的操作举例,Redis 源码如下:
/* Append the specified binary-safe string pointed by ‘t’ of ‘len’ bytes to the
- end of the specified sds string ‘s’.
- After the call, the passed sds string is no longer valid and all the
- references must be substituted with the new pointer returned by the call. */
sds sdscatlen(sds s, const void *t, size_t len) {
// 获取原字符串的长度
size_t curlen = sdslen(s);
// 按需调整空间,如果容量不够容纳追加的内容,就会重新分配字节数组并复制原字符串的内容到新数组中
s = sdsMakeRoomFor(s,len);
if (s == NULL) return NULL; // 内存不足
memcpy(s+curlen, t, len); // 追加目标字符串到字节数组中
sdssetlen(s, curlen+len); // 设置追加后的长度
s[curlen+len] = ‘\0’; //