锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. ShardingSphere读写分离和数据脱敏

ShardingSphere读写分离和数据脱敏

0
  • 软件开发
  • 发布于 2024-08-19
  • 15 次阅读
黄健
黄健

读写分离

读写分离是通过主从的配置方式,将查询请求均匀的分散到多个数据副本,进一步的提升系统的处理能力。

主从架构:读写分离,目的是高可用、读写扩展。主从库内容相同,根据SQL语义进行路由。

分库分表架构:数据分片,目的读写扩展、存储扩容。库和表内容不同,根据分片配置进行路由。

将水平分片和读写分离联合使用,能够更加有效的提升系统性能, 下图展现了将分库分表与读写分离一同使用时,应用程序与数据库集群之间的复杂拓扑关系。


读写分离虽然可以提升系统的吞吐量和可用性,但同时也带来了数据不一致的问题,包括多个主库之间的数据一致性,以及主库与从库之间的数据一致性的问题。 并且,读写分离也带来了与数据分片同样的问题,它同样会使得应用开发和运维人员对数据库的操作和运维变得更加复杂。

读写分离应用方案
在数据量不是很多的情况下,我们可以将数据库进行读写分离,以应对高并发的需求,通过水平扩展从库,来缓解查询的压力。如下:


分表+读写分离
在数据量达到500万的时候,这时数据量预估千万级别,我们可以将数据进行分表存储。

分库分表+读写分离
在数据量继续扩大,这时可以考虑分库分表,将数据存储在不同数据库的不同表中,如下:

透明化读写分离所带来的影响,让使用方尽量像使用一个数据库一样使用主从数据库集群,是ShardingSphere读写分离模块的主要设计目标。

主库、从库、主从同步、负载均衡

  • 核心功能

    • 提供一主多从的读写分离配置。仅支持单主库,可以支持独立使用,也可以配合分库分表使用

    • 独立使用读写分离,支持SQL透传。不需要SQL改写流程

    • 同一线程且同一数据库连接内,能保证数据一致性。如果有写入操作,后续的读操作均从主库读取。

    • 基于Hint的强制主库路由。可以强制路由走主库查询实时数据,避免主从同步数据延迟。

  • 不支持项

    • 主库和从库的数据同步

    • 主库和从库的数据同步延迟

    • 主库双写或多写

    • 跨主库和从库之间的事务的数据不一致。建议在主从架构中,事务中的读写均用主库操作。

读写分离配置

基于上一篇基本应用中使用的工程,对city表配置读写分离

  • 新建一个配置文件application-master-slave.yaml,用于配置读写分离
#datasource
spring:
  shardingsphere:
    datasource:
      names: master,slave0
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding1?useSSL=false&characterEncoding=UTF-8
        username: root
        password: 123456
      slave0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding2?useSSL=false&characterEncoding=UTF-8
        username: root
        password: 123456
    masterslave:
      name:
      master-data-source-name: master
      slave-data-source-names: slave0
      load-balance-algorithm-type: ROUND_ROBIN
    sharding:
      tables:
        city:
          key-generator:
            column: id
            type: SNOWFLAKE
  • application中配置上面新建的配置文件生效
  • 新建测试类 TestMasterSlave
@SpringBootTest
public class TestMasterSlave {
   

    @Resource
    private CityRepository cityRepository;

    @Test
    public void testAdd(){
   
        City city = new City();
        city.setName("shanghai");
        city.setProvince("shanghai");
        cityRepository.save(city);
    }

    @Test
    public void testFind(){
   
        List<City> list = cityRepository.findAll();
        list.forEach(city->{
   
            System.out.println(city.getId()+" "+city.getName()+" "+city.getProvince());
        });
    }

}

写操作路由到主库,读操作路由到从库

强制路由

官网地址如下:
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/manual/sharding-jdbc/usage/hint/

  在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过在外部业务代码中指定路由配置的一种方式,在ShardingSphere中叫做Hint。如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作。

  HintManager主要使用ThreadLocal管理分片键信息,进行hint强制路由。在代码中向HintManager添加的配置信息只能在当前线程内有效。

Hint使用场景:

  • 数据分片操作,如果分片键没有在SQL或数据表中,而是在业务逻辑代码中

  • 读写分离操作,如果强制在主库进行某些数据操作

