从本地到云端:看 Redis 如何降本增效

2024-08-23 07:47:58 浏览数 (3)

封面封面

前言

假设不存在数据库,数据都存在一个文本文件里,想搜索"腾讯云开发者社区",要怎么去搜索?

在Linux中,有grep、awk等命令来查找;使用一些高级语言,像Java,Python也可以通过IO流来读取一个文件。到这为止,有什么可以优化的点吗?

从外存,内存到缓存

在计算机当中,数据是存在磁盘的。当在Windows平台上进行解压缩文件操作时,打开任务管理器可看到清晰的数据读写,通常是MB/s的级别。

文件解压时产生的读写(固态)文件解压时产生的读写(固态)

而内存呢?切到内存上,只能看见3200MT/s。换算后,内存与外存在带宽上就能相差几百倍。寻址能力更是相差几万倍。可见二者差距。

DDR4:常见的工作频率为2133MHz,单通道带宽可达25.6GB/s(2133MHz * 64bit/8)。 MT/s /2 = MHz;

回到开头的话题,抛出第一个疑问点:假如文件足够大时,硬盘(读写)率先成为瓶颈,代码该怎样去解决呢?先来看数据库的优化的做法。

数据库中的存储优化

data pagedata page

现在有一张表,很多行数据,存在物理磁盘中。数据库中用了很多4k的小块,这里的4k和磁盘的对齐方式正好匹配,一次I/O对应一块区域。结合上图,可以想到数据库的建表中的索引,再加上数据库的独特的B 树结构,减少很多磁盘的寻址,IO的流量。同时,索引也是小块page。

索引就如查字典一样,无论多么复杂的字,都可以几步内检索到。

对齐方式验证:新建一个空白文档,输入一堆数字保存后 文件大小:1.14KB(不到4KB),占用空间:4KB。

缓存

B 树和data page就是万能方案了吗?并不是。假设有10个人,100个,1000个人来查字典,一本字典有点显得捉襟见肘。实际上,有很多人查的字是重复的,我们就可以让前来查字典的人先去问查过字典的人,这样就形成了缓存。

缓存在一定程度上弥补了硬盘的缺陷,当然也带来了更多的管理问题。就缓存用的数据库来说,Redis已经成为首选。

Redis

先来看看他有多火吧!

db-engine rankingdb-engine ranking

键值对型(key-value)的第一,总数据库的第六。

为什么是 Redis

在与前端进行交互时,较为常用的是JSON格式。那试着用这种方式与缓存交互?

存入数字,字符串,列表都不在话下,但取出来就犯愁了。一个大列表只取一条数据,I/O很快成为瓶颈。那么这种类似JSON一样传输文件的就是Memcache。

全部数据返回优点不太现实,于是尝试在“值”加入类型,再给于一定增删改查方法,这种数据向计算移动的做法。恭喜,到此已经入门了Redis的存储方式。

Redis 的字符串

先来看一段代码

代码语言:shell复制
set key1 9 # 存一个key为key1,值为9的对象
object encoding key1 # 查看类型,结果是int

append key1 'a'  # key1末尾追加字符串'a',
get key1 # 9a
object encoding key1 # raw

这就是Redis的字符串,get、set都是String类型的内置方法。但他又与传统的类型不同,从2和6行,变化了的编码方式可以看出来,这是一种动态类型的字符串,Redis用SDS来存储。

SDS(Simple Dynamic String)简单动态字符串。

重写字符串有什么好处呢?

①解决遗留问题

取得字符串长度是一个很常见的操作,C语言的实现方式是遍历(Redis由C开发),Redis直接用一个length来维护。

代码语言:c复制
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 :SDS编码类型,当前类型为sdshdr8*/
    char buf[]; /* 实际存储内容 */
};

②优化内存

从上述代码中还可以看到__attribute__ ((__packed__))这样一段,其实是为了告诉编译器,不使用字节对其,而使用紧凑型分配内存。

③二进制安全

在IO中读写文件,分为两种,字符型和字节型。字符型代表就是txt文本。字节流就很多了,图片,java编译后的class文件。字节流可以通过一定编码格式变成字符,而反过来就很难操作。Redis正是采用字节流。

使用上有什么亮点呢?

比如去存储一个中国的“中”字,UTF-8编码会占三个字节,GBK编码就会占两个字节。假如你在链接时使用了--raw,这样就解锁了ASCII编码,取出来的值又变成了其他方式。

团队在使用时要统一编码格式。 还有很多优化的点,篇幅有限,先略过。

云上的 Redis

笔者作为开发人员,只熟悉一些简单部署命令,很多时候需要一个面板来操作。加上Redis是内存数据库,加在后台服务的机器上,很影响性能,所以很有必要去云上选购一台Redis。

  • 内网低延迟 无论是数据库或是其他服务,通过外网连接,网速都将会成为性能瓶颈。云厂商早就想到了这一点,所以勾选的时候注意服务器省市和分区。购买调节参数时,换配置会重置分区以及其他选项需注意。
  • 全面的控制面板 自动备份,主备替换,一件导入配置等独特的功能满足基本所需。
  • 数据看板 有种看到图表就掌控大全的舒适感。
腾讯云数据库 Redis 功能一览腾讯云数据库 Redis 功能一览

案例:热门排行榜

给几个热门话题做一个排行榜,需要准备什么?

  • 话题唯一
  • 热度数值
  • 实时排行
  • 高频次的IO

Redis中恰好有这样一个数据类型,完美契合,那就是Sorted Set。

数据结构

大多数语言都有set结构,它是一种不重复元素的集合,而Redis的Sorted Set在唯一性的基础上,又增加了有序性。

底层结构

  • listpack(7.0版本之前是ziplist)
  • skiplist dict

这个顺序也代表着,前者适合较小范围的场景。

代码语言:c复制
/* ZSETs use a specialized version of Skiplists: zsets使用一种特殊版本的skiplists */
typedef struct zskiplistNode {
    sds ele;
    double score;
    struct zskiplistNode *backward;
    struct zskiplistLevel {
        struct zskiplistNode *forward;
        unsigned long span;
    } level[];
} zskiplistNode;
  • ele 真实数据,可以用来存储话题
  • score 分数,可以存放热度值或其加权计算后的数值

具体操作

Java 中可以使用三种方式连接Redis

  • Jedis
  • Lettuce
  • Redisson
  • Spring Data Redis(基于Jedis)

由于API不同,所以下面主要来说具体Redis的命令。

向“热搜”榜单添加话题与分数

代码语言:shell复制
ZADD '热搜' 1 "黑神话:悟空"
ZADD '热搜' 2 "腾讯云"
ZADD '热搜' 3 "云游戏"

获取热搜榜

代码语言:shell复制
ZREVRANGE '热搜' 0 9 WITHSCORES

返回结果

代码语言:txt复制
 1)  "云游戏"
 2)  "3"
 3)  "腾讯云"
 4)  "2"
 5)  "黑神话:悟空"
 6)  "1"

更新热搜榜

代码语言:shell复制
ZINCRBY '热搜' 999 "黑神话:悟空"

再去获取排行榜,已经变成了

代码语言:txt复制
 1)  "黑神话:悟空"
 2)  "1000"
 3)  "云游戏"
 4)  "3"
 5)  "腾讯云"
 6)  "2"

到现在为止,已经获得了一个简单的热搜榜。

one more thing

Redis的功能不止于此,这些都来自于他独特的数据类型以及架构。不过,最常用的还是缓存功能。经过常见八股文的洗礼,就可以成为一个高手!

0 人点赞