分布式缓存Redis_2 Redis持久化和一致性解决方案

                                           分布式缓存Redis

                                        2 Redis持久化和一致性解决方案

                                                                                                                                                                                             田超凡

                                                                                                                                                                                        20191122

 

转载请注明原作者

 

1 Redis数据存储模式

Redis提供了五种基本数据类型来方便我们在Redis服务器中存放缓存数据(String、Hash、Set、Sorted Set、List),对于Java对象数据在Redis中的存取方式有两种常用实现方案:

  1. .基于JSON序列化机制存放对象数据

存:将Java对象序列化为JSON字符串存入Redis

取:从Redis中通过Key读取缓存数据字符串,反序列化为Java对象

使用JSON序列化存放Java对象的优点是可读性好,可以兼容不同语言对Redis中存取数据的操作(目前市面上主流的编程语言如Python/PHP/C#等都提供了对JSON轻量级数据交换格式的兼容和序列化机制),在Redis客户端中读取Redis缓存数据的时候查看结果也更直观。

但是JSON序列化机制的弊端就是数据安全性较差、存取效率较低,正是因为JSON是一个应用广泛的数据交换格式,导致存储在Redis的数据可以通过Redis客户端直接查看到存储的JSON字符串,很明显是不太安全的,这种方式不适合存储安全性较高的信息和敏感信息,同时因为这种方式总是以字符串的形式存取Redis缓存数据,实现方式过于单一,不够灵活,导致每次存取对象数据的时候都需要基于JSON序列化和反序列化实现。

使用JSON序列化机制存放对象数据-代码实现

@Component
public class RedisUtils {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    public void setString(String key, String value) {
        setString(key, value, null);
    }

    public void setString(String key, String value, Long timeOut) {
        stringRedisTemplate.opsForValue().set(key, value);
        if (timeOut != null) {
            stringRedisTemplate.expire(key, timeOut, TimeUnit.SECONDS);
        }
    }

    public String getString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }
}

 

 

@RestController
public class IndexController {
    @Autowired
    private RedisUtils redisUtils;

    @RequestMapping("/setRedis")
    public void setRedisKey(UserEntity userEntity) {
        redisUtils.setString("userEntity", JSONObject.toJSONString(userEntity));
    }

    @RequestMapping("/getRedis")
    public UserEntity setRedisKey() {
        String userEntityJson = redisUtils.getString("userEntity");
        UserEntity userEntity = JSONObject.parseObject(userEntityJson, UserEntity.class);
        return userEntity;
    }
}

 

 

  1. .基于Redis内置序列化机制存放二进制对象数据

存:将Java对象直接存到Redis服务器中,对于实现而言可以直接存取Java对象,但是实际存储的形式是二进制字节码。

取:从Redis服务器中读取二进制数据并直接转换为各类Java对象。

使用Redis内置序列化机制存放二进制对象数据的优点是实现方式灵活、存取效率高,因为存取都是针对二进制数据来操作的,二进制数据相当于Java文件编译后生成的.class字节码文件,使用Redis存取二进制对象数据对于Java对象的存取而言,执行效率更高,同时二进制字节码格式的数据相对于JSON格式的数据而言更加安全,数据隐蔽性更强。

但是Redis内置序列化机制存放二进制对象数据也是有瓶颈的,因为这里的二进制数据格式实际保存的是编译后的class字节码,这种数据格式只有Java语言可以操作和使用,其他语言是无法操作二进制字节码数据的,因此这种实现方式只适用于Java语言背景下的数据存取,并且存放的二进制数据可读性很差,必须通过Java语言来解析,因此基于二进制格式存取Redis数据的时候对不同平台不同语言的兼容性差,只能使用Java语言来实现二进制数据的存取。

 

基于Redis内置序列化机制存放二进制对象数据-代码实现

@Component
public class RedisTemplateUtils {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    public void setObject(String key, Object object) {
        redisTemplate.opsForValue().set(key, object);
    }

    public Object getObjet(String key) {
        return redisTemplate.opsForValue().get(key);
    }

}

 

@Data
public class UserEntity implements Serializable {
    private Long userId;
    private String userName;
}

 

 

注意:不论是采用JSON序列化机制存放对象数据还是使用Redis内置序列化机制存放二进制对象数据,需要被序列化的类必须实现Serializable接口

 

2 Redis持久化机制

Redis持久化机制主要是出于对容灾和数据同步的考虑。

Redis存储的数据都是保存在内存中,不是文件的形式,这就导致如果Redis服务器关机或者出现问题宕机之后,如果没有使用持久化机制就会导致Redis数据丢失,造成无法挽回的严重后果。

Redis持久化机制就是用来实现Redis数据的持久化,采用不同的持久化策略实现方式来对Redis服务器中的数据进行及时的同步备份,这样每次Redis服务器启动的时候都会默认先加载备份的数据文件,有效解决Redis宕机之后数据丢失的问题,实现Redis服务器数据的持久化操作。

Redis持久化也可以理解为Redis数据同步,实现Redis数据同步的方式有两种:全量同步和增量同步。

 

3 全量同步增量同步

全量同步:使用定时(避开高峰期)或者采用周期的方式实现数据同步

Redis的RDB持久化模式就是一种全量同步的实现机制。

 

增量同步:采用对Redis服务器数据行为的操作实现数据同步

Redis的AOF持久化模式就是一种增量同步的实现机制。

 

全量同步(RDB)与增量同步(AOF)的区别:

全量同步:定时同步,同步效率高,同步数据大概率丢失(每次同步间隔时间越长,最新同步数据丢失的概率就越大)

