常用数据类型
:字符串String、哈希表Hash、列表List、集合Set、有序集合Zset
特殊数据类型
:地理位置Geospatial、基数统计Hyperloglog、位图BitMaps
1. Redis是单线程的,为什么还能这么快?
. 对服务端程序来说,线程切换和锁通常是性能杀手,而单线程避免了线程切换和竞争所产生的消耗;
2. Redis在持久化时fork出一个子进程,这时已经有两个进程了,怎么能说是单线程呢?
Redis是单线程的,主要是指Redis键值对读写是由一个线程来完成的。而Redis的其他功能,如持久化、异步删除、集群数据同步等,则是依赖其他线程来执行的。所以,说Redis是单线程的只是一种习惯的说法,事实上它的底层不是单线程的。
客户端通过watch命令,要求服务器对一个或多个key进行监视,如果在客户端执行事务之前,这些key发生了变化,则服务器将拒绝执行客户端提交的事务,并向它返回一个空值。
普通数据
:在设置过期时间时,可以附加一个随机数,避免大量的key同时过期,导致缓存雪崩。
热点数据
:. 热点数据不设置过期时间,使其达到“物理”上的永不过期,可以避免缓存击穿问题。
参考文章:细说Redis分布式锁
setnx命令
SET if Not eXists 命令在指定的 key 不存在时,为 key 设置指定的值。
设置成功,返回 1 。 设置失败,返回 0 。
setnx实现加锁
第一版本
这种方式的缺点是容易产生死锁,因为客户端有可能忘记解锁,或者解锁失败
setnx key value
第二版本
给锁增加了过期时间,避免出现死锁。但这两个命令不是原子的,第二步可能会失败,依然无
法避免死锁问题。
setnx key value
expire key seconds
第三版本
通过“set…nx…”命令,将加锁、过期命令编排到一起,它们是原子操作了,可以避免死锁。
set key value nx ex seconds
第四版本
上面虽然已经解决了死锁的问题,但是还没有解决下面这个问题:
进程A在任务没有执行完毕时,锁已经到期被释放了。等进程A的任务执行结束后,它依然会尝试释放锁,因为它的代码逻辑就是任务结束后释放锁。但是,它的锁早已自动释放过了,它此时释放的可能是其他线程的锁。
在加锁时就要给锁设置一个标识,进程要记住这个标识。当进程解锁的时候,要进行判断,是自己
持有的锁才能释放,否则不能释放。
这就需要采用Lua脚本,通过Lua脚本将两个命令编排在一起,而整个
Lua脚本的执行是原子的。
# 加锁
set key random-value nx ex seconds
# 解锁
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
RDB持久化
手动触发:SAVE、BGSAVE、FLUSHALL命令触发RDB持久化操作,创建“.rdb”文件;
自动触发:通过配置选项,让服务器在满足指定条件时自动执行BGSAVE命令
AOF持久化
将我们所有的命令都记录下来,恢复的时候就把这个文件全部再执行一遍
如果要使用AOF,需要修改配置文件:
appendonly no yes
则表示启用AOF
默认是不开启的,我们需要手动配置,然后重启redis,就可以生效了!
如果redis部署了多台,当一台或几台故障时,整个系统依然可以对外提供服务,这样就提高了服务的可用性。这就是高可用(High Availability)。
1. 主从复制
1. 概念
将一台Redis服务器的数据,复制到其他的Redis服务器,前者称为主节点Master,后者称为从节点Slave;数据的复制是单向的,只能由主节点到从节点。
2. 作用
数据冗余:主服务器数据复制到从服务器
故障恢复:主节点出现问题时,可以由从节点提供服务
负载均衡:读写分离,主节点提供写服务,从节点提供读服务
2. 哨兵
1. 概念
在主从复制的基础上,哨兵引入主节点的自动故障转移:当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
2. 自动故障转移过程
Redis Sentinel(哨兵)是一个分布式架构,它包含若干个哨兵节点和数据节点。每个哨兵节点会对数据节点和其余的哨兵节点进行监控,当发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它就会与其他的哨兵节点进行协商,当多数哨兵节点都认为主节点不可达时,它们便会选举出一个哨兵节点来完成自动故障转移的工作,同时还会将这个变化实时地通知给应用方。整个过程是自动的,不需要人工介入,有效地解决了Redis的高可用问题!
3. 集群
1. 概念
上面两种方式的每台服务器数据都是一样的,那么存储能力跟单机服务器是一样的。所以集群将数据分散到多个节点,解决存储能力受到单机限制的问题。
2. 数据分片
Redis集群引入了哈希槽的概念,Redis集群有16384个哈希槽(编号0-16383)集群的每个节点负责一部分哈希槽。
以3个节点组成的集群为例
节点A包含0~5460号哈希槽
节点B包含5461~10922号哈希槽
节点C包含10923~16383号哈希槽
每个Key通过CRC16算法生成一个值,用这个值对16384取余得到的数,在哪个节点的哈希槽区间,来决定放在哪个节点的哈希糟中,然后直接自动跳转到这个对应的节点上进行存取操作。
当写入数据将导致超过内存缓存时,Redis会采用maxmemory-policy所指定的策略进行数据淘汰,该策略一共包含如下8种选项。
LRU
:最近最少使用原则。不足之处在于,若一个key很少被访问,只是刚刚偶尔被访问了一次,则它就被认为是热点数据,短时间内不会被淘汰。
LFU
:最近最不频繁使用原则。把访问次数最低的数据
淘汰出内存。如果两个数据的访问次数相同,LFU再比较这两个数据的访问时间,把访问时间更早的数据淘汰出内存。
惰性删除
:
访问key的时候,Redis会先检查它的过期时间,如果过期就立刻删除key。
定期删除
:
Redis将设置了过期时间的key放到字典中,当已过期key的比例超过25%,就随机扫描20个key并且删除已过期的key;
问题
缓存雪崩
:在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库(一大片)缓存穿透
:总是访问一些数据库都没有的数据,导致缓存也一直没有,所以一直都会直接访问数据库。缓存击穿
:某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨大的压力
解决
缓存雪崩
:设置随机过期时间
缓存穿透
:设置key-null缓存有效时间、设置布隆过滤器
缓存击穿
:设置热点数据永不过期
四种同步策略
先更新缓存数据,再更新数据库数据;
先更新数据库数据,再更新缓存数据;
先删除缓存数据,再更新数据库数据;
先更新数据库数据,再删除缓存数据;
比较
删除与更新缓存数据的选择
虽然更新缓存可以在查询时更容易命中,但是更新缓存数据消耗性能比较大,所以建议选择删除。
缓存和数据库谁先操作
更新数据库、再删除缓存会更好。