【InnoDB ClusterSet】快速部署

news2024/11/24 14:35:49

快速部署 InnoDB ClusterSet

文章目录

  • 快速部署 InnoDB ClusterSet
    • 前言
    • 前期准备
    • 架构设计
    • 部署过程
      • 1. 使用配置账号通过 MySQL Shell 连接到 InnoDB Cluster 任一成员
      • 2. 为主 InnoDB Cluster 实例设置变量
      • 3. 创建以当前集群作为主集群的 ClusterSet
      • 4. 为每个独立服务器实例添加配置账户
      • 5. 在集群集中创建副本集群
      • 6. 向副本集群中添加两个副本成员实例
      • 7. 验证集群集的状态
      • 8. (可选)向集群集中添加新的副本集群
      • 9. 重新引导路由
      • 10. (可选)在另一台服务器上新建并引导一个路由
      • 11. 测试使用路由对 MySQL 经典协议的实际影响
      • 12. 测试使用路由对 MySQL X 协议的实际影响
      • (解释)正常配置的测试结果差异
    • 结论

前言

回顾前面我们搭建了 InnoDB Cluster 集群,基于组复制技术可以提供容错、高可用、弹性伸缩、故障转移、负载均衡、连接重定向、自动选主等功能,而且支持单主、多主两种模式,适合在稳定、低延迟网络下部署集群,不支持(集群级)容灾。(一般来讲,容灾是指跨地域容灾;当然一些架构将实例部署在不同机架、机房也可以提供实例级、机房级容灾,但不适合对网络的时效性、延迟要求高的系统)InnoDB Cluster 的核心亮点是高可用。 此外,一个 InnoDB 集群最多支持 9 个成员实例,横向扩展受限。

为了弥补 InnoDB Cluster 不支持容灾的不足,出现了 InnoDB ClusterSet 。为了提供最大程度的容灾能力,InnoDB ClusterSet 将可用性置于一致性之上。而且 InnoDB ClusterSet 因不限制副本集群数量而可无限 横向扩展(Scale-out)。InnoDB ClusterSet 在主集群与副本集群间建立名为 clusterset_replication 的 ClusterSet 复制通道来异步复制数据,该复制过程由底层的组复制技术管理。在遇到问题或故障时,首先主集群尝试在组内自我协调问题;如若不行,主发生故障,主集群内部会自动选主, clusterset_replication 也会将复制源切换到主集群的新主上;某些情况下如有必要可以进行 **受控切换(Controled Switchover)**到某一可选的副本集群;如果主集群无法满足 多数派(法定人数,Quorum),先尝试手动修复,如若不行,万不得已之下才选择 紧急切换(Emergency Failover)。InnoDB ClusterSet 虽提供了读取流量的无限横向扩展,却未解决单点写入的问题,甚至主集群还不支持多主模式。InnoDB ClusterSet 的核心亮点是容灾。

前期准备

  1. 一个满足 8.1 节 InnoDB ClusterSet 要求 的已部署的至少三节点的 InnoDB Cluster 集群,作为 InnoDB ClusterSet 的主集群。
  2. 一些独立的(推荐至少三个)额外服务器实例,按你的规划组成 InnoDB ClusterSet 的副本集群。
  3. (可选)一个额外服务器实例,作为第二个副本集群,仅提供备份和有限读取能力。
  4. 所有实例的 MySQL 版本在 8.0.27 以上。这是 InnoDB ClusterSet 的要求。我选择的是最新的 GA 版 8.0.31 。
  5. MySQL 账号,配置账号、管理员账号、路由账号,均已在部署 InnoDB Cluster 时配置过了。
  6. 其他软件要求,MySQL Shell 8.0.31,MySQL Router 8.0.31 (要求最低为 8.0.27)。

有关用 Systemd在一台服务器上运行多个 MySQL 服务器实例的方法,详见 使用 systemd 管理多个 MySQL 服务器实例 ,及 2.5.9 节 使用 systemd 管理 MySQL 服务器实例 。

架构设计

架构图

部署过程

1. 使用配置账号通过 MySQL Shell 连接到 InnoDB Cluster 任一成员

[root@ic-source ~]$ mysqlsh --quiet-start=2 ic_config@ic-source

