一、redis简介

1、概念:

Redis 是完全开源免费的,遵守BSD协议,是一个高性能(NOSQL)的key-value数据库, Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。(Vmware在资助着redis项目的开发和维护)

2、特点

  • ==性能极高== – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • ==丰富的数据类型== – Redis支持的类型 String, List, Hash, Set 及 Ordered Set 数据类型操作。
  • ==原子== –Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • ==丰富的特性== – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

3、Redis总结

  • redis单个key 存入512M大小
  • redis支持多种类型的数据结构(string,list,hash.set.zset)
  • redis 是单线程 原子性
  • redis可以持久化 因为使用了 RDB和AOF机制
  • redis支持集群 而且redis 支持库(0-15) 16个库
  • redis 还可以做消息队列 比如聊天室 IM
  • 企业级开发中:可以用作数据库、缓存(热点数据(经常会被查询,但是不经常被修改或者删除的数据)和消息中间件等大部分功能。

优点:

  • 丰富的数据结构
  • 高速读写,redis使用自己实现的分离器,代码量很短,没有使用lock(MySQL),因此效率非常高。

缺点:

  • 持久化。Redis直接将数据存储到内存中,要将数据保存到磁盘上,Redis可以使用两种方式实现持久化过程。定时快照(snapshot):每隔一段时间将整个数据库写到磁盘上,每次均是写全部数据,代价非常高。第二种方式基于语句追加(aof):只追踪变化的数据,但是追加的log可能过大,同时所有的操作均重新执行一遍,回复速度慢

  • 耗内存,占用内存过高。

    二、Redis数据类型

    redis支持五种数据类型作为其Value,redis的Key都是字符串类型的。
    ==string==:redis 中字符串 value 最大可为512M。可以用来做一些计数功能的缓存(也是实际工作中最常见的)。
    ==list==:简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部(左边)或者尾部(右边),其底层实现是一个链表。可以实现一个简单消息队列功能,做基于redis的分页功能等。
    ==set==:是一个字符串类型的无序集合。可以用来进行全局去重等。
    ==sorted set==:是一个字符串类型的有序集合,给每一个元素一个固定的分数score来保持顺序。可以用来做排行榜应用或者进行范围查找等。
    ==hash==:键值对集合,是一个字符串类型的 Key和 Value 的映射表,也就是说其存储的Value是一个键值对(Key- Value)。可以用来存放一些具有特定结构的信息。

    1、String

    简介
  • string是redis最基本的类型,一个key对应一个value。

  • string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象 。

  • string类型是Redis最基本的数据类型,一个键最大能存储512MB。

  • 二进制安全是指,在传输数据时,保证二进制数据的信息安全,也就是不被篡改、破译等,如果被攻击,能够及时检测出来
    二进制安全特点:
    1、编码、解码发生在客户端完成,执行效率高
    2、不需要频繁的编解码,不会出现乱码

    应用场景:
  • String通常用于保存单个字符串或JSON字符串数据

  • 因String是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储

  • 计数器(常规key-value缓存应用。常规计数: 微博数, 粉丝数)

  • INCR等指令本身就具有原子操作的特性,所以我们完全可以利用redis的INCR、INCRBY、DECR、DECRBY等指令来实现原子计数的效果。假如,在某种场景下有3个客户端同时读取了mynum的值(值为2),然后对其同时进行了加1的操作,那么,最后mynum的值一定是5。

  • 不少网站都利用redis的这个特性来实现业务上的统计计数需求。
    8.2.哈希(Hash)
    8.2.1.简介
    Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。 Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)
    可以看成具有KEY和VALUE的MAP容器,该类型非常适合于存储值对象的信息,
    如:uname,upass,age等。该类型的数据仅占用很少的磁盘空间(相比于JSON)

2、Hash

简介

Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。 Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)可以看成具有KEY和VALUE的MAP容器,该类型非常适合于存储值对象的信息, 如:uname,upass,age等。该类型的数据仅占用很少的磁盘空间(相比于JSON)

