1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-06-16 18:10:13 +08:00

增加Redis持久化内容

This commit is contained in:
Snailclimb 2018-07-31 15:50:13 +08:00
parent a17a2800c9
commit 0da14f8496
5 changed files with 127 additions and 9 deletions

View File

@ -0,0 +1,118 @@
非常感谢《redis实战》真本书本文大多内容也参考了书中的内容。非常推荐大家看一下《redis实战》这本书感觉书中的很多理论性东西还是很不错的。
为什么本文的名字要加上春夏秋冬又一春,哈哈 ,这是一部韩国的电影,我感觉电影不错,所以就用在文章名字上了,没有什么特别的含义,然后下面的有些配图也是电影相关镜头。
![春夏秋冬又一春](https://user-gold-cdn.xitu.io/2018/6/13/163f97071d71f6de?w=1280&h=720&f=jpeg&s=205252)
**很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。**
Redis不同于Memcached的很重一点就是**Redis支持持久化**而且支持两种不同的持久化操作。Redis的一种持久化方式叫**快照snapshottingRDB**,另一种方式是**只追加文件append-only file,AOF**.这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
## 快照snapshotting持久化
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后可以对快照进行备份可以将快照复制到其他服务器从而创建具有相同数据的服务器副本Redis主从结构主要用来提高Redis性能还可以将快照留在原地以便重启服务器的时候使用。
![春夏秋冬又一春](https://user-gold-cdn.xitu.io/2018/6/13/163f97568281782a?w=600&h=329&f=jpeg&s=88616)
**快照持久化是Redis默认采用的持久化方式**在redis.conf配置文件中默认有此下配置
```
save 900 1 #在900秒(15分钟)之后如果至少有1个key发生变化Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后如果至少有10个key发生变化Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后如果至少有10000个key发生变化Redis就会自动触发BGSAVE命令创建快照。
```
根据配置快照将被写入dbfilename选项指定的文件里面并存储在dir选项指定的路径上面。如果在新的快照文件创建完毕之前Redis、系统或者硬件这三者中的任意一个崩溃了那么Redis将丢失最近一次创建快照写入的所有数据。
举个例子假设Redis的上一个快照是235开始创建的并且已经创建成功。下午306时Redis又开始创建新的快照并且在下午308快照创建完毕之前有35个键进行了更新。如果在下午306到308期间系统发生了崩溃导致Redis无法完成新快照的创建工作那么Redis将丢失下午235之后写入的所有数据。另一方面如果系统恰好在新的快照文件创建完毕之后崩溃那么Redis将丢失35个键的更新数据。
**创建快照的办法有如下几种:**
- **BGSAVE命令** 客户端向Redis发送 **BGSAVE命令** 来创建一个快照。对于支持BGSAVE命令的平台来说基本上所有平台支持除了Windows平台Redis会调用fork来创建一个子进程然后子进程负责将快照写入硬盘而父进程则继续处理命令请求。
- **SAVE命令** 客户端还可以向Redis发送 **SAVE命令** 来创建一个快照接到SAVE命令的Redis服务器在快照创建完毕之前不会再响应任何其他命令。SAVE命令不常用我们通常只会在没有足够内存去执行BGSAVE命令的情况下又或者即使等待持久化操作执行完毕也无所谓的情况下才会使用这个命令。
- **save选项** 如果用户设置了save选项一般会默认设置比如 **save 60 10000**那么从Redis最近一次创建快照之后开始算起当“60秒之内有10000次写入”这个条件被满足时Redis就会自动触发BGSAVE命令。
- **SHUTDOWN命令** 当Redis通过SHUTDOWN命令接收到关闭服务器的请求时或者接收到标准TERM信号时会执行一个SAVE命令阻塞所有客户端不再执行客户端发送的任何命令并在SAVE命令执行完毕之后关闭服务器。
- **一个Redis服务器连接到另一个Redis服务器** 当一个Redis服务器连接到另一个Redis服务器并向对方发送SYNC命令来开始一次复制操作的时候如果主服务器目前没有执行BGSAVE操作或者主服务器并非刚刚执行完BGSAVE操作那么主服务器就会执行BGSAVE命令
如果系统真的发生崩溃用户将丢失最近一次生成快照之后更改的所有数据。因此快照持久化只适用于即使丢失一部分数据也不会造成一些大问题的应用程序。不能接受这个缺点的话可以考虑AOF持久化。
## **AOFappend-only file持久化**
与快照持久化相比AOF持久化 的实时性更好因此已成为主流的持久化方案。默认情况下Redis没有开启AOFappend only file方式的持久化可以通过appendonly参数开启
```
appendonly yes
```
开启AOF持久化后每执行一条会更改Redis中的数据的命令Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同都是通过dir参数设置的默认的文件名是appendonly.aof。
![春夏秋冬又一春](https://user-gold-cdn.xitu.io/2018/6/13/163f976818876166?w=400&h=219&f=jpeg&s=91022)
**在Redis的配置文件中存在三种同步方式它们分别是**
```
appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no #让操作系统决定何时进行同步
```
**appendfsync always** 可以实现将数据丢失减到最少不过这种方式需要对硬盘进行大量的写入而且每次只写入一个命令十分影响Redis的速度。另外使用固态硬盘的用户谨慎使用appendfsync always选项因为这会明显降低固态硬盘的使用寿命。
为了兼顾数据和写入性能,用户可以考虑 **appendfsync everysec选项** 让Redis每秒同步一次AOF文件Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
**appendfsync no** 选项一般不推荐这种方案会使Redis丢失不定量的数据而且如果用户的硬盘处理写入操作的速度不够的话那么当缓冲区被等待写入的数据填满时Redis的写入操作将被阻塞这会导致Redis的请求速度变慢。
**虽然AOF持久化非常灵活地提供了多种不同的选项来满足不同应用程序对数据安全的不同要求但AOF持久化也有缺陷——AOF文件的体积太大。**
## 重写/压缩AOF
AOF虽然在某个角度可以将数据丢失降低到最小而且对性能影响也很小但是极端的情况下体积不断增大的AOF文件很可能会用完硬盘空间。另外如果AOF体积过大那么还原操作执行时间就可能会非常长。
为了解决AOF体积过大的问题用户可以向Redis发送 **BGREWRITEAOF命令** 这个命令会通过移除AOF文件中的冗余命令来重写rewriteAOF文件来减小AOF文件的体积。BGREWRITEAOF命令和BGSAVE创建快照原理十分相似所以AOF文件重写也需要用到子进程这样会导致性能问题和内存占用问题和快照持久化一样。更糟糕的是如果不加以控制的话AOF文件的体积可能会比快照文件大好几倍。
**文件重写流程:**
![文件重写流程](https://user-gold-cdn.xitu.io/2018/6/13/163f97f9bd0eea50?w=380&h=345&f=jpeg&s=14501)
和快照持久化可以通过设置save选项来自动执行BGSAVE一样AOF持久化也可以通过设置
```
auto-aof-rewrite-percentage
```
选项和
```
auto-aof-rewrite-min-size
```
选项自动执行BGREWRITEAOF命令。举例假设用户对Redis设置了如下配置选项并且启用了AOF持久化。那么当AOF文件体积大于64mb并且AOF的体积比上一次重写之后的体积大了至少一倍100%的时候Redis将执行BGREWRITEAOF命令。
```
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
```
无论是AOF持久化还是快照持久化将数据持久化到硬盘上都是非常有必要的但除了进行持久化外用户还必须对持久化得到的文件进行备份最好是备份到不同的地方这样才能尽量避免数据丢失事故发生。如果条件允许的话最好能将快照文件和重新重写的AOF文件备份到不同的服务器上面。
随着负载量的上升,或者数据的完整性变得 越来越重要时,用户可能需要使用到复制特性。
参考:
《Redis实战》
[深入学习Redis2持久化](https://www.cnblogs.com/kismetv/p/9137897.html)
> 欢迎关注我的微信公众号:"**Java面试通关手册**"(一个有温度的微信公众号,无广告,单纯技术分享,期待与你共同进步~~~坚持原创分享美文分享各种Java学习资源。)
最后,就是使用阿里云服务器一段时间后,感觉阿里云真的很不错,就申请做了阿里云大使,然后这是[我的优惠券地址](https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=hf47liqn&utm_source=hf47liqn).
![二维码](https://user-gold-cdn.xitu.io/2018/6/13/163f9961b1ee7d0d?w=258&h=258&f=jpeg&s=27034)

View File

@ -12,7 +12,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>

View File

@ -1,5 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
org.eclipse.jdt.core.compiler.compliance=1.5
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.source=1.5
org.eclipse.jdt.core.compiler.source=1.8

View File

@ -27,7 +27,7 @@ public class DesDemo {
byte[] result;
try {
result = DesDemo.encrypt(str.getBytes(), password);
System.out.println("加密后:" + new String(result));
System.out.println("加密后:" + result);
byte[] decryResult = DesDemo.decrypt(result, password);
System.out.println("解密后:" + new String(decryResult));
} catch (UnsupportedEncodingException e2) {

View File

@ -28,9 +28,9 @@ public class RSADemo {
System.out.println("-----------------------------------");
byte[] encryptByPrivateKey = encryptByPrivateKey("123456".getBytes(), privateKey);
byte[] encryptByPublicKey = encryptByPublicKey("123456", publicKey);
System.out.println(new String(encryptByPrivateKey));
System.out.println(encryptByPrivateKey);
System.out.println("-----------------------------------");
System.out.println(new String(encryptByPublicKey));
System.out.println(encryptByPublicKey);
System.out.println("-----------------------------------");
String sign = sign(encryptByPrivateKey, privateKey);
System.out.println(sign);
@ -40,9 +40,9 @@ public class RSADemo {
System.out.println("-----------------------------------");
byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey);
byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey);
System.out.println(new String(decryptByPublicKey));
System.out.println(decryptByPublicKey);
System.out.println("-----------------------------------");
System.out.println(new String(decryptByPrivateKey));
System.out.println(decryptByPrivateKey);
}