增量同步:每次对Redis写操作都会同步,是一种偏实时的同步方式(always/everysec),提供了两种同步策略,因为需要经常执行同步,导致同步效率较低,但是能够最大限度保证同步数据的完整性。

注意:Redis默认开启的是RDB同步模式

 

4 RDB和AOF同步实现Redis持久化

RDB

Redis默认采用RDB方式实现数据的持久化,以快照的形式将数据持久化到磁盘的是一个二进制的文件dump.rdb, 在redis.conf文件中搜索“dump.rdb “。

 

Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:

save 900 1    #在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。

save 300 10   #在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。

save 60 10000  #在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。

set(包含增加和覆盖)、del

例如:

set name yushengjun

set name mayikt

del name

 

RDB全量同步底层是采用fork子线程的方式实现数据同步

AOF

在Redis的配置文件中存在三种同步方式,它们分别是:

appendfsync always     #每次有数据修改发生时都会写入AOF文件,能够保证数据不丢失,但是效率非常低。

appendfsync everysec  #每秒钟同步一次,可能会丢失1s内的数据,但是效率非常高。

appendfsync no          #从不同步。高效但是数据不会被持久化。

直接修改redis.conf中 appendonly yes

建议最好还是使用everysec 既能够保证数据的同步、效率也还可以。

AOF增量同步官方推荐的同步策略是everysec,即每秒同步一次,最大限度同时保障备份数据的准确性(即使最坏的情况最新一次同步后宕机,也只会丢失1s内的缓存数据)并提高同步效率(每秒同步一次,不是每次写都同步)。

always 表示每次Redis服务器写数据都会执行AOF数据同步

everysec 表示每隔1s会执行一次AOF数据同步

 

AOF增量同步是以执行命令的形式基于缓冲区机制实现同步

 

3 Redis和MySQL数据一致性问题

在使用Redis作为分布式缓存的项目中,读取缓存数据的策略是:先读取Redis缓存数据,此时会有两种情况:

如果没有读到Redis缓存数据,那么会再读取MySQL数据库中的数据,把读取到的数据重新写入Redis缓存。

如果读取到了Redis缓存数据,则直接做进一步处理,不会再查询MySQL数据库,降低数据库访问压力,提高数据查询效率,实现分布式缓存。

 

4 基于SpringBoot注解整合Redis

使用SpringBoot内置的@Cacheable注解绑定MySQL查询方法可以实现Redis和MySQL的数据同步,也就是会自动执行上面说到的Redis缓存执行策略,实现步骤如下:

  1. .在SpringBoot启动类标注@EnableCaching启用Redis注解配置
  2. .在需要在Redis和MySQL数据库之间保持一致性的持久层查询方法上使用@Cacheable注解标注,表示建立Redis缓存数据和对应MySQL数据库查询方法查询结果的一致性,当MySQL数据库查询的数据在Redis缓存数据中不存在的时候,会自动同步MySQL查询数据到Redis缓存,如果cacheName指定的缓存数据在Redis中存在,那么就不会执行这个对应的MySQL查询。

 

需要格外注意的是@Cacheable的key表示Redis数据已执行需要绑定的MySQL数据库查询方法名,方法名必须加单引号,否则会抛出异常。

 

@RestController
public class MemberController {
    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/findMemberAll")
    @Cacheable(cacheNames = "member", key = "'findMemberAll'")
    public List<MemberEntity> findMemberAll() {
        return userMapper.findMemberAll();
    }
}

 

 

public interface UserMapper {
    @Select("select * from users")
    List<MemberEntity> findMemberAll();
}

 

 

spring:
  redis:
    host: 192.168.212.155
    password: 123456
    port: 6379
    database: 1
  #数据库连接配置
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/user?characterEncoding=utf-8&useSSL=false
    username: root
    password: root

 

 

<!--mysql数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

<!--mybatis-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

 

 

开启我们的@EnableCaching

 

5 Redis和MySQL数据一致性问题解决方案

方式1:直接清除Redis的缓存,重新读取数据库即可(比较粗暴,不建议使用,这种方式没有监听MySQL数据变化,频繁执行全量操作,影响Redis服务器执行效率)

方式2:使用MQ消息队列,Redis异步订阅MySQL的binlog日志文件实现增量同步,当数据库执行增删改操作的视乎,binlog日志文件会实时更新,此时会通知订阅它的Redis观察者实现数据同步。(推荐,但是需要手动定义观察者Redis、主题角色MySQL binlog日志文件来实现观察者模式)

方式3:使用Alibaba的canal监听数据库发生的增删改操作并在回调方法中实现Redis数据同步(重新查询数据库数据并存入Redis,全量操作)(推荐,直接使用Alibaba canal已经封装好的监听器API监听数据库数据变化,在回调函数中使用Redis实现数据同步)

 

补充说明:
如果在redis.conf配置文件中同时开启了RDB和AOF数据同步,会优先采用AOF增量同步 ,因为AOF增量同步每次对Redis服务器数据的写行为都会进行数据同步,在同一段时间内而言,AOF执行数据同步的次数比RDB全量同步执行次数更加频繁(因为AOF执行策略是always或者everysec,即要么每次写操作都同步,要么每隔1s同步一次),所以如果同时开启了这两种同步方式,Redis服务器会默认采用AOF增量同步的方式实现数据同步,AOF增量同步的优先级大于RDB全量同步,但是在没有显式指定Redis数据同步实现方式的情况下,Redis默认采用的是RDB全量同步的方式实现数据同步。

 

转载请注明原作者

 

发布了100 篇原创文章 · 获赞 10 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/qq_30056341/article/details/103193714