redis复习1-5题

1. redis和memcached有什么区别?redis的线程模型是什么?为什么单线程的redis还能支持高并发?

	1. 区别
		1. redis拥有更多的数据结构,应用场景更多,功能更强大
		2. memcached没有原生的集群模式,redis是官方原生支持cluster
	2. 线程模型
		1.  Redis线程模型是通过非阻塞I/O多路复用模型轮询监听多个socket请求,并把请求压入队列,然后交由文件事件分派器分派给相应的文件事件处理器进行处理,整个过程只在调用select、poll、epoll这些函数的时候才会阻塞,收发客户消息是不会阻塞的,可以充分利用整个线程;
		2. 其中Redis命令是交给命令请求处理器然后在内存中操作的,而内存操作非常快;
		3. 同时由于文件事件分派器是单线程的,所以整个Redis也是单线程的,这样就避免了多线程频繁上下文切换问题
	3. redis与客户端通信
		1. redis进程中的serversocket会监听客户端socket的请求,比如刚开始的时候,客户端就会发送一个连接请求,redis进程监听到这个AE_READABLE事件之后,通过IO多路复用程序将这个事件压到一个队列中,然后文件事件分派器就回去队列中读取这个连接事件,并找到对应的连接应答器,而如果之后客户端socket要想进行比如set k v这样的操作,也还是一样redis进程监听事件,通过IO多路复用程序,将事件压到队列,文件事件分派器去队列取事件分派到对应的处理器,命令请求处理器或者名利回复处理器。
		2. 然后其他的客户端想和redis进行连接的话,就会形成多个socket去和redis进行连接,但是都是通过一个io多路复用程序,一个队列,一个文件事件分派器,因为文件事件分派器是单线程,所以是一个线程去处理多个客户端的事件,这样就是一个redis的通信过程
	4. redis单线程的高效率
		1. IO多路复用程序监听多个客户端socket请求,它不处理请求,只对请求干两件事:监听+压队列,所以它本身是一个非阻塞的IO多路复用模型
		2. 真正处理请求都是在各种命令处理器,他们的处理操作都是基于纯内存处理,很快
		3. 单线程避免了多线程的频繁上下文切换问题

2. redis都有哪些数据类型?分别在那些场景下使用比较合适?

	1. string:就是普通的set和get,做简单的kv缓存
	2. hash:可以将对象缓存在redis中,对象不能有其他对象引用,key=15,value={“id”:150,"name":“sam”},后续操作的时候,可以直接仅仅修改这个对象中的某个字段的值
	3. list,有序列表,粉丝列表:key:某大v,value={粉丝1,粉丝2...},lrange可以从某个元素开始读取多少个元素,基于list做分页查询,做消息队列
	4. set,无序集合,自动去重(系统部署在多台机器上,基于redis进行全局的set去重),基于set做交集,并集的操作,比如两个人的粉丝列表整一个交集,看看两个人的共同好友
	5. sorted set,去重排序,排行榜:将每个用户以及对应分数写好,zadd board score username,接着zrevrange board0 99,就可以获取排名前100的用户,zrank board username,可以看到用户在排行榜里面的排名

3. redis持久化

	1. RDB(默认) 是 Redis DataBase 的缩写。按照一定的时间周期策略把内存的数据以快照的形式保存到硬盘的二进制文件。即 Snapshot 快照存储,对应产生的数据文件为 dump.rdb,通过配置文件中的 save 参数来定义快照的周期。核心函数:rdbSave(生成 RDB 文件)和 rdbLoad(从文件加载内存)两个函数。
	2. AOF 是 Append-only file 的缩写。Redis会将每一个收到的写命令都通过 Write 函数追加到文件最后,类似于 MySQL 的 binlog。当 Redis 重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。每当执行服务器(定时)任务或者函数时,flushAppendOnlyFile 函数都会被调用, 这个函数执行以下两个工作:
		- WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件;
		- SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
	3.  AOF 文件比 RDB 更新频率高,优先使用 AOF 还原数据;AOF比 RDB 更安全也更大;RDB 性能比 AOF 好;如果两个都配了优先加载 AOF。
	4. RDB 的 bgsave 大致流程。copy-on-write。触发RDB 的 save时机。
		1. Redis主进程首先会fork一个子进程。子进程开始遍历整个数据集并将数据写入到一个临时的RDB文件中。当子进程完成RDB文件的写入后,Redis会用这个新生成的RDB文件替换旧的RDB文件。在此过程中,主进程可以继续处理客户端的请求,不会被阻塞。
		2. 当Redis进程fork一个子进程时,操作系统不会立即为子进程复制一份父进程的内存,而是采用COW策略。当父进程或子进程试图修改某个内存页面时,操作系统才会为修改的页面创建一个副本,以确保每个进程有自己的独立页面。在bgsave的过程中,主进程可能会修改数据。由于COW,这些修改不会影响子进程正在写入RDB文件的数据。但这意味着,如果有大量的写操作,将导致大量的页面被复制,可能会导致系统内存的增加。
		3. - **手动触发**:可以通过Redis的`SAVE`或`BGSAVE`命令手动触发。**配置触发**:在`redis.conf`文件中,可以通过配置`save`指令来定义持久化的触发条件,例如:`save 900 1` 表示在900秒(15分钟)内如果超过1个key被修改,则自动触发bgsave。 **重启时**:当Redis重新启动时,它会自动加载RDB文件来恢复数据。 **主从同步**:当一个Redis实例成为另一个实例的从服务器(slave)时,主服务器(master)可能会触发一个bgsave操作,以便将数据发送到从服务器。

