WEB开发者-分享WEB开发知识,让开发变得更简单!

Redis中必须要掌握的20个问题,快来收藏吧!!

本篇文章给大家分享20个必知必会、必须要掌握的Redis问题,希望对大家有所帮助,快来收藏吧!

Redis中必须要掌握的20个问题,快来收藏吧!!

Redis是什么?

Redis(Remote Dictionary Server)是一个使用 C 语言编写的,高性能非关系型的键值对数据库。与传统数据库不同的是,Redis 的数据是存在内存中的,所以读写速度非常快,被广泛应用于缓存方向。Redis可以将数据写入磁盘中,保证了数据的安全不丢失,而且Redis的操作是原子性的。【相关推荐:Redis视频教程】

Redis的优点?

  • 基于内存操作,内存读写速度快。

  • Redis是单线程的,避免线程切换开销及多线程的竞争问题。单线程是指网络请求使用一个线程来处理,即一个线程处理所有网络请求,Redis 运行时不止有一个线程,比如数据持久化的过程会另起线程。

  • 支持多种数据类型,包括String、Hash、List、Set、ZSet等。

  • 支持持久化。Redis支持RDB和AOF两种持久化机制,持久化功能可以有效地避免数据丢失问题。

  • 支持事务。Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。

  • 支持主从复制。主节点会自动将数据同步到从节点,可以进行读写分离。

Redis为什么这么快?

  • 基于内存:Redis是使用内存存储,没有磁盘IO上的开销。数据存在内存中,读写速度快。
  • 单线程实现( Redis 6.0以前):Redis使用单个线程处理请求,避免了多个线程之间线程切换和锁资源争用的开销。
  • IO多路复用模型:Redis 采用 IO 多路复用技术。Redis 使用单线程来轮询描述符,将数据库的操作都转换成了事件,不在网络I/O上浪费过多的时间。
  • 高效的数据结构:Redis 每种数据类型底层都做了优化,目的就是为了追求更快的速度。

Redis为何选择单线程?

  • 避免过多的上下文切换开销。程序始终运行在进程中单个线程内,没有多线程切换的场景。
  • 避免同步机制的开销:如果 Redis选择多线程模型,需要考虑数据同步的问题,则必然会引入某些同步机制,会导致在操作数据过程中带来更多的开销,增加程序复杂度的同时还会降低性能。
  • 实现简单,方便维护:如果 Redis使用多线程模式,那么所有的底层数据结构的设计都必须考虑线程安全问题,那么 Redis 的实现将会变得更加复杂。

Redis应用场景有哪些?

  • 缓存热点数据,缓解数据库的压力。

  • 利用 Redis 原子性的自增操作,可以实现计数器的功能,比如统计用户点赞数、用户访问数等。

  • 简单的消息队列,可以使用Redis自身的发布/订阅模式或者List来实现简单的消息队列,实现异步操作。

  • 限速器,可用于限制某个用户访问某个接口的频率,比如秒杀场景用于防止用户快速点击带来不必要的压力。

  • 好友关系,利用集合的一些命令,比如交集、并集、差集等,实现共同好友、共同爱好之类的功能。

Memcached和Redis的区别?

  • Redis 只使用单核,而 Memcached 可以使用多核。

  • MemCached 数据结构单一,仅用来缓存数据,而 Redis 支持多种数据类型

  • MemCached 不支持数据持久化,重启后数据会消失。Redis 支持数据持久化

  • Redis 提供主从同步机制和 cluster 集群部署能力,能够提供高可用服务。Memcached 没有提供原生的集群模式,需要依靠客户端实现往集群中分片写入数据。

  • Redis 的速度比 Memcached 快很多。

  • Redis 使用单线程的多路 IO 复用模型,Memcached使用多线程的非阻塞 IO 模型。

Redis 数据类型有哪些?

基本数据类型

1、String:最常用的一种数据类型,String类型的值可以是字符串、数字或者二进制,但值最大不能超过512MB。

