关于redis

来源:互联网 发布:多台docker php-fpm 编辑:程序博客网 时间:2024/05/20 08:26

一、干什么用的:

1、用于kv存储:数据实际存储在内存里,比起mysql存在磁盘里,读写都更加快速

优点:比起那些存在磁盘里的,读写肯定更快(确切的说应该是redis自身的读写操作特别快,但实际使用中,redis以另一进程存在,则如计入网络开销就不好说了,典型如rocksdb等进程内数据库而且还基于lsm-tree的另论,尤其对于写)

缺点:数据存在内存里,内存能用多大呢,大规模的数据不宜存在redis

2、同样是kv存储,redis能持久化:这个主要是针对同样作为kv缓存的memcached,memcached无法持久化,进程结束后数据就丢失。而redis可以选择是否持久化落盘,进程结束后再次启动可以重新加载到内存。

优点:可以持久化

缺点:开启持久化影响了读写的性能

redis有两种持久化方式:

2.1、rdb:定时的生成当前数据快照,定时时间在配置文件内设置,如默认每5分钟生成一次快照。

优点:其实现方式是fork一个子进程去做快照生成,对正常读写影响小;生成的rdb二进制文件小,启动加载快

缺点:在定时间隔之间如果redis进程意外退出,则上一次生成快照到退出之间的数据就丢失了

2.2、aof:类似mysql的binlog,它是把定时间隔内的操作命令记录下来,定时时间在配置文件里也可以配置默认是1秒

优点:基本不会丢数据了,如按默认最多只丢最后1秒以内的数据;生成的文件里边是命令,易读

缺点:对正常读写影响大于rdb方式;相比rdb的二进制快照数据文件,aof的命令文件随运行时间推移越来越大,启动加载慢

3、使用场景基本结论:

不适宜用于存储特大量数据,适宜存储一些实时性强的(需要快速读写)、重要性高的(需要持久化)、数据量有限的(视情况不能占内存太大比例)的数据

4、还有什么特点或者功能:

1、支持的数据结构多:从业务功能看,除了最简单的KV即字典(dict)外,还支持链表型(list、hset),数组型(smember)、有序数组型(zset),这是memcached所不具备的;

      利用这些东西,首先可以支持一些场景,如简单的分布式消息队列、发布订阅模式等,另外可以让很多业务需求的实现变的更形象清晰;需要注意上述业务功能的数据结构,所对应的实际数据结构(接下来描述);

主要是,分布式消息队列,顺带包括发布订阅功能:如果是kafka属于重型完整分布式消息队列,那么redis就是轻型简装版的,使用方式为:

1.1、通过发布订阅:一个发布者,1-N个订阅者:

订阅者命令"subscribe XXX",如"subscribe msgque1"

发布者命令"publish XXX message",如"publish msgque1 helloworld"

那么所有订阅者将收到"message"、"msgque1"、"helloworld"

很简单,但是有个问题:发布一个消息,所有订阅者都收到,如果发布的很快,需要多个订阅者分担消费以加速时,redis不支持

结论:不实用,实际场景往往是生产者速度很快需要多个消费者满足生产消费平衡

1.2、通过list:改进了1.1的问题,可以由多个消费者加快消费速度了

生产者lpush,消费者brpop

但不论是1.1的方式还是1.2的方式,redis并不是专业的消息队列,体现于如下:

1、消息持久化:无论是rdb还是aof,不能100%保证一条消息不丢,如果不能容忍就不能用了

2、缺乏专业消息队列的ack机制(也就是说无状态),消息被从redis取出后就没了,如果消费端发生某些问题想重新取,就取不到了

简单场景用下还是可以的,但简单场景用生产者消费者的多线程+队列,岂不还更简单了

2、事务的支持:支持,执行"multi"后,依次执行"set a b" 、"set b c"、"exec",会返回"OK"、"OK"

    redis的事务功能实际是由redis内部的队列封装实现;

    在执行事务内的命令时不会执行其他命令,有很好的隔离性;

    即便事务处理期间进程退出,也能保证数据的一致性;很明显随其持久化功能,还具有持久性

    但也有缺点:

    1、如果事务中,比如"set a b",然后需要走一段业务逻辑,这段业务逻辑依赖于redis内key为a的value,也就是说需要请求"get a",然后用这个value进行自己的业务逻辑,再去写"set c XXX",这个XXX是和前面的a的value有关,如:

执行"multi",然后"set a b",然后进行业务逻辑,业务逻辑中需求执行"get a",然后业务逻辑用这个"get a"的结果XXX去做相关的"set c XXX",然而实际结果是:"OK"、"QUEUED"、"OK",也就是说get a的结果是"QUEUED"......结论:redis的事务里,如果后面的set命令依赖于前面的get命令的结果,这是不支持的。

    2、multi命令后,事务里每一个命令,会依次网络请求redis服务器一次,而不是打包一次性请求。这个不属于问题属于缺陷。

3、数据淘汰机制:包括熟悉的基于时间的TTL方式,还有基于内存大小的LRU方式

TTL:由定时器事件完成

LRU:LRU本身是什么都知道(可参考LRUCache),每次执行写入相关命令时,检查当前使用内存是否超过配置文件里的阈值(maxmemory),如超过,则选择一些LRU链表尾的数据,保存在硬盘里,注意不是删除掉!这部分细节较复杂可参考深入剖析redis数据淘汰机制

