redis 简单使用总结

做者:王清培(Plen wang)  沪江Java资深架构师node

最近一段时间与redis接触比较频繁。发现有些东西仍是工做中常常会用到的,本身也花了点时间巩固下。本篇文章主要是以总结性的方式梳理,由于redis的主题很大,任何一个技术点展开都是几篇文章的量。也能够说这篇文章是个概览。linux

1.redis基本数据结构与短结构压缩

了解redis的数据结构有助于了解每种数据结构的优劣势,方便设计合理的cache结构。git

1.1.redis提供5种数据结构

1.STRING:能够存储字符串、浮点型、整型,若是是字符串能够执行字符串操做,若是是浮点型、整型也能够执行加减操做。redis会识别出它的具体类型。github

2.LIST:链表,链表中的每一个NODE包含一个字符串。能够对链表进行两端推入、弹出操做。redis

3.SET:无序集合,不会存在相同的集合元素,集合的交集、并集、差集运算。缓存

4.HASH:键值对的无需散列,增、删、获取键值。服务器

5.ZSET:有序集合,根据一个浮点数分值来排序。数据结构

这几种数据类型用起来场景仍是比较明显的,遇到复杂的cache场景咱们须要结合这几种数据结构一块儿来设计。架构

好比,购物车场景,咱们首先须要两个HASH来存储,第一个HASH是用户与购物车之间的关系,第二个HASH是购物车中的商品列表。app

先经过userId获取到shoppingCartId,而后再经过shoppingCartId就能够获取到用户购物车的ProductIds。而后再经过ProductIds就能够异步读取用户购物车的商品信息。

经过userId获取shoppingCartId,再经过shoppingCartId获取ProductIds,这看似两个步骤能够在一个redis command中执行完成。(借助redis的pipeline管道。)

图1:

1

设计cache的时候,能够稍微综合的考虑下这几种结构,若是没有理想的结构再扩展下,将这几种结构混搭下能够知足复杂的cache结构。有一个平衡点就是过时时间的把握,集合没有办法设置具体item的过时时间,因此具体的数据对象仍是须要设置expire,保证数据的新鲜度,好比,这里的STRING:p100商品。

1.2.压缩表与集合的整数集合编码

redis为列表、集合、散列、有序集合提供了压缩存储的方式,在存储数据很少的状况下用redis提供的短结构, 能够换来极大的存储压缩比。

redis的数据类型对应的短结构实现:list、zset、hash,这三个将使用ziplist,set使用集合整数编码。

默认的存储结构是以entry node 链表来存储数据的。然而,entry node 是一个结构化的存储。就拿list来讲,entry node 先后包含了一个指针,造成双向链表,中间是包含的数据。数据节点包含三个部分,当前字符串所用空间,当前字符串所剩空间,当前字符串的值。

这些松散性的结构带来了必定的存储开销。redis提供了一个ziplist(压缩表)的功能,一旦开启压缩表那么本来用entry node的存储结构将使用序列化的字节序列来存储。这有优点也有劣势,优点就是存储空间少了,劣势就是不灵活了。存储空间少了,不只带来空间利用率高,还有一个大的优点就是执行master\slave repliaction 或者BGSAVE的时候都是有好处,存储或者带宽都会消耗小。

set背后使用hash来保证不会存在重复的能够。当对set使用短结构存储的时候,redis将使用整数集合编码进行存储。

还有一点,若是咱们设置的最大压缩限制超过以后redis将恢复entry node链表的是方式存储。由于,若是你的压缩表数据太多,读取或者修改就会很影响性能。可能须要对整个压缩列表进行解码、编码操做,等等。

2.redis 事务

redis 提供transaction 功能,你可使用事务来处理一些数据一致性要求比较敏感的场景。redis的事务是没有所谓的隔离级别这一说的,性能是才是它追求的目的。事务所包含的全部command,是一个接着一个被执行,这执行结束以前其余客户端的全部请求都被block住。

使用redis事务比较简单,它有一个表示事务开始的命令 multi,而后使用exec提交。

有两点须要注意,在没有执行exec的时候全部在multi以后的命令都不会被执行。默认状况下,redis收到multi命令以后会将用户接下来的提交都暂时性的存放在一块儿queue里,直到接收到exec命令才会去执行queue里的全部命令。还有一点,有些redis客户端为了提升性能,会将multi与exec的全部命令都暂时存在redis客户端本地,而后一次性提交。这其实基于的是redis的pipeline 。

