【Redis】Redis是什么?为什么这么快?

  1. 完全基于内存,将数据存储在内存中,读取的时候不需要进行磁盘的 IO
    reids的瓶颈是内存以及网络,之前单线程实现是因为单线程实现简单
  2. 采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU
    这是历史遗留问题,以前服务器的配置低,单线程实现简单而且效果差不多,现在服务器的配置普遍提升,redis已经开始做多线程,性能提升了数倍
  3. 高效的数据结构,Redis 是用 C 语言开发完成的,但在 Redis 字符串中,并没有使用 C 语言中的字符串,而是用一种称为 SDS(Simple Dynamic String)的结构体来保存字符串
struct sdshdr{
 int len;//已使用的字符串的长度
 int free;//未使用的长度
 char buf[];//具体数据,存储的字符数组
}

SDS 与 C 字符串的区别

①常数时间内获得字符串长度

C 字符串本身不记录长度信息,每次获取长度信息都需要遍历整个字符串,复杂度为 O(n);C 字符串遍历时遇到 ‘\0’ 时结束。

SDS 中 len 字段保存着字符串的长度,所以总能在常数时间内获取字符串长度,复杂度是 O(1)。

②避免缓冲区溢出

假设在内存中有两个紧挨着的两个字符串,s1=“xxxxx”和 s2=“yyyyy”。

由于在内存上紧紧相连,当我们对 s1 进行扩充的时候,将 s1=“xxxxxzzzzz”后,由于没有进行相应的内存重新分配,导致 s1 把 s2 覆盖掉,导致 s2 被莫名其妙的修改。

但 SDS 的 API 对 zfc 修改时首先会根据free检查空间是否足够,若不充足则会分配新空间,避免了缓冲区溢出问题。

③减少字符串修改时带来的内存重新分配的次

在 C 中,当我们频繁的对一个字符串进行修改(append 或 trim)操作的时候,需要频繁的进行内存重新分配的操作,十分影响性能。

如果不小心忘记,有可能会导致内存溢出或内存泄漏,对于 Redis 来说,本身就会很频繁的修改字符串,所以使用 C 字符串并不合适。而 SDS 实现了空间预分配和惰性空间释放两种优化策略:

**空间预分配:**当 SDS 的 API 对一个 SDS 修改后,并且对 SDS 空间扩充时,程序不仅会为 SDS 分配所需要的必须空间,还会分配额外的未使用空间。

分配规则如下:如果对 SDS 修改后,len 的长度小于 1M,那么程序将分配和 len 相同长度的未使用空间。举个例子,如果 len=10,重新分配后,buf 的实际长度会变为 10(已使用空间)+10(额外空间)+1(空字符)=21。如果对 SDS 修改后 len 长度大于 1M,那么程序将分配 1M 的未使用空间。

**惰性空间释放:**当对 SDS 进行缩短操作时,程序并不会回收多余的内存空间,而是使用 free 字段将这些字节数量记录下来不释放,后面如果需要 append 操作,则直接使用 free 中未使用的空间,减少了内存的分配。

④二进制安全

在 Redis 中不仅可以存储 String 类型的数据,也可能存储一些二进制数据。

二进制数据并不是规则的字符串格式,其中会包含一些特殊的字符如 ‘\0’,在 C 中遇到 ‘\0’ 则表示字符串的结束,但在 SDS 中,标志字符串结束的是 len 属性。

在 Redis 中,常用的 5 种数据结构和应用场景如下:

  • String: 缓存、计数器、分布式锁等。
  • List: 链表、队列、微博关注人时间轴列表等。
  • Hash: 用户信息、Hash 表等。
  • Set: 去重、赞、踩、共同好友等。
  • Zset: 访问量排行榜、点击量排行榜等。

Redis持久化

RDB 将某个时间点的所有数据都存放到硬盘上

如果系统发生故障,将会丢失最后一次创建快照之后的数据。

如果数据量很大,保存快照的时间会很长

AOF 将写命令添加到 AOF 文件(Append Only File)的末尾

随着服务器写请求的增多,AOF 文件会越来越大

缓存穿透,缓存雪崩以及缓存击穿

缓存穿透:就是客户持续向服务器发起对不存在服务器中数据的请求。客户先在Redis中查询,查询不到后去数据库中查询。

  1. 接口层增加校验,对传参进行个校验,比如说我们的id是从1开始的,那么id<=0的直接拦截;
  2. 缓存中取不到的数据,在数据库中也没有取到,这时可以将key-value对写为key-null,这样可以防止攻击用户反复用同一个id暴力攻击

缓存击穿:就是一个很热门的数据,突然失效,大量请求到服务器数据库中

最好的办法就是设置热点数据永不过期

缓存雪崩:就是大量数据同一时间失效。

缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。