TTL和LRU的设计目的就不一样,TTL为业务而生,用于业务性质需求的删除过期数据,而LRU机制实际上是实现了redis可以存储超过物理内存限制的数据量

4、数据分片或集群:

个人了解至少包括:配置redis主从、按hash分片(典型为使用twemproxy)、redis 3.0的集群机制

4.1、主从:方式近似mysql的主从。

redis的任何一个实例可同时作为主机和从机,即redis的主从配置,可以形成一种树状结构(或所谓有向无环图DAG);应该是便于容灾

和mysql的主从一样,redis的主从也是,主机往往只用于写,从机用于读,主机从机通过binlog方式同步。具体实现和上面的aof的机制一样。

用途也和mysql的主从一样,适合非强一致性要求的场景,适合只需要满足最终一致性的场景。

细节较复杂可参考redis主从配置细节

4.2、按hash分片:就是按hash分片

按hash分片,每个redis节点都是平级的,可以自己编程实现,更典型方式是通过twitter的twemproxy,输入全部redis实例地址和proxy地址和选用哪个hash即可,非常简单即可搭建实现。

优点:简单

缺点:无法热增加新redis实例;高并发时proxy单点压力太大

4.3、redis 3.0的机制

没用过redis 3.0

结论:个人感觉日常使用的话,前两种应该够了。

5、hiredis及其坑:

hiredis是redis的c客户端驱动,用过很久了,基本结论是:同一进程的访问redis还是交给单例去做吧,或者每个线程各自持有句柄,不要多线程用一个句柄去访问。hiredis的坑这is篇文章讲的比较细。

另外hiredis支持二进制数据读写,即支持存在\0的流写入(如PB生成的二进制),这是对数据压缩传输存储的优良方式,一定用这个不要自己瞎搞。

6、redis vs memcached

这是面试常见问题:

相同点:都是内存型,均通过tcp网络请求,自身都很快,所以并发都很厉害;均不适合特别大的数据、100%的一条不能丢的数据的存储

差异:

1、支持数据结构:

memcached:仅dict

redis:dict、list、hset、set、sorted set

redis胜

2、总容量和单条容量:

memcached:单进程最大存储2G;具备TTL机制(近似redis并不立即释放内存,只是get时发现过期了不返回value了,都是为了避免内存抖动,提高cache命中率);每个value最大1M

redis:可突破物理内存限制(LRU机制,多出来的数据落盘);具备TTL机制;每个value最大512M(但redis是单线程IO模型,总搞很大的IO会加剧阻塞影响并发量)

redis胜

3、持久化:

memcached:不支持

redis:支持,包括rdb和aof两种方式

redis胜

4、事务:

memcached:有专门的CAS命令,支持并发下操作同一个数据的强一致性

redis:无专门CAS命令,支持简单事务(同一连串命令依次进行不会被打断,但如果命令之间有get命令且get命令后边的set命令依赖于get命令的结果,则无法支持了,因为get命令获得的结果不是对应的value,而是"QUEUED"......)

memcached胜

5、网络IO模型:

memcached:基于libevent、主线程监听、多个子线程负责与客户端的IO的模型,多核下这种多线程模型,读写速度高于redis

redis:单线程模型,自然无法利用多核,遇到某些耗时操作(如flush all)会等这个耗时操作完成再做下一个操作,比较影响并发(包括redis不应该总读写很大的value);但单线程模型也不存在memcached多线程的一致性同步的开销;

6、分布式集群:

memcached:只支持用户自己按hash分片

redis:有典型的主从同步(同AOF方式,binlog)、也支持按hash分片、redis 3.0的机制

redis胜


5、redis各部分数据结构背后的数据结构:

5.1、字典:所谓的"set a b",对应hash。redis支持多个隔离的数据集(select 0那个,近似mysql里的database概念),每个数据集有一个hash表,用于"set a b"这样;

   既然是hash表,那就肯定有可能会扩容,这里有一个redis的重要细节知识,面试中有时遇到,即redis是怎么处理rehash的:

   redis是单线程模型,也就是说不宜某个操作特别长导致一直阻塞着,所以如果在set时发现需要rehash了,redis并没有在set操作里直接去做rehash,而是分配新的hash表的空间,本次set操作的是新空间,但并不立即就把旧hash表已存储数据搬到新hash表,而是以后再操作老hash已存在的数据时,就把该条数据从老hash表搬到新hash表,直到最终老hash表的数据都搬过去。

   也就是说本该一次性完成的rehash,实际上是慢慢逐渐完成的。细节可参考redis数据结构dict

5.2、list:所谓的"lpush"、"brpop",对应普通双向链表;

5.3、hlist:所谓的"hset a b c"、"hget a b",注意并非由hash实现,而是对应一种特殊双向链表(压缩双链表,ziplist),这种双向链表为了节省空间,连普通双向链表的prev、next两个指针都省了(即每个节点都省去8字节),改成用位域的方式,细节可参考redis数据结构ziplist;

5.4、set:所谓的"sadd key value1"、"smember key",如果value中都是整数,对应的是有序数组(iniset,差不多二叉排序树),读写是二分查找实现对数时间复杂度;如果value不完全是整数,那么对应的是dict实现,即一个key对应一个hashmap;

5.5、sorted set:所谓的"zadd zkey 1.0 value1"、"zrange zkey 0 -1",对应是跳表(可参考跳表)。业务角度来说zset是redis里很好用的一个东西。

redis数据结构是从redis源码角度看redis的功能的一篇优质文章。

原创粉丝点击