Redis 笔记

Redis 是什么?
远程字典服务器(Remote Dictionary Server), Redis并不是简单的key-value存储,实际上是一个数据结构服务器,支持不同类型的值。

Redis与其他key-value存储有什么不同?
Redis有着更为复杂的数据结构并且提供对他们的原子性操作。Redis的数据类型都是基于基本数据结构的同时对程序员透明,无需进行额外的抽象。
Redis运行在内存中,也可以持久化到磁盘,所以在对不同数据集进行高速读写时需要权衡内存空间。相比在磁盘上操作复杂的数据结构,在内存中操作起来非常简单,这样Redis可以做很多内部复杂度很强的事情。同时,在持久化到磁盘时,它是紧凑的以追加的方式写入的,因为他们并不需要进行随机访问。

Redis的内存占用情况怎么样?
Redis会记录键值对的时候,会对类型信息引用计数等,以缺少数据的重复存储。


如果不希望使用一致性哈希或其他方式将数据集分布在不同的节点,还能采用Redis吗?
一个可行的方案是同时使用传统数据库和Redis一起协作,Redis里面存放状态信息(元数据,小但经常写的信息),和所有其他读写频繁的数据,使用Redis List 存放与时间顺序有关的id列表、编码等等。然后,使用MySQL作为存储引擎来存放更大的数据。


有哪些办法可以降低Redis的内存占用?
如果你使用的是32位的Redis实例,可以好好利用Hash,list,sorted set,set等集合类型数据,因为通常情况下很多小的Key-Value可以用更紧凑的方式存放到一起。

Redis的内存用完了会发生什么?
INFO命令可以查看Redis的使用量,因此编写一个监视Redis服务器状态的临界监控脚本以检查服务器的状态。 或者可以在配置文件中使用“maxmemory”配置最大可用内存,当达到上限时Redis的写入命令会返回错误信息。又或者你可以将Redis当缓存来使用配置淘汰机制,当Redis达到内存上限时会冲刷掉旧的内容。


Redis是单线程的,我怎么提高多核CPU的利用率?
CPU不太可能是Redis的瓶颈,一般内存和网络才是。使用O(N) 或者O(log(N)) 的命令,他们几乎不需要使用什么CPU。
为了最大限度的使用CPU,可以在同一个服务器部署多个Redis的实例,并把他们当作不同的服务器来使用。如果你想使用多个台服务器,可以考虑分片。


键名(Keys)
key值是二进制安全的,可以用任何二进制序列作为key值,空字符串也是有效key值。键名大长太短都不好,最好坚持一种模式:object-type:id:field

字符串(Strings)
是一种最基本的类型。Redis字符串是二进制安全的。一个字符串的值最多能存储512M字节的内容。

链表(Lists ): 按插入顺序排序的字符串元素的集合。结构:push key value1, push key value2...{a,b,c}
时间复杂度: O(N) ,链表的主要的特性就是支持时间常数的插入和靠近头尾部元素的删除,所以访问列表两端的元素是非常快的。
应用场景:消息队列、时间轴,固定长度的列表

哈希(Hashes):是字符串字段和字符串值之间的映射,所以它可以完美的表示对象。
时间复杂度: O(1)

集合(Sets): 不重复且无序的字符串元素的集合。
时间复杂度: O(1)
应用场景:统计独立IP,随机地获取元素。


有序集合(Sorted sets):集合中的每个字符串元素都关联到一个叫score浮点数进行排序。
时间复杂度: O(log(N))
应用场景:排行榜


HyperLogLog:是一种基于概率的数据结构,用于统计集合中不同元素的个数,运算时所需内存大小与集合大小成正比。有一种精度换取空间的做法:没有真的将元素加到集合中,而只是保存了一个标识位,但得到的结果会有一定误差。对于reids来说,这个误差小于1%,而优点是无论集合中有多少元素,最多需要12k的内存。


BitMap:通过一个bit位来表示某个元素某个位置上对应的二进制值,其中位置介于0到2^32之间。命令:SetBit key offset value。每当有更大的偏移量,空白位置以0填充。