2. 为主 InnoDB Cluster 实例设置变量

 MySQL  ic-source:33060+ ssl  JS > cluster=dba.getCluster()

提示
在确保主集群可用时,也可以直接在 mysqlsh 命令中使用--cluster选项完成前两步的等价操作:

[root@ic-source ~]$ mysqlsh --quiet-start=2 --cluster ic_config@ic-source

--clusterset选项

3. 创建以当前集群作为主集群的 ClusterSet

 MySQL  ic-source:33060+ ssl  JS > cs = cluster.createClusterSet('myClusterSet',{clusterSetReplicationSslMode: 'REQUIRED'})

验证 ClusterSet 状态为HEALTHY

 MySQL  ic-source:33060+ ssl  JS > cs.status()

查看 ClusterSet 状态 1
查看 ClusterSet 状态的扩展信息。该扩展信息展示 InnoDB ClusterSet 拓扑中的每个集群的详细状态,即包含集群集内每一个集群中的每个成员实例的信息。

 MySQL  ic-source:33060+ ssl  JS > cs.status({extended: 1})

查看 ClusterSet 状态的扩展信息 1

注意
请记住这两个命令,此后不再赘述,只截图对照。

4. 为每个独立服务器实例添加配置账户

分别为每个独立服务器实例添加配置账户,这些独立服务器实例之后将按你的期望组成集群集中的一个或多个副本集群。注意,这里使用的是 root@`%` 账号,初始没有,需要创建。 为什么使用它?因为初始化的实例只有 4 个本地账号,需要建立一个远程连接账号,一般首选 root@`%` 账号,注意授权时必须使用WITH GRANT OPTION选项

 MySQL  ic-source:33060+ ssl  JS > dba.configureInstance('root@ic-source:3307',{clusterAdmin: 'ic_config'})
 
 MySQL  ic-source:33060+ ssl  JS > dba.configureInstance('root@ic-replica1:3307',{clusterAdmin: 'ic_config'})
 
 MySQL  ic-source:33060+ ssl  JS > dba.configureInstance('root@ic-replica2:3307',{clusterAdmin: 'ic_config'})
 
 MySQL  ic-source:33060+ ssl  JS > dba.configureInstance('root@ic-replica1:3308',{clusterAdmin: 'ic_config'})

为每个独立服务器实例添加配置账户

5. 在集群集中创建副本集群

 MySQL  ic-source:33060+ ssl  JS > rs1 = cs.createReplicaCluster('ic-source:3307','replicaCluster01',{recoveryProgress: 1, recoveryMethod: 'CLONE'})

这里recoveryProgress: 1显示详细静态进度信息,默认值为 2 显示动态进度条。recoveryMethod: 'CLONE'表示恢复方式为 克隆 ,初始实例没有用户数据,选择该方式最快。

在集群集中创建副本集群

6. 向副本集群中添加两个副本成员实例

向副本集群中添加两个副本成员实例,以组成一个三节点的副本集群。

 MySQL  ic-source:33060+ ssl  JS > rs1.addInstance('ic_config@ic-replica1:3307', {recoveryMethod: 'CLONE'})
 
 MySQL  ic-source:33060+ ssl  JS > rs1.addInstance('ic_config@ic-replica2:3307', {recoveryMethod: 'CLONE'})

向副本集群中添加两个副本成员实例

7. 验证集群集的状态

 MySQL  ic-source:33060+ ssl  JS > cs.status({extended: 1})

7. 验证集群集的状态

8. (可选)向集群集中添加新的副本集群

这次我们创建一个只有一个成员的副本集群,我在测试环境做这步的目的是验证是否支持创建只有一个成员的副本集群。生产环境这么做的目的是只做备份和少量分担读取流量,供本地应用使用。

 MySQL  ic-source:33060+ ssl  JS > cs.createReplicaCluster('ic-replica1:3308','replicaCluster02',{recoveryProgress: 1, recoveryMethod: 'CLONE'})

8. (可选)向集群集中添加新的副本集群

检查集群集状态。


在这里插入图片描述

 MySQL  ic-source:33060+ ssl  JS > cs.describe()

在这里插入图片描述

9. 重新引导路由

