Redis持久化有两种方式:
RDB:根据指定规则将内存中的数据保存到硬盘上,这个过程称为“快照”,下次Redis启动的时候加载快照文件到内存。快照文件保存在Redis工作目录中的dump.rdb文件中,可以通过配置文件中的dir和dbfilename设置路径和文件名,如dbfilename newname.rdb。
AOF:将每次执行的命令追加保存到硬盘上(所以推荐使用高速的硬盘),下次Redis启动的时候加载硬盘上的命令来恢复数据。 命令文件保存在Redis工作目录中的appendonly.aof文件中(可以通过配置文件中的dir和appendfilename设置路径和文件名,如appendfilename newname.aof。
RDB:
快照的过程:调用fork()复制当前进程的内存副本到子进程,子进程将内存中的数据写入到硬盘中的临时文件,写完所有数据后将临时文件替换掉.rdb文件。
fork()会使用写时复制技术,即fork()后实际上父子进程是公用一份内存的,只有但父进程要修改内存中的一片数据的时候才会将该片数据复制一份以保证子进程的数据不受影响。写时复制策略保证了fork()后内存占用不会增加一倍,所以当只有2G内存,但Redis占用了1.5G的话,执行fork()也不会超出内存限制,但此时需要确保系统允许申请超过可用内存的空间(linux中为在/etc/sy sctl.conf文件中加入vm.overcommit_memory = 1或执行sy sctl vm.overcommit_memory=1命令)。虽然使用了写时复制技术,但如果fork()的时候Redis就占用了很大的内存,fork()后又有大量的写请求命令的话(写操作会复制数据),那么很有可能就会超出内存限制。
Redis会在以下几种情况下进行快照:
①、根据配置文件中sava参数配置进行快照
如下所示的配置文件,可以存在多个条件,每个条件互不影响,满足的话都会执行:
save 60 1000 //在60秒内有1000个或1000个以上的键被修改,则进行快照 save 300 10 //在300秒内有10个或10个以上的键被修改,则进行快照 save 900 1 //在900秒内有1个或1个以上的键被更改,则进行快照
②、执行SAVE或BGSAVE命令
SAVA会同步的进行快照,所以其它命令就不会被执行,直到快照结束。
BGSAVE会异步的进行快照,所以期间Redis可以照样执行其它命令,执行BGSAVE命令会立即返回OK,可以通过LASTSAVE命令来获取最后一次成功执行快照的具体时间(返回UNIX时间戳)。
③、执行FLUSHALL命令
FLUSHALL命令会清空Redis中所有的数据,如果配置文件中配置了sava参数的话,那么在清空数据前会进行一次快照。
④、执行复制时
当设置了主从模式的时候,Redis会在复制初始化的时候进行快照。
当Redis异常退出后,就会丢失快照之后进行的操作数据,保险的方法还是使用AOF模式。
AOF
默认AOF功能没有开启,可以通过配置文件的appendonly参数来开启,如appendonly yes。实际上,默认情况下Redis是每隔一秒才将命令写入到硬盘文件的,可以通过配置文件来调整:
appendfsync everysec //每秒写入 appendfsync always //每次命令就写入 appendfsync no //每30秒写入一次
aof文件中也许有一些重复的数据,比如进行了三条数据更新的命令,实际上只保存最后一条就行。Redis提供了对aof文件的重写命令BGREWRITEAOF,执行后其会将这种重复的命令进行剔除和优化操作,以减小文件大小。也可以通过配置文件来设置自动重写aof文件:
auto-aof-rewrite-min-size 64mb //设置文件超过这个大小后才可以进行重新操作 auto-aof-rewrite-percentage 10 //当aof文件大小超过上一次重写时大小的10%的时候进行重写(之前没重写过则以启动时aof文件大小为标准依据)
缓存问题
一般我们是先从缓存中取数据,缓存中不存在数据的话再去数据库中取,数据库中有数据的话将其更新到缓存,数据库没数据的话就不做处理。获取缓存会存在以下三个问题:
①、缓存穿透:缓存和数据库中都没有数据,而用户不断的发起请求,比如发起请求id为“-1”的数据,导致数据库压力过大。解决方案有两种:一个是接口层增加校验,如用户鉴权校验,id校验等。一个是数据库无数据的话更新缓存数据值为null,然后设置键的过期时间为3秒等较短的时间(设置较短的时间防止数据库中已经有了数据但还是一直获取的缓存中的null数据)。
②、缓存击穿:缓存中没有数据但数据库中有数据,比如服务刚起来或者缓存数据刚失效,如果此时有大量用户并发的话,会引起数据库压力较大。解决方案有两种:一个是使用锁,如下所示。一个是设置热点数据永不过期。
String getValue(String key) throws Exception { String value = getValueFromRedis(key); if (value = null) { //Redis中不存在 if (lock.tryLock()) { //获得锁 try { value = getDateFromDB(key); if (value != nullptr) { setDateToRedis(key, value); } } finally { lock.unLock(); //释放锁 } } else { //获取锁失败,等待50毫秒后再去获取数据 Thread.sleep(50); getValue(key); } } return value; }View Code
③、缓存雪崩:缓存中数据大批量过期,当查询量很大的时候会引起数据库压力过大。解决方案有两种:一个是防止每个键的过期时间相同。一个是设置热点数据永不过期。