Redis现在是火的一塌糊涂,不知道Redis怎么能行,各个云都提供了对Redis的支持,如阿里云等。

什么是Redis

Redis 是一个开源的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。支持多种类型的数据结构,如string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(sorted set:有序集合)。

首先Redis是将数据保存到内存中,都知道内存断电后数据就没了,所以,如果Redis不做一些处理,他所保存的数据将消失。这里的数据存储类似于java中的map,对Redis而言,key都是字符串,而value则是上述中的多种数据类型。

Redis大部分用来做缓存,什么是缓存呢?也就是把数据库中的数据暂时放入到Redis中,客户端请求时如果缓存中有,则直接拿出来返回,如果没有则去数据库中查找,并放入Redis,这样响应速度会有提升,也给数据库减轻压力。

消息中间节则可以简单理解为多个应用之间进行数据交互,如系统A向Redis订阅了主题"momo",而系统B向这个主题"momo"中发送消息,此时系统A就能收到这个消息(这是个订阅发布模型)。但是Redis貌似也不推荐作用于消息中间件,就像用Java去写桌面程序一样,而推荐使用RabbitMQ、ActiveMQ等进行处理。

诞生起因

无非就是对当前使用的东西不满意,或者这个东西无法满足自己需求,所以自行开发新的。在2008年,意大利的一家创业公司Merzia推出了一款基于MySQL的网站实时统计系统LLOOGG,然而没过多久该公司的创始人 Salvatore Sanfilippo便 对MySQL的性能感到失望,于是他决定亲自为LLOOGG量身定做一个数据库,并于2009年开发完成,这个数据库就是Redis。不过Salvatore Sanfilippo并不满足只将Redis用于LLOOGG这一款产品,而是希望更多的人使用它,于是在同一年Salvatore Sanfilippo将Redis开源发布,并开始和Redis的另一名主要的代码贡献者Pieter Noordhuis一起继续着Redis的开发,直到今天。Salvatore Sanfilippo自己也没有想到,短短的几年时间,Redis就拥有了庞大的用户群体。Hacker News在2012年发布了一份数据库的使用情况调查,结果显示有近12%的公司在使用Redis。国内如新浪微博、街旁网、知乎网,国外如GitHub、Stack Overflow、Flickr等都是Redis的用户。

大公司当然少不了支持,VMware公司从2010年开始赞助Redis的开发, Salvatore Sanfilippo和Pieter Noordhuis也分别在3月和5月加入VMware,全职开发Redis。

大佬就是大佬,永远是那么不经意间。

Redis大小限制

参考:

https://redis.io/topics/data-types-intro

https://redis.io/topics/data-types 

字符串value的最大长度为512 MB。Redis总结(一)_javaList的元素个数最多为2^32-1个,也就是4294967295个。同样Set、Hash也是一样。Redis总结(一)_java_02Key 大小也是512MB

客户端命令

Redis分为客户端、服务端,服务端用来保存数据,而客户端通过Redis的通信协议+一堆命令和服务端进行交互,由于开源,也有一堆的客户端实现(Java、C等众多语言)。java中比较推荐的是jedis和redisson,有能力的也可以自研。也有可视化的工具。

这些命令构成了对上述数据类型的操作,可以在以下网址进行学习 

1.http://redisdoc.com 

2.https://www.runoob.com/redis/redis-commands.html 

3.http://doc.redisfans.com/index.html 

4.https://www.redis.net.cn/tutorial/3501.html

Redis通信协议

Redis默认通过6379端口监听,所以,创建一个Socket,怼到此端口就可以了,客户端向服务端发送数据时,以\n\r结束,服务响应时,通过不同前缀,来区分数据类型,所以,怼成功后,按照Redis命令规范输出到IO流,输出完再开始按照Redis应答规范解析就可以了。

Redis定义了5种应答前缀,分别为+、-、:、$、*,通过IO流拿到数据后,需要判断是什么类型,转化成不同Java对象,比如String、Integer、List可以参考以前的博客

手写Redis客户端了解下?

Redis到底是单线程还是多线程?

很多人以为,高性能的服务器是多线程来实现的,其实不然,对Redis是而言,是单线程的,这是因为线程上下文切换需要花费一定时间。上下文切换意思是从当前执行任务切换到另一个任务执行的过程。但是,为了确保下次能从正确的位置继续执行,在切换之前,会保存上一个任务的状态。但过多的上下文切换,会把CPU时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,导致系统的整体性能下降。

而Redis采用单线程的话,就可以避免上下文切换等,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗。这也是Redis快的一个原因。

但是这里的单线程是指网络请求模块使用了一个线程,也就是一个线程处理所有网络请求,其他模块还是多线程。

Redis又如何让一个线程处理n个请求呢?答案是采用NIO多路复用技术实现,多路是指多个网络连接,复用指复用同一个线程。在java中可以使用SocketChannel、Selector等实现。

订阅/发布

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。这类似于微信的公众号,客户可以订阅任意数量的公众号,当公众号有新文章后,订阅的人都会收到,Redis 客户端也可以订阅任意数量的主题。

订阅者通过SUBSCRIBE 来订阅一个或多个主题,发布者用PUBLISH来向主题发送消息。这里的主题可以比作一个挂载点,众多客户端将自己挂到上面,发布者向指定挂载点发送消息时,挂在上面的客户端都能收到消息。Redis总结(一)_java_03

事务

Redis 事务可以一次执行多个命令, 并且带有这三个重要的保证

  1. 批量操作在发送 EXEC 命令前被放入队列缓存。
  2. 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行
  3. 事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

其中MULTI、EXEC、DISCARD 和WATCH、UNWATCH命令是对Redis事务的操作,其中MULTI声明事务开始,后续命令将排队按顺序等待EXEC执行。DISCARD是取消事务。WATCH和UNWATCH是监视一个或多个key和清除事务监控的键。