应用场景:
  • 常用于存储一个对象
  • 为什么不用string存储一个对象?
    hash是最接近关系数据库结构的数据类型,可以将数据库一条记录或程序中一个对象转换成hashmap存放在redis中。
    用户ID为查找的key,存储的value用户对象包含姓名,年龄,生日等信息,如果用普通的key/value结构来存储,主要有以下2种存储方式:
    第一种方式将用户ID作为查找key,把其他信息封装成一个对象以序列化的方式存储,这种方式的缺点是,==增加了序列化/反序列化的开销==,并且在需要修改其中一项信息时,需要把整个对象取回,并且修改操作需要对并发进行保护,引入CAS等复杂问题。
    第二种方法是这个用户信息对象有多少成员就存成多少个key-value对儿,用用户ID+对应属性的名称作为唯一标识来取得对应属性的值,虽然省去了序列化开销和并发问题,==但是用户ID为重复存储,如果存在大量这样的数据,内存浪费还是非常可观的==。
    总结:
    Redis提供的Hash很好的解决了这个问题,Redis的Hash实际是内部存储的Value为一个HashMap,并提供了直接存取这个Map成员的接口

    3、List

    简介
    Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素) 类似JAVA中的LinkedList
    应用场景
  • 对数据量大的集合数据删减

列表数据显示、关注列表、粉丝列表、留言评价等…分页、热点新闻(Top5)等
利用LRANGE还可以很方便的实现分页的功能,在博客系统中,每片博文的评论也可以存入一个单独的list中。

  • 任务队列

(list通常用来实现一个消息队列,而且可以确保先后顺序,不必像MySQL那样还需要通过ORDER BY来进行排序)

任务队列介绍 (生产者和消费者模式)
在处理Web客户端发送的命令请求时,某些操作的执行时间可能会比我们预期的更长一些,通过将待执行任务的相关信息放入队列里面,并在之后对队列进行处理,用户可以推迟执行那些需要一段时间才能能完成的操作,这种将工作交给任务处理器来执行的做法被称为任务队列(task queue)。

  • 常用案例:

1:订单系统的下单流程、
2:用户系统登录注册短信等
用户登录成功后,
三个资源:
列表页面 (登录后就允许访问)
实名认证页面(登录后就允许访问)
转帐页面 (登录后、还要实名认证后)

4、Set类型

简介

Redis 的 Set 是 String 类型的无序集合。 集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。类似于JAVA中的 Hashtable集合
redis的集合对象set的底层存储结构特别神奇,==底层使用了intset和hashtable两种数据结构存储的==,intset我们可以理解为数组,hashtable就是普通的哈希表(key为set的值,value为null)。
intset内部其实是一个数组(int8_t coentents[]数组),而且存储数据的时候是有序的,因为在查找数据的时候是通过二分查找来实现的。

应用场景

1、对两个集合间的数据计算进行==交集、并集、差集运算==
2、以非常方便的实现如共同关注、共同喜好、二度好友等功能。对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存储到一个新的集合中。
3、利用唯一性,可以统计访问网站的所有==独立 IP==

5、有序集合(sorted set) ZSET

简介

1、 Redis 有序集合和集合一样也是string类型元素的集合,且不允许重复的成员。
2、==不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。==
3、==有序集合的成员是唯一的,但分数(score)却可以重复。==
4、集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
5、==Redis的ZSet是有序、且不重复 ==
(很多时候,我们都将redis中的有序集合叫做zsets,这是因为在redis中,有序集合相关的操作指令都是以z开头的)

应用场景
  • 常应用于:排行榜

1比如twitter 的public timeline可以以发表时间作为score来存储,这样获取时就是自动按时间排好序的。
2比如一个存储全班同学成绩的Sorted Set,其集合value可以是同学的学号,而score就可以是其考试得分,这样在数据插入集合的时候,就已经进行了天然的排序。
3还可以用Sorted Set来做带权重的队列,比如普通消息的score为1,重要消息的score为2,然后工作线程可以选择按score的倒序来获取工作任务。让重要的任务优先执行。

三、Redis 发布订阅