2、Hash:Hash 是一个键值对集合。

3、Set:无序去重的集合。Set 提供了交集、并集等方法,对于实现共同好友、共同关注等功能特别方便。

4、List:有序可重复的集合,底层是依赖双向链表实现的。

5、SortedSet(ZSet):有序Set。内部维护了一个score的参数来实现。适用于排行榜和带权重的消息队列等场景。

特殊的数据类型

1、Bitmap:位图,可以认为是一个以位为单位数组,数组中的每个单元只能存0或者1,数组的下标在 Bitmap 中叫做偏移量。Bitmap的长度与集合中元素个数无关,而是与基数的上限有关。

2、Hyperloglog。HyperLogLog 是用来做基数统计的算法,其优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定的、并且是很小的。典型的使用场景是统计独立访客。

3、Geospatial :主要用于存储地理位置信息,并对存储的信息进行操作,适用场景如定位、附近的人等。

Redis事务

事务的原理是将一个事务范围内的若干命令发送给 Redis,然后再让 Redis 依次执行这些命令。

事务的生命周期:

  • 使用MULTI开启一个事务;

  • 在开启事务的时候,每次操作的命令将会被插入到一个队列中,同时这个命令并不会被真正执行;

  • EXEC命令进行提交事务。

Redis中必须要掌握的20个问题,快来收藏吧!!

一个事务范围内某个命令出错不会影响其他命令的执行,不保证原子性:

first:0>MULTI
"OK"
first:0>set a 1
"QUEUED"
first:0>set b 2 3 4
"QUEUED"
first:0>set c 6
"QUEUED"
first:0>EXEC
1) "OK"
2) "OK"
3) "OK"
4) "ERR syntax error"
5) "OK"
6) "OK"
7) "OK"

WATCH命令

WATCH命令可以监控一个或多个键,一旦其中有一个键被修改,之后的事务就不会执行(类似于乐观锁)。执行EXEC命令之后,就会自动取消监控。

first:0>watch name
"OK"
first:0>set name 1
"OK"
first:0>MULTI
"OK"
first:0>set name 2
"QUEUED"
first:0>set gender 1
"QUEUED"
first:0>EXEC
(nil)
first:0>get gender
(nil)

比如上面的代码中:

  1. watch name开启了对name这个key的监控
  2. 修改name的值
  3. 开启事务a
  4. 在事务a中设置了namegender的值
  5. 使用EXEC命令进提交事务
  6. 使用命令get gender发现不存在,即事务a没有执行

使用UNWATCH可以取消WATCH命令对key的监控,所有监控锁将会被取消。

持久化机制

持久化就是把内存的数据写到磁盘中,防止服务宕机导致内存数据丢失。

Redis支持两种方式的持久化,一种是RDB的方式,一种是AOF的方式。前者会根据指定的规则定时将内存中的数据存储在硬盘上,而后者在每次执行完命令后将命令记录下来。一般将两者结合使用。

RDB方式

RDB是 Redis 默认的持久化方案。RDB持久化时会将内存中的数据写入到磁盘中,在指定目录下生成一个dump.rdb文件。Redis 重启会加载dump.rdb文件恢复数据。

bgsave是主流的触发 RDB 持久化的方式,执行过程如下:

Redis中必须要掌握的20个问题,快来收藏吧!!

接下来看一下 AOF 持久化执行流程:

Redis中必须要掌握的20个问题,快来收藏吧!!

主从复制的原理?

  • 当启动一个从节点时,它会发送一个 PSYNC 命令给主节点;

  • 如果是从节点初次连接到主节点,那么会触发一次全量复制。此时主节点会启动一个后台线程,开始生成一份 RDB 快照文件;

  • 同时还会将从客户端 client 新收到的所有写命令缓存在内存中。RDB 文件生成完毕后, 主节点会将RDB文件发送给从节点,从节点会先将RDB文件写入本地磁盘,然后再从本地磁盘加载到内存中

  • 接着主节点会将内存中缓存的写命令发送到从节点,从节点同步这些数据;

  • 如果从节点跟主节点之间网络出现故障,连接断开了,会自动重连,连接之后主节点仅会将部分缺失的数据同步给从节点。

