核心思路就是把⼀些常见的数据放到触手可及(访问速度更快)的地方, 方便随时读取.
访问速度特别的快,但由于存储空间是有限的,所有我们只能存储“热点数据”(频繁访问的数据)在Redis中 俗称“二八定律”:20%的热点数据,可以应对80%的应用场景,从而整体上的性能得到提升
对应MySQL这种访问硬盘的数据库,Redis是内存数据库,Redis用做为MySQL的缓存
为什么MySQL性能不高? 1.首先数据存储在硬盘上,硬盘IO需要花费大量时间,尤其是随机访问; 2.如果查询中不能命中索引,那么将需要表的遍历,大大增加硬盘IO的次数 3.对应执行SQL需要一系列的解析,校验等等工作 4.如果是一些复杂查询,例如联合查询,笛卡尔积之类的操作效率会很低...............
如果数据库并发量特别高,那么它将会可能出现宕机(出现故障,罢工了,不做了)的情况
如何解决?
1.开源:增加多个设备,部署更多的数据库机器,构成数据库集群 2.节流:引入缓存,访问的时候尽量访问缓存内的数据,从而降低直接访问数据库的请求数量 实际开发中,往往是俩种方法结合起来使用
Redis为什么可以作为MySQL的缓存:
Redis访问速度比MySQL快很多,并且处理一个访问请求,Redis消耗的资源比MySQL少很多,因此Redis支持更大的并发量: 1.Redis数据在内存,并且访问速度比硬盘快很多 2.Redis只支持简单的KEY-VALUE 存储,不涉及很多复杂查询方法手段
业务服务器先查询 Redis, 看想要的数据是否在 Redis 中存在.
一个重要问题:那么多数据,哪些数据才是我们需要的热点数据呢?
会将所有访问的数据,以日志的形式给记录下来,最后挑选出频率前%N的数据作为“热点数据”;
此处的数据,可以按照每天/周/月去更新热点数据
那么经过这样一段时间内,缓存的数据肯定会存储满,那么Redis也引入了对应的策略--------------内存淘汰机制:
通用的淘汰机制有以下机制:
在Redis中已经实现好了以上类似机制:
整体来说 Redis 提供的策略和我们上述介绍的通用策略是基本⼀致的. 只不过 Redis 这里会针对 "过期 key" 和 "全部 key" 做分别处理.
1.定期生成的数据这种情况不涉及缓存预热;
2.实时生成: Redis服务器首次接入连接之后是没有数据的,此时所有的请求将在MySQL中(就怕此时MySQL没有抗住这么多请求挂了),那么随之时间的推移,Redis上的数据慢慢越来越多,MySQL承担的压力就会小很多;
缓存预热就是解决上述问题: 将定期生成和实时生成结合一下,先通过“离线”的方式,通过统计的途径,先找到一些热点数据(这些数据并一定精准,有就行)导入到Redis中,就能帮助MySQL承担很大的压力,随之时间的推移,热点数据会逐渐调整,来使用当前情况;
在查询某个key的时候,在Redis中没有,在MySQL中也没有,那么这么key肯定不会更新到Redis中~ 但是这个查询没有,接连着查询了多次,依然会给MySQL造成一些压力;
为什么出现这种情况?
解决:
在短时间内,Redis上大规模的key失效,导致缓存命中率陡然下降,让MySQL的压力迅游变大,导致宕机~
为何产生?
1.Redis挂了
2.Redis没有挂,但是可能之前给很多key设置的过期时间相同,一下子全过期自动删除了
解决:
相当于缓存雪崩的特殊情况,针对Redis热点key全部过期了,导致大量的请求直接访问到MySQ中,引起数据库宕机
在一个分布式系统当中,会涉及到多个节点访问同一个公共资源的情况下,此时就需要通过“锁”来进行互斥,避免出现“线程安全”的问题;
像Java中的synchronized 这种锁只能在当前进程中生效,在分布式这种多个进程多个主机的场景下就不能使用了‘;
实现思路:通过设置一个键值对来标识锁的状态
举例买票的例子:
此时客户端1查询001车 还有1张车票 在即将执行1->0 过程前 客户端2也执行查询余票 发现还有一张,那么客户端2也执行1->0 那么这出现“超卖” 的情况,俩个人都买到票 加锁:
此时, 如果 买票服务器1 尝试买票, 就需要先访问 Redis, 在 Redis 上设置⼀个键值对. 比如 key 就是车次, value 随便设置个值 (比如 1). 如果发现操作成功,那么就可以视为当前没有节点对001车次加锁,那么就可以进行数据库的读写,操作完成之后,就可以吧这个key删除(相当于解锁) 如果服务器1在操作数据库时候,服务器2也想买票,那么它在给Redis新增key的时候,就会发现此时车次的key已经存在,就可以认为其他服务器此时正在持有锁,那么服务器2就可以等待或者放弃~
Redis中的setnx刚刚好就行对应此场景:当key不存在才可以创建成功,如果存在那么将失败 ~
此时如果某个服务器加锁成功,但意外发生了,服务器崩溃了没有执行解锁操作(删除操作),那么就导致其他服务器没有获取到锁;
可以设置key的过期时间,让这个锁持有多久,如果超过一定时间,那么将自动释放;
使用set ex nx 命令,在设置锁的同时加入过期时间;
注意这是一条命令 不能写成 setnx expire 此时是俩条命令,就有可能造成原子性,仍然会出现无法正确释放锁的问题
所谓的加锁就是在Redis上执行setnx生成一个key 解锁就是删除这个key
那么有没有可能会出现服务器1执行了加锁操作,而服务器2执行了解锁? 那么是有可能的
需要引入一点校验机制:
1.给服务器编号,每个服务器有一个自己的身份标识
2.进行加锁的时候,设置key-value 对应要针对的哪个资源加锁(比如车次),value存储刚才服务器的编号 例如“001:1” ->“001:服务器1”
这样就可以在删除key的时候,校验当前的key是否是当初加锁的服务器,避免删除错误~
在解锁的时候: 1.先查询判断是否删除 2. 才进行删除
此时这个是俩步操作,并不是原子的,就有可能出现问题
很多程序里面都支持内嵌脚本语音.Redis支持Lua语音作为内嵌脚本~
if redis.call('get',KEYS[1]) == ARGV[1] then
return redis.call('del',KEYS[1])
else
1 2 3return 0
end;
过期时间续约问题可能存在,
在给key加锁的时候,设置它的过期时间,那么设置多久呢才是比较合适?
更好的是能够去进行“动态续约”
本质上就是在加锁的服务器上的一个单独的线程,通过这个线程来对锁过期时间进行一个“续约”
❤ 举个具体的例子: 初始情况下设置过期时间为 10s. 同时设定看门狗线程每隔 3s 检测⼀次. 那么当 3s 时间到的时候, 看门狗就会判定当前任务是否完成.
使用Redis作为分布式锁,有没有可能Redis自身挂了?
那么是很有可能的!!!
Redis一般是以集群(至少也是主从的形式,而不是单机)的方式去部署的
服务器1向它的master节点就行加锁操作,这个数据刚写入主节点,主节点挂了,从节点就会新的主节点 由于主节点和从节点之间的数据是同步的,但是有延迟的 那么刚才写入的key还没有被新的主节点接收,此时相当于服务器1就没有成功进行加上锁 但是服务器2仍然可以进行去加上锁,
由于主节点和主节点之间数据都是一致的,相互是备份的关系
在进行加锁的时候,会按照一定的顺序,针对这组Redis都进行加锁 如果某个节点加不上锁,并没有关系,可能是Redis挂了,继续给下一个节点加锁即可 如果此时key加锁成功的节点个数占总的一半,那么可视为加锁成功 同时解锁的时候,会把上述的节点都需要解锁~
简而言之, Redlock 算法的核心就是, 加锁操作不能只写给⼀个 Redis 节点, 而要写个多个!! 分布式系统中任何⼀个节点都是不可靠的. 最终的加锁成功结论是 "少数服从多数的".