来自 技术 2019-04-17 的文章

分布式系列十二: Redis高级主题 - 罪恶斯巴克

持久化

Redis 支持持久化, 其持久化数据有两种方式. 两种可以同时使用. 如果同时使用, Reids 在重启时将使用 AOF 方式来还原数据.

RDB

按照一定策略定时同步内存的数据到磁盘.文件名 dump.rdb

snapshot: 快照复制. Redis在指定情况下触发快照: (1) 按配置的规则;(2) save 或 bgsave 命令执行;(3) flushall 命令; (4)执行复制

配置的规则: save seconds exchange 当在 seconds 指定的时间内, key 的数量更改大于 exchange 时发生快照.

save 或 bgsave 命令: 执行快照同步操作, 注意save这个操作会暂时阻塞客户端请求. bgsave则不会阻塞

flushall: 清除内存所有数据, 只要规则不为空, redis就会执行快照

执行复制:

快照原理

fork 复制一份当前进程的副本, 这个进程是子进程, 负责同步持久化到磁盘. 而父进程负责处理客户端请求.

快照的优缺点:

缺点: 可能会丢失数据, 在下一次快照前宕机优点: 最大化Redis的性能, 父子进程职责分离

AOF

保存命令到磁盘, 也就是持久化日志.

配置: appendonly yes 启动aof. 默认的文件名是 appendonly.aof.

配置 auto-aof-rewrite-percentage 100 当 aof 文件与上一次文件的大小相比, 超过配置的百分比就进行重写

配置 auto-aof-rewrite-min-size 64m 限制允许重写最小 aof 文件大小, 即小于64m时不重写

aof 重写原理: aof 重写是安全的. 相当于同时将命令追加到现有的aof文件, 同时写入新的 aof 临时文件, 临时文件最终将覆盖原 aof 文件.

同步磁盘数据: aof机制会将命令记录到aof文件, 但实际是同步到操作系统的缓存区, 最终由操作系统同步到磁盘. 可以通过下面配置修改策略appendsync always 每次执行写入就同步, 安全但影响性能appendsync everysec 每一秒执行appendsync no 不执行同步, 由操作系统去执行, 效率高但不安全

aof 文件损坏后的修复, 使用redis-check-aof-fix

集群

master/slave 复制

主从方式, 从是只读的, slave也可以有自己的slave.

slave节点上配置 slaveof masterip masterport

配置 slave-serve-stale-data no 可以用来保证数据同步后再做其他操作

命令info replication可以查看信息.

实现原理:

slave 连接到 master 后, 会向 master 发送 SYNC 命令.master 收到命令后, 会做两件事(1) 执行bgsave;(2)master 将收到的修改命令存入缓冲区, 再将命令传输给slave

复制方式:

基于rdb文件复制无硬盘复制 配置rpli-diskless-sync yes增量复制 PSYNC master run id. offset

命令replconf listening-port 6379可以用来查看复制过程

缺点: 无法做master选举

sentinale 哨兵模式监控master选举配置文件为sentinel.conf

配置节点: sentinel monitor mymaster 192,168,11,111 6379 2 最后的2为投票数

是高可用方案, 但不是高性能方案

集群

原理

Reids有slot槽的概念: redis中有16384个. 根据key的 CRC16 算法, 取得的结果与槽数取模.落入的槽的索引是固定的. 然后根据节点数将槽的范围确定到每个节点上.

当节点新增和删除时, 节点的槽范围发生变化, 数据迁移需要人工干预.

三方方案

redis shardding : jedis支持codis : 代理, 分片和数据迁移自动化twemproxy :

缓存穿透问题数据库中不存在的对象查询后也缓存.

if(objJson!=null){ redisService.expire("key",timeout:3*60);} else { redisService.expire("key",timeout:5); //5秒内不查询数据库}synchronized 方法, 不理想, 排队影响性能.同步块, 需要双重判定, 否则阻塞的线程均会查询数据库, 代码如下

String value = redisService.get("key");if(value==null){ synchronized(lock){ value = redisService.get("key"); //多线程再次查询,防止数据库多次查询 if(value==null){ value = dao.get(); redisService.set("key",value); if(objJson!=null){ redisService.expire("key",timeout:3*60); } else { redisService.expire("key",timeout:5); //5秒内不查询数据库 } } }}

使用ReentrantLocktryLock(),else中线程等待一段时间, 这样不阻塞进程

使用分布式锁

if(redisService.setNx("key")==true){ //查询数据库}不设超时时间或设置一个较长的超时时间, 然后判断当前时间是否超过缓存时间, 结合分布式锁, 超过则刷新缓存. 此解决方法可能会发生少量数据不一致的情况.

采用何种策略需要结合实际需求, 是保证可用, 还是保证一致性? 对于一致性要求不高的场景, 可能最后一种方案可以彻底解决击穿问题. 对于一致性要求较高的场景, 使用同步/锁的方式会更好.

标签:   记事本      PHP菜鸟   
上一篇:it是什么职业?
下一篇:没有了