先说第一条意思,也就是当输入MULTI开启事务后,后续所有Redis正常操作命令会按照先进先出(FIFO)的顺序出入列,并返回QUEUED,直到输入EXEC为止。正常操作是指按照Redis命令规范、格式来操作,否则这个过程中会报错,事务将退出。Redis总结(一)_java_04MULTI不能嵌套,否则报错,但是不会影响事务状态。Redis总结(一)_java_05第二条意思是,Redis事务在使用EXEC开始执行后,即使有n条命令失败,剩余的也都会执行,如下图,incr在操作非数字时候会报错,但是报错后紧跟着的命令还是会执行,直到所有命令都完成。Redis总结(一)_java_06第三见名之意,就不说了。

那为什么Redis不支持回滚呢?大概是因为作者觉得失败的命令是由编程错误锁造成的,而这些错误应该在开发的过程中就被发现,所有上述说的当事务开启后,如果有错误的语法输入,事务则会关闭。并且没有事务回滚,Redis内部能简单快速。

缓存击穿

缓存击穿意思是在某个时间,Redis中的某一个数据在抗这非常大的请求,但是下一秒数据却过期了,这一堆请求走向数据库。

解决办法可以通过增加互斥锁,当第一个线程拿到锁后请求数据库,并设置到Redis中,其他线程后续直接从Redis中取。

缓存穿透

缓存穿透是指Redis中没有此条数据,便走向数据库,而数据库中也没有这条数据。比如自增id,如果查询小于1的id,肯定不存在。当面临非常大的请求时,数据库也会受不了。

可以通过参数校验,明知这个数据在数据库中一定不存在,则直接返回,或者通过布隆过滤器,布隆过滤器实际上是一个很长的二进制向量和一系列随机映射函数,也就是里面存的不是1就是0,布隆过滤器可以用于检索一个元素是否在一个集合中(但这不代表一定存在)。

新建一个布隆过滤器,默认张这个样子。Redis总结(一)_java_07此时如果要添加一个数据在里面,就需要用到多个不同的hash函数,计算出数据位置,并把对应位置置为1。例如H,通过三个hash函数计算出为1、2、6,如图。Redis总结(一)_java_08接着添加一个X,通过三个hash计算出为3、4、7,如图。Redis总结(一)_java_09现在要判断某个数据是否存在,例如L,通过三个hash计算出位置为1、3、8,而上图位置1、3都为bit 1,但是8为0,则表示L一定不存在,但是如果计算出位置为1、3、7,而不巧这几位都为1,但是L确确实实不存在,所以说,布隆过滤器能保证数据一定不存在,而不保证数据一定存在。

一般情况下布隆过滤器不能删除,这是因为每一位可能被多个数据所共享,如果删除其中一位,会影响其他数据。

可以使用Google提供的guava实现。

<dependency>
 <groupId>com.google.guava</groupId>
 <artifactId>guava</artifactId>
 <version>19.0</version>          
</dependency>

缓存雪崩

缓存雪崩是指Redis中的数据在某一时刻大面积失效,所有的数据又都走向数据库。导致数据库、CPU和内存负载过高。可以给缓存设置一个随机值过期时间,让每个key的过期时间分布开来。

持久化

Redis有两种方式持久化,分别是RDB、AOF RDB是一种快照存储持久化方式,就是将某一时刻的内存数据保存到硬盘中,默认保存的文件名为dump.rdb,而在Redis服务器启动时,会重新加载dump.rdb文件中的的数据到内存当中。客户端可以通过向Redis服务器发送save或bgsave命令让服务器生成rdb文件,这两个命令的区别是save是同步阻塞,而 bgsave 是非阻塞,或者通过服务器配置文件指定触发RDB条件。也就是在配置文件redis.conf中增加以下配置。

# 900s内至少达到一条写命令
save 900 1
# 300s内至少达到10条写命令
save 300 10
# 60s内至少达到10000条写命令
save 60 10000

而AOF持久化方式会记录客户端对服务器的每一次写操作命令,并将这些写操作以Redis协议追加到后缀名为aof文件末尾,在Redis服务器重启时,会加载并运行aof文件中的命令,从而起到恢复数据的目的。开启AOF功能需要配置:appendonly yes,默认不开启。

# 默认关闭若要开启将no改为yes
appendonly no
# append文件的名字
appendfilename "appendonly.aof"
# AOF文件的写入方式
# always一旦缓存区内容发生变化就写入AOF文件中
appendfsync always
# everysec 每个一秒将缓存区内容写入文件 默认开启的写入方式
appendfsync everysec
# 将写入文件的操作交由操作系统决定
appendfsync no
# 当AOF文件大小的增长率大于该配置项时自动开启重写(这里指超过原大小的100%)。
auto-aof-rewrite-percentage 100
# 当AOF文件大小大于该配置项时自动开启重写
auto-aof-rewrite-min-size 64mb

RDB优点:

1、RDB是一个快照文件,数据很紧凑,它保存了 Redis 在某个时间点上的数据集,体积比较小。

2、RDB 适合用于灾难恢复,而且体积小,方便拷贝。

3、RDB 在恢复大数据集时的速度比 AOF 的恢复速度要快。

RDB缺点:

1、服务器故障时候会丢失数据。也就是服务器突然嗝屁,Redis没来得及保存数据。但如果保存时间太频繁,又影响性能。

AOF 的优点 

1、AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然可以保持良好的性能,并且就算发生故障停机,也最多只会丢失一秒钟的数据。

AOF 的缺点

1、对于相同的数据集来说,AOF 文件的体积通常要大于 RDB 文件的体积。