因为我在部署 InnoDB Cluster (即现在的主集群)时已经引导过了路由,现在因集群拓扑由 InnoDB Cluster 升级为 ** InnoDB ClusterSet** ,路由配置也需要相应地改变。

[root@ic-source mysql]$ mysqlrouter --bootstrap ic_config@ic-source:3306 --account myRouter1 --user=mysqlrouter --conf-use-sockets --name 'routerSource' --force
 MySQL  ic-source:33060+ ssl  JS > cs.listRouters()

在这里插入图片描述

10. (可选)在另一台服务器上新建并引导一个路由

mysqlrouter --bootstrap ic_config@ic-replica1:3306 --account myRouter2 --user=mysqlrouter --conf-use-sockets --name 'routerReplica1' --force

ic-source 上的 mysqlrouter 状态:
ic-source 上的 mysqlrouter 状态
ic-replica1 上的 mysqlrouter 状态:
ic-replica1 上的 mysqlrouter 状态

这里遇到两个 Bug ,一是不能用ClusterSet.setRoutingOption()设置全局选项;二是以前引导的路由历史记录永远存在,路由器名称还出现重名,虽然拼接完主机名后并不重名。我这误操作一共搞了 5 个路由器。。。

 MySQL  ic-source:33060+ ssl  JS > cs.listRouters()

在这里插入图片描述

查看 mysql 内的表mysql_innodb_cluster_metadata.routers发现的确有 5 条,这元数据也是不动态更新的!
在这里插入图片描述
为了矫正它,我需要截断这张表,并重新引导路由器并重启。

mysql> truncate table mysql_innodb_cluster_metadata.routers;

在 ic-source 上重新引导两个路由,第二个路由需要使用-d 目录选项创建自包含目录结构:

[root@ic-source ~]$ mysqlrouter --bootstrap ic_config@ic-source:3306 --account myRouter1 --user=mysqlrouter --conf-use-sockets --name 'pcPriRouter' --force
[root@ic-source ~]$ mysqlrouter --bootstrap ic_config@ic-source:3307 --account myRouter1 --user=mysqlrouter --conf-use-sockets -d /var/lib/mysqlrouter/rc01PriRouter --name 'rc01PriRouter' --force

在 ic-replica1 上重新引导两个路由:

[root@ic-replica1 ~]# mysqlrouter --bootstrap ic_config@ic-replica1:3306 --account myRouter2 --user=mysqlrouter --conf-use-sockets --name 'pcRep1Router' --force
[root@ic-replica1 ~]# mysqlrouter --bootstrap ic_config@ic-replica1:3307 --account myRouter2 --user=mysqlrouter --conf-use-sockets -d /var/lib/mysqlrouter/rc01Rep1Router --name 'rc01Rep1Router' --force

选取 5 个空闲端口,我选的是 [6456-6459](对应默认的 MySQL 4 个端口 [6446-6459]),8445(对应默认的 HTTP 端口 8443)。在自包含目录内的mysqlrouter.conf文件内搜索port,将 5 个 port 以此改为如上 5 个端口,保存退出。

改之前记得使用如下命令验证一下端口是否空闲。

lsof -i:端口

然后,使用 mysqlsh 配置各路由的选项。

 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("replica1::pcRep1Router","stats_updates_frequency",15)
Routing option 'stats_updates_frequency' successfully updated in router 'replica1::pcRep1Router'.
 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("replica1::rc01Rep1Router","stats_updates_frequency",30)
Routing option 'stats_updates_frequency' successfully updated in router 'replica1::rc01Rep1Router'.
 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("source::pcPriRouter","stats_updates_frequency",5)
Routing option 'stats_updates_frequency' successfully updated in router 'source::pcPriRouter'.
 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("source::rc01PriRouter","stats_updates_frequency",10)
Routing option 'stats_updates_frequency' successfully updated in router 'source::rc01PriRouter'.


以下命令是可选操作,只为了快速部署不建议执行。

 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("source::rc01PriRouter","target_cluster","replicaCluster01")
Routing option 'target_cluster' successfully updated in router 'source::rc01PriRouter'.
 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("replica1::rc01Rep1Router","target_cluster","replicaCluster01")