你也能够单独使用pipeline,而不使用multi、exec事务。只是为了减小redis key的传输次数而已。若是不会产生数据一致性问题是能够这么作的。

说到redis事务就不得不提redis事务的性能问题,因此如今结合redis来开发分布式事务来提供性能也是很常见的方案:https://github.com/Plen-wang/redis-lock 供参考。

3.redis两种持久化原理(RDB、AOF)

redis支持两种方式来持久化数据,第一种:snapshotting(镜像或快照)也称RDB、第二种:AOF(append-only file 文件追加)。

RDB:镜像模式就是将某个时间段的全部内存数据直接写入硬盘。

AOF:将执行的写命令增量复制到硬盘里面。

这两种其实就是不一样的侧重,RDB是数据持久化,AOF是命令持久化,数据持久化比较直接,还原的时候直接恢复数据便可。命令持久化恢复的话须要执行一遍命令才行。

redis执行持久化操做取决于两方面:默认是根据持久化配置来执行,还有就是用户手动触发。手动触发有两个命令:

SAVE:会block全部的用户操做,知道持久化结束。

BGSAVE:后台子进程执行,linux中使用fork命令开启一个子进程副本,这个子进程副本与主进程共用一套内存空间,直到主进程或子进程对内存进行修改,被修改以后的内存区域将被子进程复制出来单独使用。

RDB持久化的问题是会存在丢失数据的可能,AOF持久化最多丢失一秒内的命令。因此持久化结合这两种来执行会在数据完整性和性能之间取的平衡。

4.redis 主从复制的基本原理

redis提供复制功能,这有不少好处。容灾、扩展读写性能。

有两个点须要注意:从服务器一旦进行同步数据时会清空本身的全部数据。redis不支持主主复制。

复制过程大体以下:

1.从服务器链接主服务器,发送SYNC命令。

2.主服务器执行BGSAVE,并使用缓存区记录BGSAVE以后执行的全部写命令。

3.BGSAVE执行完毕以后,向从服务器发送快照文件。从节点丢弃全部本实例数据。载入主服务器发送过来的快照数据。

4.快照发送完毕以后开始发送缓存区中的写命令。从节点开始向常规的操做同样执行增量复制。

5.开始进入常规的复制操做。

有个问题须要分享下,redis一旦占用的内存哪怕咱们清楚了key,这部份内存仍是没法还给操做系统的,这是redis的设计策略。虽然从redis info命令中查看的内存大小是没用占用的,可是操做系统没法使用这部份内存。

还有个问题,全部redis服务器若是要被复制数据,也就是要执行BGSAVE,那么至少须要保留30%-45%的空余内存。由于BGSAVE执行的时候可能须要copy key出来,若是这个时候写命令过多还须要给缓存区留有点空间。

5.redis 扩展读性能

有了主从复制功能以后实际上扩展读性能是比较容易的。利用主从链,将同步操做分派给同步节点,这样主节点就能够专门只负责写命令处理。

图2:

2

复制节点专门处理复制功能,最下游的读节点专门来接受读请求。若是考虑主节点宕机问题,能够开启复制节点的持久化功能,或者开启读节点的部分节点也行。主要是为了防止宕机能够快速恢复master。若是是异地双活,能够把左右两边的全部读节点开启持久化,左边一个机房,右边一个机房,既能够双活也能够恢复。(实际状况没有这么简单,只是个思路)

读节点是不可以执行写入命令的,这个才能保证未来的数据复制。

6.redis HA方案(sentinel)

redis 的高可用方案基于本身的一套sentinel 集群来管理。

图3:

3

sentinel cluster 保持对redis集群的监控,若是sentinel-1发现master在某个时间段内没有响应ping命令,就主观判定是可能宕机了。sentinel-二、sentinel-3若是都发现master是无响应的,那么三个投票判定master是客观宕机了,作一次master、slave切换。同时会经过sentinel-sh脚本进行一些通知操做。

固然,redis-HA方案有好几种,咱们也能够用keepalived、VIP来实现,将master、backup、slave分离开来,master、backup自动VIP切换。