redis知识总结

redis

redis事务

事务对于关系型数据库很重要的功能。比如redis不支持新建一个带有过期时间的key,如果用redis实现分布式锁,一般就是设置一个key,然后设置过期时间,但是这个过程又不能保证事务性。
redis也提供了事务的功能,但是跟平常理解的事务又有点不太一样。
redis的事务只能保证要么全部不执行,要么全部都执行,而不会在某条命令出错之后,把之前的命令回滚掉,取而代之的是继续执行下去。

watch命令

watch可以监视redis中键的修改情况,配合事务使用。事务开始之前执行watch命令,监视一个键,如果键在redis客户端的exec命令提交之前被其他客户端修改了,那么该客户端的redis_dirty_cas标志位就会被打开。
当向服务器发送exec命令时,服务器会拒绝执行这个事务。一般还需要在事务执行完毕后,关闭对key的监视

原子性
redis保证事务要么一个都不执行,要么全部执行。具有原子性。但是不会像mysql一样,在执行错误后,回滚掉之前的操作。redis的设计者认为,命令执行出错是程序问题,不应该出现在生产环境中。回滚操作与redis简单高效的设计理念是不相符的。

一致性
redis也可以保证一致性。如果提交的事务,在入队阶段就失败了,比如提交了不存在的命令,那么事务会被拒绝执行。
执行事务的阶段也可能出错,最常见的原因就是对键执行了不支持的命令。这些出错命令不会对redis产生任何影响。

隔离性
redis使用单线程来执行事务,并且保证执行过程中,不会被中断。所以总是以串行的方式执行事务中的命令。

redis面试

一篇不错的博客link

pipeline

redis官方文档link 翻译过来就是:

  1. 执行命令的典型的过程就是,服务端通过socket发送命令,阻塞,服务端响应,客户端继续执行。即使执行命令的过程很快,但是也会有网络开销。
  2. pipeline是一种很老的技术了。客户端可以发送多个命令到服务器,而无需等待回复,只需在最后读取回复即可。
  3. 当客户端使用流水线发送命令时,服务器将被迫使用内存对答复进行排队;如果命令太多,可能会占用很多内存,redis建议不超过1W。
  4. 其实通过pipeline能降低的不仅是网络开销。执行socket IO是比较慢的,这涉及调用read()和write()系统调用,这意味着从用户态到内核态的转换,上下文切换是一个巨大的速度惩罚。
    当使用pipeline时,许多命令通常通过单个read()系统调用来读取,并且通过一次write()系统调用来传递多个响应。
redis对象 (redisObject的数据结构)
  1. redis中有的命令是对所有的类型的键通用的,DEL,expire,rename。还有一种命令只能对特定类型的键执行:set,get等。
  2. 同一种类型的对象又有不同的实现方式,比如列表,有ziplist和linkedlist,当执行llen命令时,会根据具体的存储类型选择不同的实现方式。
  3. redis使用引用计数来回收内存。
  4. 值为整数的字符串对象,会被共享。其他复杂的对象不会被共享,因为判断对象是否相等的成本太高。
  5. 上面的操作,通过redisobject对象的type,encoding,ptr和refcount几个属性实现。
  6. redisobject的lru属性记录对象最后一次被访问的时间,可用来统计对象的空转时间。过期策略那里会被用到。
处理过期键

对于有过期时间的键,redis专门有expire字典存储这些键。过期字典对应的值,就是键的过期时间。专门起一个过期字典,是为了定期删除策略的存在。

redis同时采用两种方法,惰性删除和定期删除。
惰性删除:访问一个键的时候,检查这个键有没有过期,如果过期把键删除掉。返回空回复给客户端。这种方式对内存不友好。
定期删除:启动定时线程,访问数据库的过期键字典,随机选取数据库键进行检查,发现过期就删掉。执行一段时间后,终止掉。下次接着执行。

当redis是主动架构的时候,如果客户端访问的是从库,从库发现数据库键过期,也不会把键删除,而是把相应的值取出来返回给客户端。这么做是为了保持
主从数据库的一致性。

只有当客户端访问主库,主库发现键已经过期,就会把过期键删掉,同时给从库发送命令删除这个键。这时再访问从库,也不会访问到这个过期的键了。

rdb && aof(append only file)

redis是内存型的数据库,一旦服务器宕机,数据就不见了,当然这对内存数据库是可以容忍的。除非你在redis中存储的是原数据本身,而不是数据副本。
针对这个问题,redis提供了两种方式解决就是rdb和aof。

  1. rdb

