学习笔记(2)基本配置以及高性能高可用数据库设计

学习笔记(2)基本配置以及高性能高可用数据库设计

基本配置

微服务设计

gmall-api:抽取所有的bean、模型类、异常、通用类等

gmall-common:抽取工具类,通用类等

gmall_pms:商品管理系统

gmall_ums:用户管理系统

gmall_sms:营销管理系统

gmall_admin_web: 后台管理系统对接前端的web项目

gmall_api: 不包含mapper,impl,mapper应该是每个具体实现里的,impl应该是服务类的真正实现。api保留了接口,javabean。

<!--所有工具类,公共依赖-->
<artifactId>gmall-common</artifactId>
<!--所有的公共api、bean、异常、模型等-->
<artifactId>gmall-api</artifactId>

使用MyBatisPlus逆向生成生成 Mapper 、 Model 、 Service 、 Controller 层代码。下面是其配置步骤

配置整合MyBatisPlus

//在启动类加如下注解

@MapperScan(basePackages = "com.atguigu.gmall.pms.mapper")
//gmall-mbg 逆向工程代码生成器
public class CodeGenerator {

    public static void main(String[] args) {

        String moduleName = "pms";

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir("E:\\ideaworkshop\\gmall-parent\\gmall-mbg" + "/src/main/java");
        gc.setAuthor("Lfy");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖
        gc.setServiceName("%sService");    //去掉Service接口的首字母I
        gc.setIdType(IdType.AUTO); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式
        gc.setBaseColumnList(true);
        gc.setBaseResultMap(true);//生成每个xml的baseResultMap

        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://192.168.2.137:3307/gmall_"+moduleName+"?useUnicode=true&useSSL=false&characterEncoding=utf8");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(moduleName); //模块名
        pc.setParent("com.atguigu.gmall");
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude(moduleName + "_\\w*");//设置要映射的表名
        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix(pc.getModuleName() + "_");//设置表前缀不生成
        strategy.setEntityTableFieldAnnotationEnable(true);//是否生成实体时,生成字段注解

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        //strategy.setLogicDeleteFieldName("is_deleted");//逻辑删除字段名
        //strategy.setEntityBooleanColumnRemoveIsPrefix(true);//去掉布尔值的is_前缀

        //自动填充
        //TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT);
        //TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE);
        //ArrayList<TableFill> tableFills = new ArrayList<>();
        //tableFills.add(gmtCreate);
        //tableFills.add(gmtModified);
        //strategy.setTableFillList(tableFills);

        //strategy.setVersionFieldName("version");//乐观锁列

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        // 6、执行
        mpg.execute();
    }
}

在这里插入图片描述

Springboot默认带了一个数据源,比阿里的效率跟高

如上一篇笔记所写,各服务需要远程调用,下面介绍dubbo的配置整合

配置整合dubbo

//启动类加如下注解
@EnableDubbo
//配置文件
dubbo.application.name=gmall-pms
dubbo.registry.address=zookeeper://192.168.2.134:2181
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
dubbo.consumer.check=false

dubbo控制台:java -jar dubbo-admin-0.0.1-SNAPSHOT.jar

暴露dubbo服务

import com.alibaba.dubbo.config.annotation.Service;
@Slf4j
@Service
@Component
public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements BrandService {}

在这里插入图片描述

@Slf4j打印日志lombok
就不用System.out.println("保存成功...."+byId.getName());了

在这里插入图片描述

日志太多,不方便查看,用es检索

