Redis
缓存穿透
指查询一个一定不存在的数据,缓存自然是没有的,就到了数据库
解决办法
1.设置拦截器,不合法不到持久层
2.查询不存在的数据,也把空缓存更新到缓存,这样下次就到缓存查询空了
缓存雪崩
如果缓存数据设置的过期时间是相同的,并且Redis恰好将这部分数据全部删光了。这就会导致在这段时间内,这些缓存同时失效,全部请求到数据库中。
1.随机过期时间
2.设置限流(hystrix)
Redis做锁 :不公平锁,效率高 内存
Zk:公平锁 效率低 删除节点和增加节点,可靠性高
先删除缓存 再更新数据库,最后再删除缓存 缓存和数据库一致性
(要考虑高并发,数据库主从,读写 写写,以及2阶段问题)
Redis的所有命令都是原子性的(由于单线程),原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程),即不需要在redis操作上加synchronized和lock。
为什么被设计成单线程,因为是内存操作,时间很短。
但是对于计数的,必须使用incr或者lua脚本
因为计数是先get ,然后在set +1操作
过期时间用setnx,或者lua脚本,防止没有设置过期时间就挂了
Value 要用时间,判断这个value是不是自己的,防止删除另一个锁
时间也有问题,比如分布式系统,每台机器时间可能有重复或者时钟回拨。
Redis 事务不保证原子性,即多个操作某一个失败了,其他不会回滚。
Redis事务可以保证的是多个命令不被其他客户端插入打断,即事务的隔离性。
Redis支持简单的事务:
命令 说明
mutli 代表事务开始
exec 代表事务结束
discard 命令表示停止事务。
watch 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
WATCH 锁定量
MULTI
incr 锁定量
if 锁定量 <= 库存量
减库存
EXEC
Lua脚本 来控制多个redis操作的原子性 ,新建是 时候,把过期时间和set 放在一步,删除的时候 ,是不需要先get,在del的。
但是,为了确保不误删除,首先是get 判断value。
Rdb相当于快照(dump),按照设置的条件把所有内存的保存到rdb文件中,可以有save 和bgsave一个用主线程,另一个另开一个线程,也可以client自己触发。数据多的不采用,因为io太多。并且丢数据比较多。 异步
Aof (binlog)每一个命令都追加到appendonly.aof文件中(除了读操作),os会在内存中缓存write的修改,不会立即写入磁盘,这个是有设定的,有几种策略。
例如对一个key多次修改,其实就最后一个有用
集群:
数据量大,去中心化,分片(在分片前有一个中间件提供算法(可以是本地的一个算法
即 if条件1 到分片1 if条件2 到分片2),分到不同的服务器),每片上有一个主从 异步复制(slave发起)
分片算法被放在哪里?是在分片时需要首先考虑的问题,分片部署方式一般分为以下三种:
(1)在客户端做分片;这种方式在客户端确定要连接的redis实例,然后直接访问相应的redis实例;
(2)在代理中做分片;这种方式中,客户端并不直接访问redis实例,它也不知道自己要访问的具体是哪个redis实例,而是由代理转发请求和结果;其工作过程为:客户端先将请求发送给代理,代理通过分片算法确定要访问的是哪个redis实例,然后将请求发送给相应的redis实例,redis实例将结果返回给代理,代理最后将结果返回给客户端。
(3)在redis服务器端做分片;这种方式被称为“查询路由”,在这种方式中客户端随机选择一个redis实例发送请求,如果所请求的内容不再当前redis实例中它会负责将请求转交给正确的redis实例,也有的实现中,redis实例不会转发请求,而是将正确redis的信息发给客户端,由客户端再去向正确的redis实例发送请求。
上面主要描述了分片的优点,当然分片的存在也有缺陷,例如:
(1) 通常无法支持涉及多键的操作;在redis中有很多一次操作多个key的操作,例如求集合交集的SINTER操作,该操作将涉及到多个键,而这多个键有可能被分片到不同的redis实例中,此时就无法执行这种操作。
(2)Redis的事务操作中涉及多个键时也不能用;
(3)分片将导致数据处理更加复杂;例如在分片过程中,随着redis实例的增加,数据备份等操作都将会变得更加复杂。
(4)Redis目前不支持动态分片操作,扩容和缩容操作都会比较复杂,尤其分片操作部署在客户端时,需要重新配置和启动客户端。在使用过程中缩容用的不多,扩容可以采用后面介绍的预分片策略来缓解此问题。
2用ShardedJedisPool连接Redis maser节点,并将数据根据一致性哈希算法存放到不同的master节点,从而达成数据分片。
虚拟槽分区
哨兵 为了主从自动切换。
redis数据结构
Hash:多属性 只更新一个只取一个
List: 双向链表 对结果分页;在博客引擎实现中,你可为每篇日志设置一个list,在该list中推入进博客评论等等。
Set:没排序的无重复 ip计数
SortSet:
每一个成员都会有一个分数(score)与之关联,Redis正是通过分数来为集合中的成员进行从小到大的排序。需要额外指出的是,尽管SortedSet中的成员必须是唯一的,但是分数(score)却是可以重复的。在SortedSet中添加、删除或更新一个成员都是非常快速的操作,其时间复杂度为O(logn)。由于SortedSet中的成员在集合中的位置是有序的,因此,即便是访问位于集合中部的成员也仍然是非常高效的。
使用场景:
-
可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOP TEN的用户信息。当然也可以利用ZRANK命令通过username来获取玩家的排行信息。最后将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息。
-
SortedSet类型还可用于构建索引数据。
-
建立一个SortedSet中元素个数不要超过 1 W。
主从同步
完整同步
发送命令让主创建并发送rdb文件,并向从发送缓冲区里的写命令。
部分同步
挤压缓冲区(主维护)
偏移量(主从都维护)
acid
A 原子性 一个操作要么全成功 要么全失败
C 一致性
I 隔离性 没有中间状态,一个事务查询不能查询另一个事务的中间状态(未提交的),也就是互相隔离的
D 持久性 一旦事务结束事务一定会保存,不会丢失回滚 通过 commit log 以及缓存
比如人员集合有姓名 地址信息,要修改地址
Memcache 要把整个人员对象取出,转型 在修改地址 在把对象放回去
MemCachedClient mc = new MemCachedClient();
String key = “key”;
Object value = mc.get(key);
Redis 直接 更新地址字段即可,采用hashmap 结构