文章目录
redis
为什么单线程还这么快?
- 官方说单线程已经足够redis的使用,redsi的性能瓶颈在于网络IO和内存大小
- 单线程避免了多线程上下文切换
- 避免多线程并发加锁问题
Redis 性能测试
redis-benchmark -n 10000 -q
redis 分区
分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
分区的优势
- 通过利用多台计算机内存的和值,允许我们构造更大的数据库。
- 通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。
分区的不足
redis的一些特性在分区方面表现的不是很好:
- 涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
- 涉及多个key的redis事务不能使用。
- 当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。
- 增加或删除容量也比较复杂。redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。
分区类型
Redis 有两种类型分区。 假设有4个Redis实例 R0,R1,R2,R3,和类似user:1,user:2这样的表示用户的多个key,对既定的key有多种不同方式来选择这个key存放在哪个实例中。也就是说,有不同的系统来映射某个key到某个Redis服务。
范围分区
最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的Redis实例。
比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。
这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各 种对象的映射表,通常对Redis来说并非是好的方法。
哈希分区
另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:
- 用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。
- 对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。
Redis 指定分区
key 在同一个分区可以加 {same}keyxxx 来实现,redis 只会计算 {} 里的内容的 hash 作为分区依据
redis KEY 过期策略
- 定时过期 主动 定时器CPU消耗过大
- 惰性过期 被动 只有在下次操作时去判断是否删除,内存消耗大
- 定时过期 一定数量 采样 惰性+定时
redis 淘汰策略
策略说明
- lru: least recently used 最近最少使用,时间维度 (maxmemory-samples 采样设置)
- lfu:least frequently used 最不常用,热度维度
- random: 随机
- ttl 过期时间
策略
- volatile-lru 有TTL的KEY且最近最少使用
- allkeys-lru 所有KEY且最近最少使用
- volatile-lfu 有TTL的KEY且最不常用
- allkeys-lfu 所有的KEY且最不常用
- volatile-random 随机删除有TTL的key
- allkeys-random 随机删除所有key
- volatile-ttl 只删除带TTL的key
- noeviction 不删除key 内存满了只能读不能写
redis 持久化
AOF和RDB可同时开启
RDB Redis DataBase
- Redis 默认的持久化方案。在指定的时间间隔内,执行指定次数的写操作,则会将内存中的数据写入到磁盘中。即在指定目录下生成一个dump.rdb文件。Redis 重启会通过加载dump.rdb文件恢复数据
- 可以手动触发RDB 命令为save和bgsave
- save会阻塞
- bgsave后台运行不会阻塞 redis会fork一个子进程去执行
AOF Append Only File
- Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
- 三种方式
- appendfsync always : 每次命令都会记录
- appendfsync erverysec : 1秒一次
- appendfsync no : 系统自动
- rewrite 1.和上一次重写次数相同则重新 2.达到一定大小则重写
- 压缩重复指令
Redis分布式
- 命令 slaveof ip:port 加入集群
- 命令 slaveof no one 退出集群
- 主节点读写 从节点只读
- 主从同步:将RDB文件发送给从节点(可以设置发送超时)
- 实时同步:主节点将命令实时发送给从节点去执行
- 增量复制:如果从节点断开一段时间再链接到集群后会根据偏移量做增量复制
哨兵机制Sentinel
有几个redis节点就要有几个Sentinel
哨兵启动命令为:./redis-sentinel …/sentinel.conf
- 工作模式
- 客户端>>sentinel>>master
- 投票机制
- Raft算法 动画地址:http://thesecretlivesofdata.com/raft/
- 监控
- 通过投票选取master
- master挂掉重新选取并修改选取后master的配置文件为主节点
- master恢复修改配置文件为从节点
Redis Cluster
Redis Cluster 是3.0以后提出的版本. 槽:1~16383
-
分区策略
- 虚拟槽(slot)
- 集群只和槽有关系,和数据无关,可以能做到平滑增加和删除节点
- key指定在同一个分区可以加 xxx{same}xxx 来实现,redis 只会计算 {} 里的内容的 hash 作为分区依据
-
master salve
- 拥有主从角色分配、主从切换和高可用
- 所以不需要启动Sentinel
其他
- 一致性哈希 原理: http://www.zsythink.net/archives/1182
- 使用场景: 分库分表,负载均衡等
pipeline 管道
多个命令一起执行后再返回(批量执行),速度较快,但是每次执行条数大小有限制(本地缓存),如果数据量大会影响客户端和服务端性能(对于数据实时性和成功性不是特别严格的话可以使用pipeline)
redis客户端
- jedis 轻量级 线程不安全,可以通过线程池解决安全问题
- lettuce 基于netty实现的线程安全客户端
- redission 分布式客户端,支持分布式的操作
问题
- 先删缓存,在更新数据库 => 采用延迟双删
- del redis -> update sql -> del redis
- redis 热点数据发现
- 客户端
- 代理词 proxy
- 服务端(监控 monitor)只能监控单节点 Facebook开源项目:redis-faina
- 抓包 elk中packetbeat
- 缓存雪崩
- 所有过期时间设置相同,当同时失效后会给数据库带来非常大的压力
- 解决方案
- 随机数过期时间
- 永不过期
- 预更新(定时器扫描更新)
- 每次修改更新缓存
- 双缓存
- 缓存穿透(场景1: 访问数据库为空->reids也为空 场景2: 无限创建用户)
- 1.key为查询条件等 value为特殊字符比如空传
- 2.使用布隆过滤器
面试题
- 如何在海量元素中(例如10亿无序,不定长,不重复)快速判断一个元素是否存在
- 使用布隆过滤器(Bloom Filter)
- 布隆过滤器可以解决缓存穿透
- 脑裂的解决方案
- min-slaves-to-write 1 表示连接到master的最少slave数量
- min-slaves-max-lag 10 slave连接到master的最大延迟时间
以上方案只会使脑裂几率降低,并不能阻止脑裂