rdb直译过来就是redis database,保存redis数据库中所有的键值对。当且仅当redis数据库重启的时候,会从rdb中读取内容,恢复数据。rdb就是已一定格式保存redis键值对的文件。

问题1:这个文件什么时候更新?
当我们调用save或者bgsave命令的时候,会生成rdb文件。save:会阻塞服务器,生成rdb文件期间,不能响应客户端请求。一般来说对于互联网应用是不能容忍的。
[bgsave]:服务器新起子线程来完成这项工作,这是服务器可正常接受响应。

实际工作过程中,应该要以定时任务的方式来做这项工作,不能靠人工输入命令来保证。一般的解决方案就是,1.要么定期执行,2.要么服务器产生变更之后,自动执行。

自动间隔性保存
向服务器提供配置:save 300 10,表示服务器在300秒之内,至少进行10次修改,bgsave命令就要被执行了。采用这种方式要求服务器记录两个属性:
dirtylastsave。dirty是上次bgsave以来,执行了多少次修改数据库命令;lastsave是上次执行bgsave的时间。为什么要记录dirty参数呢?
当服务器没有变更的时候, 显然是不需要重新执行保存命令了。

还有个问题没想明白? 执行bgsave命令期间,客户端又对数据库产生了新的变更,那么这个新的变更,怎么被保存下来。

  1. aof

看到名字,第一印象想到的竟然是[append of file],惭愧。

相比于rdb的实现方式,aof记录的是更改数据库的命令,类似于数据库的binlog。实时性比较强。
aof就是以一种append的方式,不断的向文件末尾添加redis命令,更新文件速度还是比较快的。我们知道所有更改文件的操作,由于操作系统提供的文件缓冲功能,一般不会直接落盘
所以常见都是有三种解决方案:

  1. 执行完一次命令后,强制刷盘。最大程度保证数据的可靠性,最多只会丢失一条命令而已,但是频繁刷盘,性能较低。
  2. 定时刷盘。比如一秒钟刷盘一次。折衷方案。
  3. 全部依赖操作系统。这种情况下,当文件缓冲区满了之后,由操作系统自动刷盘。隔的时间最长,可靠性最差。

aof的问题:随着时间流逝,aof文件内容越来越多,a文件也越来越大。因为可能会对一个键产生很多次操作,aof要记录很多条命令,其实很多情况下都是浪费的。
可以直接读取数据库当前的状态,针对一个键生成一条命令来大量减少aof文件的大小。

aof后台重写功能:类似于bgsave后台生成rdb文件一样,也可以通过读取数据库状态,后台生成aof文件。

看下aof如何解决刚才说的bgsave命令时,产生新变更的问题。当执行aof重写时,服务器会新开一个aof重写缓冲区,redis服务器执行完一个命令后,它会同时将命令发送给这个缓冲区。
当子进程将数据库的内容读取完之后,再将aof重写缓冲区的内容读入新的aof文件。这一切做完之后,通过rename操作将重写之后的aof文件,命名为正式的aof文件。这样就完成了新旧文件的替换。

注意aof重写的时候,原来的aof文件还是继续更新的。在aof重写完成后,旧的aof文件就不起作用了。

事件模型

redis是基于Reactor模式开发的网络事件处理器。redis中的事件分为两类:文件时间和时间事件。

文件事件:是对套接字操作的抽象,包括服务端于客户端的连接,通信,以及主从服务器的复制等。redis是单线程的服务器,通过多路复用技术,支持多个连接。
redis也为每一类事件绑定了一类处理器,当事件发生时,提交给处理器处理。

时间事件:定时事件和周期事件(目前只有周期事件)。redis所有的时间事件保存在一个无序链表里,要遍历完整个链表才能拿到最快到期的事件。周期事件现在由serverCron负责:
更新服务器状态、清理过期键、尝试持久化操作、主从同步、集群间通信等。

事件处理过程:服务器对文件事件和时间事件的处理都是同步的、原子的执行,不会出现抢占或中断当前事件的情况。服务执行过程中,就是在一个循环中不断的处理文件事件和时间事件。如果时间事件没有到期,
那么会接着处理文件事件。[对于比较费时的时间事件,redis通常采用子线程或者子进程来执行]

猜你喜欢

转载自blog.csdn.net/liang0000zai/article/details/80374902