哨兵Sentinel

主从复制存在不能自动故障转移、达不到高可用的问题。哨兵模式解决了这些问题。通过哨兵机制可以自动切换主从节点。

客户端连接Redis的时候,先连接哨兵,哨兵会告诉客户端Redis主节点的地址,然后客户端连接上Redis并进行后续的操作。当主节点宕机的时候,哨兵监测到主节点宕机,会重新推选出某个表现良好的从节点成为新的主节点,然后通过发布订阅模式通知其他的从服务器,让它们切换主机。

Redis中必须要掌握的20个问题,快来收藏吧!!

pipeline的作用?

redis客户端执行一条命令分4个过程:发送命令、命令排队、命令执行、返回结果。使用pipeline可以批量请求,批量返回结果,执行速度比逐条执行要快。

使用pipeline组装的命令个数不能太多,不然数据量过大,增加客户端的等待时间,还可能造成网络阻塞,可以将大量命令的拆分多个小的pipeline命令完成。

原生批命令(mset和mget)与pipeline对比:

  • 原生批命令是原子性,pipeline非原子性。pipeline命令中途异常退出,之前执行成功的命令不会回滚

  • 原生批命令只有一个命令,但pipeline支持多命令

LUA脚本

Redis 通过 LUA 脚本创建具有原子性的命令:当lua脚本命令正在运行的时候,不会有其他脚本或 Redis 命令被执行,实现组合命令的原子操作。

在Redis中执行Lua脚本有两种方法:evalevalshaeval命令使用内置的 Lua 解释器,对 Lua 脚本进行求值。

//第一个参数是lua脚本,第二个参数是键名参数个数,剩下的是键名参数和附加参数
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

lua脚本作用

1、Lua脚本在Redis中是原子执行的,执行过程中间不会插入其他命令。

2、Lua脚本可以将多条命令一次性打包,有效地减少网络开销。

应用场景

举例:限制接口访问频率。

在Redis维护一个接口访问次数的键值对,key是接口名称,value是访问次数。每次访问接口时,会执行以下操作:

  • 通过aop拦截接口的请求,对接口请求进行计数,每次进来一个请求,相应的接口访问次数count加1,存入redis。
  • 如果是第一次请求,则会设置count=1,并设置过期时间。因为这里set()expire()组合操作不是原子操作,所以引入lua脚本,实现原子操作,避免并发访问问题。
  • 如果给定时间范围内超过最大访问次数,则会抛出异常。
private String buildLuaScript() {
    return "local c" +
        "\nc = redis.call('get',KEYS[1])" +
        "\nif c and tonumber(c) > tonumber(ARGV[1]) then" +
        "\nreturn c;" +
        "\nend" +
        "\nc = redis.call('incr',KEYS[1])" +
        "\nif tonumber(c) == 1 then" +
        "\nredis.call('expire',KEYS[1],ARGV[2])" +
        "\nend" +
        "\nreturn c;";
}

String luaScript = buildLuaScript();
RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);
Number count = redisTemplate.execute(redisScript, keys, limit.count(), limit.period());

PS:这种接口限流的实现方式比较简单,问题也比较多,一般不会使用,接口限流用的比较多的是令牌桶算法和漏桶算法。

更多编程相关知识,请访问:编程入门!!

以上就是Redis中必须要掌握的20个问题,快来收藏吧!!的详细内容,更多请关注web开发者其它相关文章!

本文链接:https://www.webkfz.com/sjk/sjkzh/1rst.html

版权声明:站内所有文章皆来自网络转载,只供分享作用,不代表本站的观点!

发表评论 共有 0 条评论)

联系客服
网站客服 业务合作 QQ
1244305267
公众号
公众号
公众号
返回顶部