4. redis实现分布式锁

	1. 使用setnx来加锁。key是锁的唯一标识,按业务来决定命名,value这里设置为test。当一个线程执行setnx返回1,说明key原本不存在,该线程成功得到了锁;当一个线程执行setnx返回0,说明key已经存在,该线程抢锁失败;
	2. 有加锁就得有解锁。当得到的锁的线程执行完任务,需要释放锁,以便其他线程可以进入。释放锁的最简单方式就是执行del指令。释放锁之后,其他线程就可以继续执行setnx命令来获得锁。
	3. 锁超时知道的是:如果一个得到锁的线程在执行任务的过程中挂掉,来不及显式地释放锁,这块资源将会永远被锁住,别的线程别想进来。所以,setnx的key必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一段时间后自动释放。setnx不支持超时参数,所以需要额外指令,expire key 30
	4. 上述分布式锁存在的问题
		1. ** SETNX 和 EXPIRE 非原子性 **假设一个场景中,某一个线程刚执行setnx,成功得到了锁。此时setnx刚执行成功,还未来得及执行expire命令,节点就挂掉了。此时这把锁就没有设置过期时间,别的线程就再也无法获得该锁。SET key value - EX second: 设置键的过期时间为second秒;PX millisecond:设置键的过期时间为millisecond毫秒;NX:只在键不存在时,才对键进行设置操作;XX:只在键已经存在时,才对键进行设置操作;SET操作完成时,返回OK,否则返回nil。
		2. **锁误解除**如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。在del释放锁之前加一个判断,验证当前的锁是不是自己加的锁。具体在加锁的时候把当前线程的id当做value,可生成一个 UUID 标识当前线程,在删除之前验证key对应的value是不是自己线程的id。还可以使用 lua 脚本做验证标识和解锁操作。
		3. **超时解锁导致并发**如果线程 A 成功获取锁并设置过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁,线程 A 和线程 B 并发执行。A、B 两个线程发生并发显然是不被允许的,一般有两种方式解决该问题:将过期时间设置足够长,确保代码逻辑在锁释放之前能够执行完成。为获取锁的线程增加守护线程,为将要过期但未释放的锁增加有效时间。
		4. **不可重入**当线程在持有锁的情况下再次请求加锁,如果一个锁支持一个线程多次加锁,那么这个锁就是可重入的。如果一个不可重入锁被再次加锁,由于该锁已经被持有,再次加锁会失败。Redis 可通过对锁进行重入计数,加锁时加 1,解锁时减 1,当计数归 0 时释放锁。
		5. **无法等待锁释放**上述命令执行都是立即返回的,如果客户端可以等待锁释放就无法使用。可以通过客户端轮询的方式解决该问题,当未获取到锁时,等待一段时间重新获取锁,直到成功获取锁或等待超时。这种方式比较消耗服务器资源,当并发量比较大时,会影响服务器的效率。另一种方式是使用 Redis 的发布订阅功能,当获取锁失败时,订阅锁释放消息,获取锁成功后释放时,发送锁释放消息。

5. redis实现消息队列

	1. rpush list1 aaa;rpush list1 bbb;rpush list1 ccc
	2. lpop list1 第一次aaa 第二次bbb 第三次ccc
	3. 缺点:没有等待队列里有值,就直接消费;在应用层引入sleep进行阻塞控制然后调用lpop
	4. 可以这样:blpop list1 30,过30秒之后还没有值的话才返回nil,30s之内添加新值就会返回
	5. 一个发布者发布主题消息(publish topic “aaa”),多个订阅者监听订阅主题消息(subscribe topic),发布订阅模式:消息的发布是无状态的,无法保证可达,如果一个订阅者下线了,再次上线之后没办法接收到消息
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值