Routing option 'target_cluster' successfully updated in router 'replica1::rc01Rep1Router'.
 MySQL  ic-source:33060+ ssl  JS > cs.routingOptions()

注意,这里我将副本集群的主成员路由的目标集群设置为它自己所在的副本集群了。同样,也将副本集群的从成员路由的目标集群设置为它自己所在的副本集群了。简言之,设置副本集群的路由指向它自己。这会导致后面出现的问题,而这是我有意设计的。如果你想搭建一个健康合理的集群,请不要像我这样做。

使用各自创建的自包含目录中的start.sh脚本启动那两个自包含路由。

root@ic-source ~]$ . /var/lib/mysqlrouter/rc01PriRouter/start.sh 
[root@ic-replica1 ~]# sh /var/lib/mysqlrouter/pcRep1Router/start.sh 

在这里插入图片描述

在这里插入图片描述

查验路由选项及配置信息。
在这里插入图片描述
在这里插入图片描述

11. 测试使用路由对 MySQL 经典协议的实际影响

使用 **mysql_config_editor ** 执行如下命令创建 login-path ,可用于快速启动 mysql 会话。

[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6446 -h ic-source -G pcPriRw -p 
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6447 -h ic-source -G pcPriRO -p 
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6456 -h ic-source -G rc01PriRW -p 
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter1 -P 6457 -h ic-source -G rc01PriRO -p
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6446 -h ic-replica1 -G pcRep1RW -p
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6447 -h ic-replica1 -G pcRep1RO -p
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6456 -h ic-replica1 -G rc01Rep1RW -p 
Enter password: 
[root@ic-source rc01PriRouter]$ mysql_config_editor set -u myRouter2 -P 6457 -h ic-replica1 -G rc01Rep1RO -p  
Enter password: 

下面开始验证端口 6446,6447,6456,6457

  1. 验证主集群主成员上的路由的 RW 端口(--login-path=pcPriRW)。三次均是一样的正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcPriRW -e 'select @@hostname,@@port;'

在这里插入图片描述

  1. 验证主集群主成员上的路由的 RO 端口(--login-path=pcPriRO)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcPriRO -e 'select @@hostname,@@port;' 

在这里插入图片描述

  1. 验证副本集群主成员上的路由的 RW 端口(--login-path=rc01PriRW)。副本集群是只读的,不支持 RW 请求,是正确输出。

点我,跳转回正确结果

[root@ic-source rc01PriRouter]$ mysql --login-path=rc01PriRW -e 'select @@hostname,@@port;'

在这里插入图片描述
查看日志可以发现路由信息说目标集群是副本集群,不接受 RW 连接。
在这里插入图片描述

  1. 验证副本集群主成员上的路由的 RO 端口(--login-path=rc01PriRO)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01PriRO -e 'select @@hostname,@@port;'

在这里插入图片描述

  1. 验证主集群从成员上的路由的 RW 端口(--login-path=pcRep1RW)。主集群从成员虽是只读的,但此路由的目标集群指向的是主集群,所以支持 RW 请求,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcRep1RW -e 'select @@hostname,@@port;'

在这里插入图片描述

  1. 验证主集群从成员上的路由的 RO 端口(--login-path=pcRep1RO)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=pcRep1RO -e 'select @@hostname,@@port;'

在这里插入图片描述

  1. 验证副本集群从成员上的路由的 RW 端口(--login-path=rc01Rep1RW)。副本集群是只读的,不支持 RW 请求了,是正确输出。

点我,跳转回正确结果

[root@ic-source rc01PriRouter]$ mysql --login-path=rc01Rep1RW -e 'select @@hostname,@@port;'

在这里插入图片描述
在这里插入图片描述

  1. 验证主集群从成员上的路由的 RO 端口(--login-path=rc01Rep1RO)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01Rep1RO -e 'select @@hostname,@@port;'

在这里插入图片描述

12. 测试使用路由对 MySQL X 协议的实际影响

MySQL X 协议,即 MySQL Shell 、 MySQL AdminAPI 、MySQL Connectors 等使用的更贴近开发者的协议,对应于 X DevAPI 。在 CI/CD、DevOps 的浪潮下,MySQL 也积极跟进,下了一步好棋。未来 DBA 行业将迎来更多的挑战,传统的只会使用、管理某些数据库的 DBA 已经要被淘汰了,未来企业对 DBA 开发能力的要求可能不仅限于 Shell、Python 脚本,还要与开发人员联动,甚至在开发人员不在时自己亲身上阵手撸代码改 Bug 。。。好,闲话不多说了。

MySQL Shell 支持保存密码功能,所以仅首次登录时需要输入密码,默认不保存。故我这里就不需要像上面一样做额外的准备工作了。

下面开始验证端口 6448,6449,6458,6459

  1. 验证主集群主成员上的路由的 RW 端口(ic-source:6448)。三次均是一样的正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-source:6448

在这里插入图片描述

  1. 验证主集群主成员上的路由的 RO 端口(ic-source:6449)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-source:6449

在这里插入图片描述

  1. 验证副本集群主成员上的路由的 RW 端口(ic-source:6458)。副本集群是只读的,不支持 RW 请求,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-source:6458

在这里插入图片描述
但在/root/.mysqlsh/mysqlsh.log日志里没有提现报错,有点意外。
在这里插入图片描述

  1. 验证副本集群主成员上的路由的 RO 端口(ic-source:6459)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-source:6459

在这里插入图片描述

  1. 验证主集群从成员上的路由的 RW 端口(ic-replica1:6448)。主集群从成员虽是只读的,但此路由的目标集群指向的是主集群,所以支持 RW 请求,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-replica1:6448

在这里插入图片描述
注意看主集群主成员上的日志里确实有相应的路由信息。
在这里插入图片描述

  1. 验证主集群从成员上的路由的 RO 端口(ic-replica1:6449)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-replica1:6449

在这里插入图片描述

  1. 验证副本集群从成员上的路由的 RW 端口(ic-replica1:6458)。副本集群是只读的,不支持 RW 请求了,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-replica1:6458

在这里插入图片描述

  1. 验证副本集群从成员上的路由的 RO 端口(ic-replica1:6459)。按 RR(Round-Robin) 算法每两次一轮询,是正确输出。
[root@ic-source rc01PriRouter]$ mysqlsh --sqlx -e 'select  @@hostname,@@port;' --uri=myRouter1@ic-replica1:6459

在这里插入图片描述

说明
其实 第 11 步 也可以改用 mysqlsh 使用--sqlc选项来测试 MySQL 经典协议,而且操作起来更简单,因为只需要修改主机名和接口号就可以了,无需额外准备工作。

(解释)正常配置的测试结果差异

如果你没做 上面那步修改目标集群的操作 ,第 11 、12 步的测试结果将是不一样的。第 11 、12 步的各自的测试结果中都只有第 3 、第 7 条出现了异常,原因正是因为我将它们的路由器的目标集群修改为副本集群了。

测试结果具体差异如下(此处采用上面的编号以方便比较):

 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("replica1::rc01Rep1Router","target_cluster",null);
Routing option 'target_cluster' successfully updated in router 'replica1::rc01Rep1Router'.
 MySQL  ic-source:33060+ ssl  JS > cs.setRoutingOption("source::rc01PriRouter","target_cluster",null);
Routing option 'target_cluster' successfully updated in router 'source::rc01PriRouter'.
 MySQL  ic-source:33060+ ssl  JS > cs.routingOptions()
{
    "domainName": "myClusterSet", 
    "global": {
        "invalidated_cluster_policy": "drop_all", 
        "stats_updates_frequency": 0, 
        "target_cluster": "primary"
    }, 
    "routers": {
        "replica1::pcRep1Router": {
            "stats_updates_frequency": 15
        }, 
        "replica1::rc01Rep1Router": {
            "stats_updates_frequency": 30
        }, 
        "source::pcPriRouter": {
            "stats_updates_frequency": 5
        }, 
        "source::rc01PriRouter": {
            "stats_updates_frequency": 10
        }
    }
}

修改后的状态,也是正常应该配置的状态,我指的是target_cluster选项。我之所以这么做的原因是想测试验证自己的想法。

修改后的状态,也是正常应该配置的状态

第 11 步中的 3 和 7
3. 验证副本集群主成员上的路由的 RW 端口(--login-path=rc01PriRW)。虽然副本集群是只读的,但路由将 RW 请求路由到目标集群——主集群上了,是正确输出。

[root@ic-source rc01PriRouter]$ mysql --login-path=rc01PriRW -e 'select @@hostname,@@port;'

在这里插入图片描述

  1. 验证副本集群从成员上的路由的 RW 端口(--login-path=rc01Rep1RW)。虽然副本集群是只读的,但路由将 RW 请求路由到目标集群——主集群上了,是正确输出。
[root@ic-source rc01PriRouter]$ mysql --login-path=rc01Rep1RW -e 'select @@hostname,@@port;'

在这里插入图片描述
想要对比,点这里 跳转到 3 ,跳转到 7 。

同样,第 12 步中的 3 和 7 也是如此。此处不再赘述,只给出测试结果。

第 12 步中的 3 :
第 12 步中的 3
第 12 步中的 7 :
第 12 步中的 7

结论

综上,我们通过部署 InnoDB ClusterSet 充分学习了如何部署、使用 InnoDB 集群集的知识,并且证明了集群集的确是只有一个主节点可以写入,其他节点均只能读取,而不管它是在主集群还是副本集群中。而且,主集群与副本集群间的复制是异步的,不支持半同步复制。

另外,在领略了 MySQL Router 在路由、故障转移、负载均衡、重定向方面的表现后,若真按照 MySQL 推荐的方案,在每个应用服务器上都部署一个 MySQL Router ,确实能最大程度地保证应用透明无感知的高可用服务。以上就是本篇的全部内容,不忙的话点个赞吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/80124.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

382. 链表随机节点-哈希表法

382. 链表随机节点-哈希表法 给你一个单链表,随机选择链表的一个节点,并返回相应的节点值。每个节点 被选中的概率一样 。 实现 Solution 类: Solution(ListNode head) 使用整数数组初始化对象。 int getRandom() 从链表中随机选择一个节点…

Vue Element动态生成的表单如何用 el-form 校验

<el-form :model"dynamicValidateForm" ref"dynamicValidateForm" label-width"100px" class"demo-dynamic"><el-form-item prop"email" label"邮箱" :rules"[{ required: true, message: 请输入…

研究良久,终于发现了他代码写的快且bug少的原因

前言 读者诸君&#xff0c;今日我们适当放松一下&#xff0c;不钻研枯燥的知识和源码&#xff0c;分享一套高效的摸鱼绝活。 我有一位程序员朋友&#xff0c;当时在一个团队中开发Android应用&#xff0c;历经多次考核后发现&#xff1a; 在组内以及与iOS团队的对比中: 他的任…

java项目请求url存在特殊字符 400错误

java项目请求url特殊字符 400错误 1 现象 请求路径带特殊字符&#xff0c;就会400错误&#xff0c;这就泄露了服务器版本和报错信息&#xff0c;无疑是敏感信息泄露&#xff0c;实属安全漏洞。 补充项目环境&#xff1a;springmvc、tomcat 8.5.59 2 原因 经排查和报错信息…

STM32--ADC模数转换器

学习江科大自化协stm32教程记录的笔记 ADC模数转换器 ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器 ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 DAC是数字-模拟转换器&#xff0c;但是P…

AI 助你轻松剪视频 # AutoCut

如果你还在犯愁每次剪视频都要反复听才能下手&#xff0c;不妨试试AutoCut , AI 大神李沐开源的一个剪辑神器&#xff0c;使用 Python 开发&#xff0c;它可以通过字幕来剪切视频。AutoCut 对你的视频自动生成字幕。然后你选择需要保留的句子&#xff0c;AutoCut 将对你视频中对…

C语言:变量的深入理解

文章目录一.什么是变量C语言中为什么要有类型&#xff1f;C语言中的类型为什么有这么多种呢&#xff1f;定义变量的本质为什么需要定义变量定义变量的本质定义变量时的规则二.深刻理解signed/unsigned定义的变量1.运算时的符号位2.数据的存储情况3.unsigned定义时的小细节三.大…

Android 13 VTS HIDL interface 解析

Android 13 VTS Introduction Android 13已经发布&#xff0c;VTS testcase发生很多变化&#xff0c;在此博客中对其每个测试项目进行流程介绍。 这里先对VTS 做一个介绍&#xff1a; VTS是vendor test suite简称&#xff0c;意为供应商测试套件。目的是确保Vendor层实现的兼容…

Spring Boot自动装配原理

Spring Boot自动装配原理1.Spring Boot 入口2.SpringBootApplicationSpringBootConfigurationComponentScanEnableAutoConfiguration判断自动装配开关是否打开获取EnableAutoConfiguration注解中的 exclude 和 excludeName获取需要自动装配的所有配置类最后3.总结1.Spring Boot…

Github访问量过百万!阿里内部至尊级分布式事务手册,实至名归

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。简单的说&#xff0c;就是一次大的操作由不同的小操作组成&#xff0c;这些小的操作分布在不同的服务器上&#xff0c;且属于不同的应用&#xff0c;分布式…

[附源码]JAVA毕业设计养生药膳推荐系统(系统+LW)

[附源码]JAVA毕业设计养生药膳推荐系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术…

[LeetCode周赛复盘] 第 323 场周赛20221211

[LeetCode周赛复盘] 第 323 场周赛20221211 一、本周周赛总结二、 [Easy] 6257. 删除每行中的最大值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6258. 数组中最长的方波1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6259. 设计内存分配器1. 题目描述2. 思路分析3. 代码…

web前端期末大作业【足球网页】学生网页设计作业源码

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

基于C++实现(控制台)单位职工管理系统(数据结构)【100010017】

1需求分析 1.1 问题描述 对单位的职工进行管理&#xff0c; 包括插入、 删除、 查找、 排序等功能。 1.2 问题要求 职工对象数不必很多&#xff0c; 便于一次读入内存&#xff0c; 所有操作不经过内外存交换。 &#xff08;1&#xff09; 由键盘输 入职工对象&#xff0c;…

1564_AURIX_TC275_电压监控寄存器整理

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 1. 如果HSM保护开启了&#xff0c;那么访问修改其他的bit就会导致一个总线错误。 2. SMU中可以配置电压监控的相关alarm是否配置 生效。 这个寄存器是几个欠压阈值的配置&#xff0c;在这…

在Ubuntu中为ROG笔记本安装驱动asusctl

我是在Kubuntu22.04上安装的&#xff0c;系统自带“省电”、“平衡”、“性能”三个电源选项&#xff0c;显卡模式切换是拿nvidia驱动切换的&#xff0c;所以目前装的这个驱动我只用到了灯光调节功能。 文章目录介绍安装安装asusctl卸载显卡模式切换驱动supergfxctl使用方法启用…

LwIP——无操作系统启动流程

目录 启动流程 虚拟网卡控制块 发送流程 接收流程 总结 启动流程 通过阅读正点原子的无操作系统移植工程的源码&#xff0c;可以总结出LwIP的无操作系统的启动流程。 前面一些都是基于其他的外设的初始化&#xff0c;我们只关心这里lwip_comm_init()&#xff0c;这个函数的…

eve-ng ubuntu 20.04 设置iptables

eve-ng ubuntu 20.04 设置iptables一、设置方法1、建立iptables规则开机加载脚本2、建立iptables规则关机/重启保存脚本3、添加可执行权限4、保存当前iptables规则到配置文件二、命令解释一、设置方法 1、建立iptables规则开机加载脚本 sudo vi /etc/network/if-pre-up.d/ipt…

(十一)Vue之条件渲染

文章目录v-if与v-else-if、v-else一起使用v-show关于 <template>标签Vue学习目录 上一篇&#xff1a;&#xff08;十&#xff09;Vue之绑定样式 v-if v-if&#xff0c;默认为true&#xff0c;如果为false&#xff0c;底层实现是直接把这个节点删除 适用于&#xff1a;…

MLE,MAP,经验风险最小化,结构风险最小化,邻域风险最小化

文章目录Maximun likelihood extimationMaximum A Posterior EstimationVicinal Risk Minimization, VRMMaximun likelihood extimation 最大似然估计:样本->参数,完全相信观测到的数据,最优可能产生这些看到的数据的模型就是最理想的模型. 频率学派认为,参数是一个常数,不…