简介
  • Redis ==发布订阅(pub/sub)是一种消息通信模式==:发送者(pub)发送消息,订阅者(sub)接收消息。
  • Redis 客户端可以订阅任意数量的频道。
    示例
    下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
    在这里插入图片描述
    当有新消息通过 PUBLISH 命令发送给频道 channel1 时,这个消息就会被发送给订阅它的三个客户端:
    在这里插入图片描述
    应用场景
  • 这一功能最明显的用法就是==构建实时消息系统==,比如普通的即时聊天,群聊等功能
  • 在一个==博客网站中,有100个粉丝订阅了你,当你发布新文章,就可以推送消息给粉丝们==。
  • 微信公众号模式

    四、Redis多数据库和事务

    redis默认16个数据库

    Redis下,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0。
  • redis配置文件中下面的参数来控制数据库总数:
    database 16 //(从0开始 1 2 3 …15)

    Redis事务

    Redis 事务可以一次执行多个命令,(按顺序地串行化执行,执行中不会被其它命令插入,不许加塞)
  • 简介*

Redis 事务可以一次执行多个命令(允许在一次单独的步骤中执行一组命令), 并且带有以下两个重要的保证:
==批量操作在发送 EXEC 命令前被放入队列缓存。==
收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

1、Redis会将一个事务中的所有命令序列化,然后按顺序执行
2、执行中不会被其它命令插入,不许出现加塞行为

  • 一个事务从开始到执行会经历以下三个阶段:

开始事务。
命令入队。
执行事务。

  • 事务的错误处理产生回滚:

队列中的某个命令出现了报告错误,执行时整个的所有队列都会被取消。

  • 事务的错误处理不产生回滚:

如果执行的某个命令报出了错误,则只有报错的命令不会被执行,而其它的命令都会执行,不会回滚。

五、Redis持久化

简介

数据存放于:
==内存:高效、断电(关机)内存数据会丢失==
==硬盘:读写速度慢于内存,断电数据不会丢失==

RDB(快照)

  • RDB:是redis的默认持久化机制。RDB相当于照快照,保存的是一种状态。几十G数据--》几KB快照
  • 快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
  • 优点:
    1、快照保存数据极快、还原数据极快
    2、适用于灾难备份
    3、缺点:小内存机器不适合使用,RDB机制符合要求就会照快照
  • 快照条件:
    1、==服务器正常关闭时 ./bin/redis-cli shutdown==
    2、==key满足一定条件,会进行快照==
    vim redis.conf   搜索save
    :/save
    save 900 1 //每900秒(15分钟)至少1个key发生变化,产生快照
    save 300 10 //每300秒(5分钟)至少10个key发生变化,产生快照
    save 60 10000 //每60秒(1分钟)至少10000个key发生变化,产生快照

    AOF

    由于快照方式是在一定间隔时间做一次的,所以如果redis 意外down 掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof 持久化方式。==Append-only file:aof 比快照方式有更好的持久化性==,是由于在使用aof 持久化方式时,redis 会将每一个收到的写命令都通过write 函数追加到文件中(默认是appendonly.aof)。当redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
  • 有三种方式如下(默认是:每秒 fsync 一次)
    1、•appendonly yes //启用 aof 持久化方式
    2、•# appendfsync always //收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
    3、•appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中
    4、•# appendfsync no //完全依赖 os,性能最好,持久化没保证

产生的问题:
aof 的方式也同时带来了另一个问题。==持久化文件会变的越来越大==。例如我们调用 incr test命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的。

六、redis缓存与数据库一致性

解决方案

一、实时同步

对强一致要求比较高的,应采用实时同步方案,即查询缓存查询不到再从DB查询,保存到缓存;更新缓存时,先更新数据库,再将缓存的设置过期(建议不要去更新缓存内容,直接设置缓存过期)。
@Cacheable:查询时使用,注意Long类型需转换为Sting类型,否则会抛异常
@CachePut:更新时使用,使用此注解,一定会从DB上查询数据
@CacheEvict:删除时使用;
@Caching:组合用法