Hint使用过程:

  1. 编写分库或分表路由策略,实现HintShardingAlgorithm接口
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<Long> {
   
    @Override
    public Collection<String> doSharding(
            Collection<String> availableTargetNames,
            HintShardingValue<Long> shardingValue) {
   
        Collection<String> result = new ArrayList<>();
        for (String each : availableTargetNames){
   
            for (Long value : shardingValue.getValues()){
   
                if(each.endsWith(String.valueOf(value % 2))){
   
                    result.add(each);
                }
            }
        }
        return result;
    }
}
  1. 在配置文件指定分库或分表策略

  2. 在代码执行查询前使用HintManager指定执行策略值

import com.elvis.pojo.City;
import com.elvis.repository.CityRepository;
import org.apache.shardingsphere.api.hint.HintManager;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import javax.annotation.Resource;
import java.util.List;

@SpringBootTest
public class TestHintAlgorithm {
   

    @Resource
    private CityRepository cityRepository;

    @Test
    public void test1(){
   
        HintManager hintManager = HintManager.getInstance();
        hintManager.setDatabaseShardingValue(2L); //强制路由到ds${xx%2}
        List<City> list = cityRepository.findAll();
        list.forEach(city->{
   
            System.out.println(city.getId()+" "+city.getName()+" "+city.getProvince());
        });
    }

}


在读写分离结构中,为了避免主从同步数据延迟及时获取刚添加或更新的数据,可以采用强制路由走主库查询实时数据,使用hintManager.setMasterRouteOnly设置主库路由即可。

数据脱敏

  数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。涉及客户安全数据或者一些商业性敏感数据,如身份证号、手机号、卡号、客户号等个人信息按照规定,都需要进行数据脱敏。

数据脱敏模块属于ShardingSphere分布式治理这一核心功能下的子功能模块。

  • 在更新操作时,它通过对用户输入的SQL进行解析,并依据用户提供的脱敏配置对SQL进行改写,从而实现对原文数据进行加密,并将密文数据存储到底层数据库。

  • 在查询数据时,它又从数据库中取出密文数据,并对其解密,最终将解密后的原始数据返回给用
    户。

Apache ShardingSphere自动化&透明化了数据脱敏过程,让用户无需关注数据脱敏的实现细节,像使用普通数据那样使用脱敏数据。

整体架构

官网地址
https://shardingsphere.apache.org/document/legacy/4.x/document/cn/features/orchestration/encrypt/

  ShardingSphere提供的Encrypt-JDBC和业务代码部署在一起。业务方需面向Encrypt-JDBC进行JDBC编程。


Encrypt-JDBC将用户发起的SQL进行拦截,并通过SQL语法解析器进行解析、理解SQL行为,再依据用户传入的脱敏规则,找出需要脱敏的字段和所使用的加解密器对目标字段进行加解密处理后,再与底层数据库进行交互。

脱敏规则

脱敏配置主要分为四部分:数据源配置,加密器配置,脱敏表配置以及查询属性配置,其详情如下图所示:

  • 数据源配置:指DataSource的配置信息

  • 加密器配置:指使用什么加密策略进行加解密。目前ShardingSphere内置了两种加解密策略:AES/MD5

  • 脱敏表配置:指定哪个列用于存储密文数据(cipherColumn)、哪个列用于存储明文数据(plainColumn)以及用户想使用哪个列进行SQL编写(logicColumn)

  • 查询属性的配置:当底层数据库表里同时存储了明文数据、密文数据后,该属性开关用于决定是直接查询数据库表里的明文数据进行返回,还是查询密文数据通过Encrypt-JDBC解密后返回。

脱敏处理流程

下图可以看出ShardingSphere将逻辑列与明文列和密文列进行了列名映射。

下方图片展示了使用Encrypt-JDBC进行增删改查时,其中的处理流程和转换逻辑,如下图所示。

加密策略解析