将日志导入logstash

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
    <!--应用名称-->
    <property name="APP_NAME" value="gmall-pms"/>
    <!--日志文件保存路径-->
    <property name="LOG_FILE_PATH" value="${LOG_FILE:-${LOG_PATH:-${LOG_TEMP:-${java.io.tmpdir:-/tmp}}}/logs}"/>
    <contextName>${APP_NAME}</contextName>
    <!--每天记录日志到文件appender-->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE_PATH}/${APP_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern>
            <maxHistory>30</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${FILE_LOG_PATTERN}</pattern>
        </encoder>
    </appender>
    <!--输出到logstash的appender-->
    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <destination>192.168.2.137:4560</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"/>
    </appender>

    <!--日志级别   DEBUG-INFO-WARN-ERROR  -->
    <root level="DEBUG">
        <appender-ref ref="CONSOLE"/>
        <appender-ref ref="FILE"/>
        <appender-ref ref="LOGSTASH"/>
    </root>
</configuration>
<!--引入logback整合logstash的第三方jar包-->
<!-- https://mvnrepository.com/artifact/net.logstash.logback/logstash-logback-encoder -->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>5.3</version>
</dependency>

高性能高可用数据库设计

数据库是整个系统最重要的一环,也是前面限流、缓存、熔断和降级等守卫的最终目标。

主从同步

最容易想到 “防止数据丢失” 的手段就是备份。备份有多种方式,如可以由运维人员手工备份、编写定时备份的脚本,或者通过触发器实现远程的实时备份等,这里我们主要介绍使用 “主从同步” 来实现数据库的备份。

目前主流的关系型数据库都支持主从同步。如图所示,当向主数据库 Master 中写入数据时, Master 会自动将数据备份到两个从数据库 Slaver1 和 Slaver2 中。

图片描述
主从同步的原理实际和 “数据库灾难恢复” 的原理是一致的:都是通过日志,将记录的所有的 DML 重新执行了一次。以 MySQL 为例,当对 MySQL 进行 DML 操作时,相应的增删改语句就会被记录在一个名为 binlog 的日志文件中。之后,主数据库通过网络将 binlog 日志文件的内容发送给从数据库,从数据库在收到后就会解析 binlog 中记录的 DML ,并且将解析后的 DML 写入自己的日志文件中(称为 “relay log”),最后从数据库会把 relay log 中全部的 DML 重新执行一遍,从而实现对主数据库的备份作用,整个过程如图所示。

图片描述

主从配置简要步骤:

​ 1)、主从数据库在自己配置文件中声明需要同步哪个数据库,忽略哪个数据库等信息。并且server-id不能一样

​ 2)、主库授权某个账号密码来同步自己的数据

​ 3)、从库使用这个账号密码连接主库来同步数据

读写分离

有了 “主从同步” 的结构后,还可以进一步实现 “读写分离”。一般而言,我们会选择将数据写入 “主数据库”,而从 “从数据库” 中读取数据。读写分离看似简单,但会大幅度提高数据库的性能。因为对数据库的访问总是 “读多写少”,而数据的并发冲突仅仅发生在 “写” 上。读写分离可以让大量的读请求与较少的写请求相互隔离,因此就不用再考虑读请求的并发冲突问题,因此可以大大降低并发冲突的基数。

MyCat是重量级框架。sharding-jdbc为增强版的数据库驱动,是轻量级框架。

我们使用sharding-jdbc完后读写分离。Sharding-JDBC 采用在 JDBC 协议层扩展分库分表,是一个以 jar 形式提供服务的轻量级组件,其核心思路是小而美地完成最核心的事情。

下面使用sharding-jdbc配置多个数据源

在这里插入图片描述

//配置文件
dataSources:
  db_master: !!com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: jdbc:mysql://192.168.2.137:3307/gmall_pms?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
  db_slave: !!com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.jdbc.Driver
    jdbcUrl: jdbc:mysql://192.168.2.137:3316/gmall_pms?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
masterSlaveRule:
  
  
SpringBoot的牛逼之处:SpringBoot引入某个场景,这个场景的组件就会自动配置好。

比如数据源自动配置:

在这里插入图片描述

分库分表