二、异步队列
  • 对于并发程度较高的,可采用异步队列的方式同步,==可采用kafka等消息中间件==处理消息生产和消费。
    三、使用阿里的同步工具canal
  • canal实现方式是==模拟mysql slave和master的同步机制==,监控DB
    bitlog的日志更新来触发缓存的更新,此种方法可以解放程序员双手,减少工作量,但在使用时有些局限性。
    四、采用UDF自定义函数的方式
  • 面对mysql的API进行编程,利用触发器进行缓存同步,但UDF主要是c/c++语言实现,学习成本高。

    七、缓存穿透与雪崩

    穿透

    概念:==缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中,于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。==。

    与缓存击穿区别

    缓存击穿 是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,==当这个key在失效的瞬间,持续的大并发就穿破缓存==,直接请求数据库,就像在一个屏障上凿开了一个洞。

穿透解决办法: 持久层查询不到就缓存空结果,查询时先判断缓存中是否exists(key) ,如果有直接返回空,没有则查询后返回,注意insert时需清除查询的key,否则即便DB中有值也查询不到(==当然也可以设置空缓存的过期时间==)

雪崩

雪崩: 缓存大量失效的时候,引发大量查询数据库。
解决办法:

  • ①用==锁/分布式锁==或者队列串行访问
  • ②缓存失效时间均匀分布

热点key(缓存击穿)

热点key: ==某个key访问非常频繁,当key失效的时候有大量线程来构建缓存,导致负载增加,系统崩溃==。
解决办法:

  • 使用锁,单机用synchronized,lock等,分布式用分布式锁。
  • 缓存过期时间不设置,而是设置在key对应的value里。如果检测到存的时间超过过期时间则异步更新缓存。
  • 在value设置一个比过期时间t0小的过期时间值t1,当t1过期的时候,延长t1并做更新缓存操作。
  • 设置标签缓存,标签缓存设置过期时间,标签缓存过期后,需异步地更新实际缓存

    八、Redis主从复制和集群

    主从复制

    简介
    应用场景:电子商务网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是==”多读少写”==。
    ==主从复制:== 一个Redis服务可以有多个该服务的复制品,这个Redis服务称为Master,其它复制称为Slaves
    在这里插入图片描述
    如图中所示,我们将一台Redis服务器作主库(Matser),其他三台作为从库(Slave),主库只负责写数据,每次有数据更新都将更新的数据同步到它所有的从库,而从库只负责读数据。这样一来,就有了两个好处:
  • 1.==读写分离==,不仅可以提高服务器的负载能力,并且可以根据读请求的规模自由增加或者减少从库的数量。
  • 2.数据被复制成了了好几份,就算有一台机器出现故障,也可以使用其他机器的数据快速恢复。
  • 需要注意的是:在Redis主从模式中,一台主库可以拥有多个从库,但是一个从库只能隶属于一个主库。

    集群

    简介
    为什么使用redis-cluster?
  • 为了在==大流量访问==下提供稳定的业务,集群化是存储的必然形态
  • 未来的发展趋势肯定是==云计算和大数据==的紧密结合
  • 只有==分布式架构==能满足要求

Redis集群搭建方案:
(1)Twitter开发的twemproxy
(2)豌豆荚开发的codis
(3)redis官方的redis-cluster
在这里插入图片描述
Redis集群搭建的方式有多种,但从==redis 3.0之后版本支持redis-cluster集群,至少需要3(Master)+3(Slave)才能建立集群==。Redis-Cluster采用==无中心结构==,==每个节点保存数据和整个集群状态==,每个节点都和其他所有节点连接。其redis-cluster架构图如右上侧:

Redis Cluster集群特点
  • 1、所有的redis节点==彼此互联==(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
  • 2、节点的fail是通过集群中==超过半数的节点检测==失效时才生效。
  • 3、客户端与redis节点直连,不需要中间proxy层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 4、redis-cluster把所有的物理节点映射到[0-16383]slot上(不一定是平均分配),cluster 负责维护
  • 5、Redis集群预分好16384个哈希槽,当需要在 Redis 集群中放置一个 key-value 时, redis 先对key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节