ShardingSphere提供了两种加密策略用于数据脱敏,该两种策略分别对应ShardingSphere的两种加解密的接口,即Encryptor和QueryAssistedEncryptor。

  • Encryptor
    该解决方案通过提供encrypt(), decrypt()两种方法对需要脱敏的数据进行加解密。在用户进行INSERT, DELETE, UPDATE时,ShardingSphere会按照用户配置,对SQL进行解析、改写、路由,并会调用encrypt()将数据加密后存储到数据库, 而在SELECT时,则调用decrypt()方法将从数据库中取出的脱敏数据进行逆向解密,最终将原始数据返回给用户。

    当前,ShardingSphere针对这种类型的脱敏解决方案提供了两种具体实现类,分别是MD5(不可逆),AES(可逆),用户只需配置即可使用这两种内置的方案。

  • QueryAssistedEncryptor
    相比较于第一种脱敏方案,该方案更为安全和复杂。它的理念是:即使是相同的数据,如两个用户的密码相同,它们在数据库里存储的脱敏数据也应当是不一样的。这种理念更有利于保护用户信息,防止撞库成功。

    它提供三种函数进行实现,分别是encrypt(), decrypt(), queryAssistedEncrypt()。在encrypt()阶段,用户通过设置某个变动种子,例如时间戳。针对原始数据+变动种子组合的内容进行加密,就能保证即使原始数据相同,也因为有变动种子的存在,致使加密后的脱敏数据是不一样的。在decrypt()可依据之前规定的加密算法,利用种子数据进行解密。queryAssistedEncrypt()用于生成辅助查询列,用于原始数据的查询过程。

    由于queryAssistedEncrypt()和encrypt()产生不同加密数据进行存储,而decrypt()可逆,queryAssistedEncrypt()不可逆。 在查询原始数据的时候,我们会自动对SQL进行解析、改写、路由,利用辅助查询列进行 WHERE条件的查询,却利用 decrypt()对encrypt()加密后的数据进行解密,并将原始数据返回给用户。这一切都是对用户透明化的。

    当前,ShardingSphere针对这种类型的脱敏解决方案并没有提供具体实现类,却将该理念抽象成接口,提供给用户自行实现。ShardingSphere将调用用户提供的该方案的具体实现类进行数据脱敏。

配置数据脱敏

  • 在sharding库中创建user表
USE sharding1;
CREATE TABLE `c_user` (
  `Id` BIGINT(11) NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(256) DEFAULT NULL,
  `pwd_plain` VARCHAR(256) DEFAULT NULL,
  `pwd_cipher` VARCHAR(256) DEFAULT NULL,
  PRIMARY KEY (`Id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
  • 创建实体类
@Entity
@Table(name = "c_user")
public class CUser {
   

    @Id
    @Column(name = "id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    // 逻辑列名
    @Column(name = "pwd")
    private String pwd;

    public String getName() {
   
        return name;
    }

    public void setName(String name) {
   
        this.name = name;
    }

    public String getPwd() {
   
        return pwd;
    }

    public void setPwd(String pwd) {
   
        this.pwd = pwd;
    }

    public Long getId() {
   
        return id;
    }

    public void setId(Long id) {
   
        this.id = id;
    }
}

  • 创建jpa repository类
public interface CUserRepository extends JpaRepository<CUser,Long> {
   

    public List<CUser> findByPwd(String pwd);

}
  • 创建application-encrypt-database.yaml文件
#datasource
spring:
  shardingsphere:
    datasource:
      names: ds0
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/sharding1?useSSL=false&amp;characterEncoding=UTF-8
        username: root
        password: 123456
    encrypt:
      tables:
        c_user:
          columns:
            pwd:
              plainColumn: pwd_plain
              cipherColumn: pwd_cipher
              encryptor: pwdEncrypt
      encryptors:
        pwdEncrypt:
          type: AES
          props:
            aes:
              key:
                value: 123
    props:
      query:
        with:
          cipher:
            column: false #是否使用密文列查询
  • 创建测试类
import com.elvis.pojo.CUser;
import com.elvis.repository.CUserRepository;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Repeat;

import javax.annotation.Resource;
import java.util.List;

@SpringBootTest
public class TestEncryptor {
   

    @Resource
    private CUserRepository userRepository;

    @Test
    @Repeat(2)
    public void testAdd(){
   
        CUser user = new CUser();
        user.setName("tiger");
        user.setPwd("abc");
        userRepository.save(user);
    }

    @Test
    public void testFind(){
   
        List<CUser> list = userRepository.findByPwd("abc");
        list.forEach(cUser -> {
   
            System.out.println(cUser.getId()+" "+cUser.getName()+" "+cUser.getPwd());
        });
    }

}

  • 如果在配置文件注释掉逻辑列到实体列明文的映射配置,则只会存入秘文列

原文链接: https://blog.csdn.net/Kiven_ch/article/details/119114941

标签: #ShardingSphere 5 #软件开发 1171 #JAVA 991
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.