主从同步是为了备份,而读写分离可以降低并发冲突,从而提高访问效率。 除此以外,我们还要思考如何存储电商平台的大量数据。单个关系型数据库的容量有限,无法存放全部的商品数据,因此就需要 “分而治之”:将商品数据根据一定的规则,拆分并存储到多个数据库中。

分库分表与读写分离都可以在应用层通过拦截器实现。例如,我们可以先根据 hash 算法设计数据与多个数据库之间的映射关系。当接收到 SQL 命令时,就使用拦截器分析这条 SQL 的语义,然后在稍加改造后发送到相应的数据库中。例如,当接收到 "insert into goods (gid , gname , gprice) values ( 1001 , ‘iphone10’ , 9999 ) “时,拦截器就会分析这条 SQL 语句的作用是 “插入”,并且能够得知插入的表名是"goods"以及数据的 id 是"1001”。之后,拦截器就可以根据表名和 id 值的 Hash 映射,寻找到对应的具体数据库,进行插入操作。

很多数据访问的 “增强” 功能都是通过拦截器实现的。例如,MyBatis Plus、PageHelper 等数据访问层框架,以及 MyCat 等中间件的底层也都采用的是” 拦截器 “技术。

在实现方式上,对于分库分表和读写分离等数据操作而言,既可以在代码层直接实现,也可以借用 MyCat 等数据库中间件实现。一般而言,对于简单的逻辑非常建议通过编码直接实现,这也是效率最高的做法。如果逻辑较为复杂,或者对数据管理有较高要求,就可以使用 MyCat 等中间件。中间件能够让我们像操作单数据库那样实现分库分表以及读写分离,大大的降低了开发成本,但既然是 “中间件” 就必定会给系统额外增加一层组件,会造成一定程度的性能损耗和网络延迟。

通常可以将主从同步、分库分表和读写分离结合使用。如图所示,当应用程序发来请求时,首先判断是读请求还是写请求。

  • 如果是写请求,可以使用 “分库分表” 技术将数据写入 Master1 或 Master2 中。之后, Master1 或 Master2 再根据 “主从同步” 将写入的数据备份到自己的两个 Slaver 中;
  • 如果发来的是读请求,就可以直接从 Slaver 中读取。但要注意,因为采用了 “分库分表”,所以在读取时,需要同时读取 Master1 和 Master2 对应的 Slaver 数据。

图片描述

图片描述

数据库高可用

我们可以使用 “主从同步” 实现某个 Master 数据库的高可用,但如何保证整个数据库架构的高可用?举个例子,如果使用了 MyCat 来实现读写分离和分库,那么一旦 MyCat 挂掉,就会导致整个数据库也无法使用,如图所示。

图片描述
在分布式或集群架构中,经常会遇到这种 “单点问题”,典型的解决方案就是 “去中心化”。“去中心化” 的核心是以下两点:

  • 与搭建集群类似,通过配置多个服务来防止单点故障;

  • 多个服务之间通过 “心跳机制” 保持连接;

    在使用 “去中心化” 时,通常会根据我们自己配置的优先级,先从多个服务中选取一个作为主节点。当这个主节点挂点后会通过 “心跳机制” 告知其他节点,其他节点再根据选举策略选择一个新的主节点,然后由这个新节点作为新的入口。如下图所示,可以先配置两个 MyCat ,并首先选择使用 MyCat-A 。当 MyCat-A 挂掉之后,“心跳机制” 会将 MyCat-A 的故障告知 MyCat-B 、 MyCat-C ,随后 MyCat-B 和 MyCat-C 根据选举策略选出一个新的入口(例如 MyCat-B ),之后 MyCat-B 就会承担起分库分表和读写分离的入口。

图片描述

提示:在实际开发时,通常可以使用 HaProxy + Keepalived 结合实现多个服务的 “去中心化”。

以 MySQL 为例,介绍了主从同步、读写分离、分库分表,这些都是使用关系型数据库应对海量数据的常见解决方案,不仅适用于秒杀系统,也适用于大部分项目的存储需求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值