分区目的:使Redis的内存空间和计算能力通过简单地增加计算机得到成倍提升。
分区过程:将数据分布到多个redis实例,分区是将你的数据分发到不同redis实例上的一个过程,每个redis实例只是你所有key的一个子集。

分区方式:
范围分区:定义不同Redis实例能够存放的数据范围。
散列分区:通过计算得到一个redis实例的落点,常见计算方式如:一致性哈希。


不同的分区实现方案:分区可以在程序的不同层次实现。

客户端分区:在客户端就已经决定数据会被存储到哪个redis节点或者从哪个redis节点读取。支持一致性哈希的客户端:Redis-rb 和 Predis
代理分区:意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis实例,然后把Redis的响应结果返回给客户端。有一种代理实现就是Twemproxy。
查询路由(Query routing) :客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个节点转发到另一个节点,而是在客户端的帮助下直接重定向到正确的redis节点。


分区的缺点:有些特性在分区的情况下将受到限制。
1、涉及多个key的操作通常不会被支持。例如:不能直接使用命令对两个集合求交集,因为它们可能被存储到不同的Redis实例,解决办法相对麻烦。
2、同时操作多个key,则不能使用Redis事务。
3、分区使用的粒度是key,所以无法对一个超大的集合中的元素进行分片。
4、数据处理会非常复杂,例如:为了备份你必须从不同的Redis实例和主机同时收集RDB / AOF文件。
6、动态扩容或缩容可能非常复杂。Redis集群在运行时增加或者删除Redis节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。


持久化数据还是缓存?
无论是把Redis当做持久化的数据存储还是当作一个缓存,从分区的角度来看是没有区别的。
一致性哈希能够实现当一个key的首选的节点不可用时切换至其他节点。同样地,如果你增加了一个新节点,立刻就会有新的key被分配至这个新节点。

重要结论如下:
如果Redis被当做缓存使用,使用一致性哈希实现动态扩容缩容。
如果Redis被当做一个持久化存储使用,必须使用固定的keys-to-nodes映射关系,节点的数量一旦确定不能变化。否则的话(即Redis节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis集群可以做到这样。


预分片
除非我们把Redis当做缓存使用,否则增加和删除节点将非常麻烦,但是使用固定的keys-instances则比较简单。为防止以后的扩容,最好的办法就是一开始就启动较多实例。即便你只有一台服务器,你也可以一开始就让Redis以分布式的方式运行,使用分区,在同一台服务器上启动多个实例。这样当你的数据不断增长,需要更多的Redis服务器时,就可以将Redis实例从一台服务迁移到另外一台服务器,而不用考虑重新分区的问题。

使用Redis复制技术,你可以做到极短或者不停机地对用户提供服务:

1、在你新服务器启动一个空Redis实例。
2、把新Redis实例配置为原实例的slave节点。
3、停止你的客户端,更新客户端配置,以便启用新的redis实例。
4、在新Redis实例中执行SLAVEOF NO ONE命令。
5、重启客户端。
6、停止原服务器的Redis实例。


Redis分区实现方案:

Redis 集群
Redis集群是自动分片和高可用的首选方案,它是查询路由和客户端分区的一种混合实现。当Redis集群可用,并且有兼容Redis 集群客户端可用的编程语言,Redis 集群将成为Redis分区的实际标准。

Twemproxy
Twitter维护的缓存代理系统,代理Memcached的ASCII协议和Redis协议。它是单线程程序,使用c语言编写,运行起来非常快。
它支持自动分区,如果其代理的其中一个Redis节点不可用时,会自动将该节点排除,这将改变原来的keys-instances的映射关系,所以你应该仅在把Redis当缓存时使用。Twemproxy本身不存在单点问题,因为你可以启动多个Twemproxy实例,然后让你的客户端去连接任意一个Twemproxy实例。

 

posted @ 2019-04-17 20:29  心随所遇  阅读(206)  评论(0编辑  收藏  举报