Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言 编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API ——源自百度百科。
Redis是属于NoSql系列的非关系型数据库,广泛应用于缓存。
应用场景:
(1)为热点数据加速查询(主要场景)。如热点商品、热点新闻、热点资讯、推广类等高访问量信息等。
(2)即时信息查询。如各位排行榜、各类网站访问统计、公交到站信息、在线人数信息(聊天室、网站)、设备信号等。
(3)时效性信息控制。如验证码控制、投票控制等。
(4)分布式数据共享。如分布式集群架构中的 session 分离
Redis有Windows和linux、mac的环境,目前Windows已经停止更新。
官网下载地址:http://download.redis.io/releases/
在linux中安装:
进入到相关的目录,解压下载好的redis安装包
tar xzf redis-3.0.0.tar.gz
进入到该目录
cd redis-3.0.0
# 编译
make
# 安装 -> 此处安装到 /usr/local/redis
make install PREFIX=/usr/local/redis
安装完毕启动即可:
#进入安装redis的目录
cd /usr/local/redis/bin
# 启动
./redis-server
以下基于Linux的Docker的安装:
# 从docker下拉取redis
docker pull redis:4.0
# 安装
docker run -di --name=redis -p 6379:6379
Redis是一种Key|Value结构的NOSQL数据库,此处说的数据类型指的是Value的数据类型。
有五种:string、hash、list、set、sortedset(zset)
1.String:最为常用的类型,底层是字符数组 -> char buf[];
# 添加/修改数据添加/修改数据
set key value
# 举例
set username zhangsan
# 获取数据
get key
# 举例
key username
# 删除数据
del key
# 举例
del username
# 获取数据字符个数(字符串长度)
strlen key
# 追加信息到原始信息后部(如果原始信息存在就追加,否则新建)
append key value
2.Hash:field|value类型,也是键值对,是在value中的键值对
底层使用哈希表结构实现数据存储
# 添加/修改数据
hset key field value
# 举例
hset user username zhangsan
# 获取数据
hget key field
# 举例
hget user username
# 删除数据
del key field1 field2 ...
# 举例
del user username
# 获取多个数据
hmget key field1 field2 …
# 举例
hmget user username password age
3.list跟Java中的List是一样的,都是单列集合,有序存储
list可以保存多个数据,底层使用双向链表存储结构实现
它既可以从左边储取,也可以从右边存取。
# 添加/修改数据 l指的是left,r指的是right
lpush key value1 [value2] # 从左边存入
rpush key value1 [value2] # 从右边存入
# 举例
lpush age 12 13 14 15 16 17
rpush age 12 13 14 15 16 17
获取数据
lrange key start stop # 从左边获取,从start开始,到stop结束
lindex key index # 从左边获取,index 根据索引值获取
llen key # 从左边 获取全部
获取并移除数据
lpop key # 从左边获取,并且从list中移除该key
rpop key # 从左边获取,并且从list中移除该key
4.set
新的存储需求:存储大量的数据,在查询方面提供更高的效率
需要的存储结构:能够保存大量的数据,高效的内部存储机制,便于查询
set类型:与hash存储结构完全相同,仅存储键,不存储值(nil),并且值是不允许重复的
sortedset与其一致,区别在于它能够排序。
添加数据
sadd key member1 [member2]
获取全部数据
smembers key
删除数据
srem key member1 [member2]
获取集合数据总量
scard key
判断集合中是否包含指定数据
sismember key member
随机获取集合中指定数量的数据
srandmember key [count]
随机获取集中的某个数据并将该数据移除集合
spop key [count]
如上即可。
持久化是指能够将内存中的数据写到硬盘中;Redis的数据都放在内存中。如果机器挂掉,内存的数据就不存在,数据不能恢复,严重影响使用。redis本身提供了持久化机制。
RDB(Redis Database): 快照形式 (定期数据保存磁盘中)会产生一个dump.rdb文件,redis默认开启了RDB的持久化方式。
特点:根据操作数记录,会存在数据丢失,性能较好,用于数据备份。
修改配置:
在redis的解压包中的redis.conf文件里,找到save
# after 900 sec (15 min) if at least 1 key changed 15分钟内有1次操作时就备份
# after 300 sec (5 min) if at least 10 keys changed 5分钟内有10次操作时就备份
# after 60 sec if at least 10000 keys changed 1分钟内有10000次操作时就备份
save 900 1
save 300 10
save 60 10000
AOF(append only file):以独立日志的方式记录每次写命令,重启时再重新执行AOF文件中命令 达到恢复数据的目的。与RDB相比可以简单理解为由记录数据改为记录数据产生的变化
特点:每秒保存,数据完整性比较好,耗费性能。
开启AOF:
在redis.conf文件中,找到
#修改为yes
appendonly no -> appendonly yes
配置AOF:
接着找到: appendfsync always 去掉注释即可。
always:总是执行
everysec:每秒钟执行(默认)
no:不执行。
AOF 和RDB对比:
命令 | RDB | AOF |
---|---|---|
启动优先级 | 低 | 高 |
体积 | 小 | 大 |
恢复速度 | 快 | 慢 |
数据安全性 | 丢数据 | 根据策略决定 |
RDB的最佳策略:
AOF的最佳策略:
如上即可。
Redis数据在内存中有三种状态,分别为:
正数:代表该数据在内存中还能存活的时间
-1: 永久有效的数据
-2: 已经删除或者还未定义的数据
删除策略就是针对已过期数据的处理策略,过期的数据会根据不同策略对Redis过期数据进行删除.
在Redis内部会有一块独立的空间,专门来记录某个内存的过期时间
过期数据是一块独立的存储空间(expires),Hash结构,field是内存地址,value是过期时间,保存了所有key的过期描述,在最终进行过期处理的时候,对该空间的数据进行检测, 当时间到期之后通过field找到内存该地址处的数据,然后进行相关操作。
Redis中有三种删除策略:
定时删除:给存储的Redis数据加上定时器,当数据存活时间达到定时器的值时,就立即删除。这种方式能够快速清理掉过期的数据,但是会给CPU带来很大的压力,用处理器性能换取存储空间(拿时间换空间)
惰性删除:给存储的Redis数据加过期时间,数据到达过期时间,不做处理。等下次访问该数据时再根据情况处理。
如果访问时数据已经过期->删除
如果数据还未过期->返回数据
这种方式能够减轻CPU的压力,但是会让内存有大量过期数据,用存储空间换取处理器性能(拿时间换空间)
定期删除:是上面两种方式的这种方案,它会随机的去检查expires,然后再随机的检查里面的key是否过期。
整个过程如下:
Redis启动服务器初始化时,会读取redis.conf文件hz的值,默认为10(hz表示每秒执行10次某个方法的频率)
每秒钟执行server.hz次**serverCron()**方法,每个方法又继续执行->databasesCron(),在每次databasesCron方法中执行一次activeExpireCycle()
**activeExpireCycle()**对每个expires[*]逐一进行检测,每次执行耗时:250ms/server.hz
对某个expires[*]检测时,随机挑选W个key检测
如果key超时,删除key
如果一轮中删除的key的数量>W*25%,循环该过程
如果一轮中删除的key的数量≤W25%,检查下一个expires[],0-15循环
W取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
参数current_db用于记录activeExpireCycle() 进入哪个expires[*] 执行
如果activeExpireCycle()执行时间到期,下次从current_db继续向下执行
数据淘汰策略:
当新数据进入redis时,如果内存不足,在执行每一个命令前,会调用**freeMemoryIfNeeded()**检测内存是否充足。如果内存不满足新 加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为逐出算法.
根据淘汰类型的不同,主要分为三类淘汰策略:
第一类:检测易失数据(可能会过期的数据集server.db[i].expires )
volatile-lru(Least Recently Used): 挑选最近最少使用的数据淘汰(可以理解为淘汰长时间不活动的数据)
volatile-lfu(Least frequently used)):挑选最近使用次数最少的数据淘汰
volatile-ttl:挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰
第二类:检测全库数据(所有数据集server.db[i].dict )
allkeys-lru:挑选最近最少使用的数据淘汰
allkeLyRs-lfu::挑选最近使用次数最少的数据淘汰
allkeys-random:任意选择数据淘汰,相当于随机
第三类:放弃数据驱逐
no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发OOM(Out Of Memory)
一般的剔除策略有 FIFO 淘汰最早数据、LRU 剔除最近最少使用、和 LFU 剔除最近使用频率最低的数据几种策略。
noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外)
allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。
volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。
allkeys-random: 回收随机的键使得新添加的数据有空间存放。
volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。
volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空间存放。
如果没有键满足回收的前提条件的话,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。
首先我们理解互联网的三高架构:
高并发
应用要提供某一业务要能支持很多客户端同时访问的能力,我们称为并发,高并发意思就很明确了
高性能
性能带给我们最直观的感受就是:速度快,时间短
高可用
一年中应用服务正常运行的时间占全年时间的百分比,如下图:表示了应用服务在全年宕机的时间
为了避免单点Redis服务器故障,准备多台服务器,互相连通。将数据复制多个副本保存在不同的服务器上,连接在一起,并保证数据是同步的。即使有其中一台服务器宕机,其他服务器依然可以继续提供服务,实现Redis的高可用,同时实现数据冗余备份。
其中负责数据增删的称为主节点,负责数据读取的称为从节点。当主节点数据发生变化时,会自动将数据同步到从节点当中。
在主从模式中,当主节点master发生宕机时,此时为了实现Redis的高可用,应该让从节点顶上,摇身一变称为主节点。
那么如何监控主从节点,当发生宕机时,如何选择从节点?此时就需要一个能够监控它的人->哨兵(Sentinel)
Sentinel(哨兵):可以管理多个Redis服务器,它提供了监控,提醒以及自动的故障转移的功能,
Replication(复制):则是负责让一个Redis服务器可以配备多个备份的服务器。
原理:
1.多个sentinel 发现并确认master有问题。
2.sentinel内部选举领导
3.选举出slave作为新的master
4.通知其余的slave成为新master的slave
5.通知客户端 主从变化
6.如果老的master重新复活,那么成为新的master的slave
要实现上边的功能的主要细节主要有以下三个定时任务:
策略总结:
1.尽量为 每一个节点部署一个哨兵
2.哨兵也要搭建集群(防止哨兵单点故障)
3.每一个节点都同时设置quorum的值超过半数(N/2)+1
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,攻击会导致数据库压力过大的现象。
解决办法:
在redis中设定null值(设定过期时间),防止过多的请求落在数据库中。
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力
为什么会出现大批量打到数据库的请求?
原因:虽然我们查询数据库之后,会将结果保存到缓存中,但由于瞬间产生了大量的请求,
所以在缓存还没有来得及保存数据时,就有一堆请求打到了数据库。
如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。
解决:(不完整,只写了自己想到的。)
1.更多的页面静态化处理
2.构建多级缓存架构
Nginx缓存+redis缓存+Mybatis二级缓存,使用cannal做数据的同步
如上知识理解即可。
解决方案:
1,不要设置有效时间,商品的类别信息
2,需要设置有效期,微博信息
主要两个步骤:
最后一个功能是事务,但 Redis 提供的不是严格的事务,Redis 只保证串行执行命令,并且能保证全部执行,但是执行命令失败时并不会回滚,而是会继续执行下去。
主从复制:是redis实现高可用的一个策略。将会有主节点 和从节点,从节点的数据完整的从主节点中复制一份。
哨兵:当系统节点异常宕机的时候,开发者可以手动进行故障转移恢复,但是手动比较麻烦,所以通过哨兵机制自动进行监控和恢复。为了解决哨兵也会单点故障的问题,可以建立哨兵集群。
集群:即使使用哨兵,redis每个实例也是全量存储,每个redis存储的内容都是完整的数据,浪费内存且有木桶效应。为了最大化利用内存,可以采用集群,就是分布式存储。这个时候可以使用redis集群。将不同的数据分配到不同的节点中,这样就可以横向扩展,扩容。