当前位置: 首页 > news >正文

国产数据库中读写分离实现机制

在数据库高可用架构下会存在1主多备的部署,备节点可以根据业务场景分发一部分流量以充分利用资源,并减轻主库的压力,因此在数据库的功能上需要读写分离来实现。

  • 充分利用备节点的资源,提升业务的吞吐量;
  • 防止运维等非业务查询访问主节点导致CPU飙升的场景,进而影响业务正常运行;
  • 在实现上考虑到主备时延过大的情况下,业务的容忍度以及规避措施;
  • 事务的处理上,更新操作在主节点而读在备节点,如何保证事务的一致性?

1、OceanBase数据库

OceanBase数据库的读写分离是由ODP(OceanBase Database Proxy)数据库代理实现的,读写分离就是数据在主节点修改后,数据同步到备节点,备节点提供数据的读取功能。OceanBase数据库支持读写分离到表的Partition级别,这也是原生的分布式数据库的优势。
在OceanBase数据库中使用读写分离功能,需要经过两部分设置:

  1. 设置SQL语句为弱读:所谓弱读是指读请求不要求读到最新的数据。对于正常的SQL为强读,使用弱读需要一定的设置。
  2. 修改路由策略:每个副本都可以提供弱读服务(主副本也可以提供读服务),通过设置路由策略,可以优先选择备副本。
1.1 弱读配置

1)在SQL语句中通过Hint

/*+READ_CONSISTENCY(WEAK)*/配置
select /*+READ_CONSISTENCY(WEAK)*/ * from t1;

2)通过OBProxy配置项obproxy_read_consistency设置

alter proxyconfig set obproxy_read_consistency = 1;

配置项默认值0表示强读、1表示弱读。SQL hint是语句级别的,配置项是实例级别的。通常可以配置多个OBProxy实例,选择其中部分OBProxy开启读配置作为运维查询使用。

1.2 修改路由策略

ODP通过配置项proxy_route_policy修改路由策略,通过设置为follower_first和follower_only可以让弱读请求优先发给备副本:

  • follower_first:优先选择备副本,如果备副本都不可用,选择主副本;
  • follower_only:选择备副本,如果备副本都不可用,断开和客户端的连接。

上述两个策略需要根据业务场景选择

2、PolarDB数据库

PolarDB MySQL版本的集群自带读写分离功能,在PolarDB控制台的数据库连接中配置读写模式,包括只读模式和可读可写模式(自动读写分离)。当配置可读可写模式后,写请求会自动转发到主节点,读请求会自动根据各节点的负载(当前未完成的请求数)转发到主节点或只读节点。

在这里插入图片描述

当客户端通过读写分离地址与数据库后端建立连接后,读写分离中间件会自动与主节点和各个只读节点建立连接。在同一个连接内(同一个session内),读写分离中间件会根据各个数据库节点的数据同步程度,来选择合适的节点,在保证数据正确的基础上(写操作之后的读有正确的结果),实现读写请求的负载均衡。另外,在配置可读可写模式后,可以配置主库是否接受读,当设置为否后,普通的读请求将不再发往主节点。而事务内,一致性要求的读请求仍会被发往主节点,以保证业务需求。另外,当所有只读节点出现故障后,读请求也会发往主节点。

在这里插入图片描述

同时读写分离模块自动对集群内的所有节点进行健康检查,当发现某个节点宕机或者延迟超过阈值(全局一致性读超时时间,取值范围:0~60000,默认为20ms)后,PolarDB将不再分配读请求给该节点,读写请求在剩余的健康节点间进行分配,以此确保单个只读节点发生故障时,不会影响应用的正常访问。当节点被修复后,PolarDB会自动将该节点纳回请求分配体系内。

2.1 请求转发逻辑

在配置了可读可写模式下,SQL语句的转发遵循以下规则:

  • 只发往主节点:DML和DDL操作、未开启事务拆分时所有事务请求、自定义函数、存储过程等;
  • 发往只读节点或主节点:非事务中的读请求、COM_STMT_EXECUTE命令等
  • 发往所有节点:所有系统变量的修改、COM_STMT_EXECUTE命令等

在这里插入图片描述

2.2 基于权重的动态负载均衡

PolarDB在可读可写(自动读写分离)模式下仅支持基于活跃请求数负载均衡的策略,优先选择最小活跃(并发)请求数的节点去路由请求,来保证多个只读节点间的负载均衡。该策略基本可以保证流量根据后端节点的负载均衡的路由到不同的后端节点上,即使后端节点的规格不一致,也能较好的进行负载均衡。

为了满足线上业务负载的多样性,PolarDB后续版本引入了基于权重的动态负载均衡策略,为每个节点配置不同的权重后,在后续的路由过程中,权重和并发请求数会同时作为参考标准去动态的调整最终的路由决策。在控制台“数据库代理服务配置”中,可以为不同的节点配置不同的权重,动态权重越大,节点的优先级越高,路由分发的流量也越大。

3、GaussDB数据库(for MySQL)

GaussDB(for MySQL)在创建数据库后,支持配置读写分离功能,通过代理地址将写请求自动访问主节点,读请求按照读权重配比或者活跃连接数情况分发到各个节点。在使用代理地址时,事务请求都会路由到实例的主节点,不保证非事务读的一致性,业务上有读一致性需求可以封装到事务中,可以使用事务拆分功能对事务中写之前的读请求进行拆分。

在GaussDB数据库控制台的数据库代理页面,新增数据库代理,包括代理模式、一致性级别、路由模式和添加的数据库节点。

  • 代理模式:支持读写模式和只读模式,只读模式下仅支持读请求业务,并且不支持DDL、DML操作和临时表操作;
  • 一致性级别:分为最终一致性和会话一致性,由于主备之间存在复制延迟,可能会导致每次读结果存在差异,默认情况只能保证最终一致性。当设置会话一致性后,保证在同一个会话内,保证每次读请求都是上一次更新操作后的结果。
  • 路由模式:包括权重负载和负载均衡,权重负载是根据设置的读权重比较分发请求,负载均衡是根据数据库节点的活跃连接数情况进行读请求分发。

在这里插入图片描述

另外,在SQL中通过hint方式指定发往主节点或只读节点。需要注意的是Hint注释仅作为路由建议,非只读SQL、事务中的场景不能强制路由到只读节点。

  • /FORCE_MASTER/强制路由到主节点;
  • /FORCE_SLAVE/强制路由到只读节点;
4、GoldenDB数据库

GoldenDB数据库的读写分离策略由连接实例级别的策略+语句级别的策略共同决定:

  • 在连接实例的服务端口进行配置,包括不开启读写分离、本地同城策略以及异地策略,其中本地同城策略需要配置权重;
  • SQL语句级别的读写分离策略,通过在SQL语句中增加hint实现,hint包括READMASTER、READBALANCE和READSLAVE

在这里插入图片描述

不同策略的组合如表所示:

在这里插入图片描述

另外,为避免只读数据节点读取的数据长时间和主数据节点不一致,当一个只读数据节点的延迟时间超过设置的延迟阈值,则不论该只读数据节点的读权重是多少,读请求都不会转发至该只读数据节点。在新版本的读写分离配置中,增加了“是否可读主”的配置,选择否后,当主备时延超过阈值时,依旧不会读主节点。

需要注意的是在多分片的实例中,当CN节点配置参数force_read_split后,开启读写分离时,显示的开启事务后,为了保证事务操作的一致性,事务内是不允许更新操作的,只允许只读事务,否则会提示“read-only transaction”的报错。

5、TDSQL数据库(for MySQL)

TDSQL数据库默认支持读写分离功能,包括3种读写分离的配置操作,其中非只读账号由参数gateway.mode.single_backend.rw_split控制:

  • 创建只读账号:创建账号时标记为只读,系统将根据只读策略将读请求发往备机,只读策略的配置分为以下场景:
    • 1:优先选择备机进行读操作,如果备机的延迟都大于设置的延迟,则从主机读取。
    • 2:只选择备机进行读操作,如果所有的备机都大于设置的延迟,则直接报错。
    • 3:只选择备机进行读取,忽略延迟参数,一般用于拉取binlog同步。
  • slave注释:参数rw_split=1,在SQL中增加slave模式的hint标签,将指定的SQL发往备机。比如在SQL中添加/slave/标记,该SQL会发送给备机。注意在mysql客户端使用时添加-c参数来解析注释,/slave/必须为小写,并且前后无空格。
//主机读//
select * from emp order by sal,deptno desc;
//从机读//
/*slave*/ select * from emp order by sal,deptno desc;
  • 全局自动读写分离:参数rw_split=2,配置该参数后会将读请求自动发送所有的备机。如果备机延迟较大,读到的数据可能会有延迟。如果是在事务中的读请求或者autocommit=0, begin/commit/rollback …等情况下,select也会发到主节点。

在这里插入图片描述

官方建议使用第1种和第2种方式配置读写分离,第三种配置方式会有一定程度的性能损耗。

6、OpenGauss数据库

openGauss数据库通过JDBC连接配置,支持一主多备情况下的读写分离,当URL中配置服务器地址时,可以通过URL中的属性标示targetServerType来区分JDBC返回的连接是否区分主机和备机。

  • Master:尝试连接到URL连接串中的主节点,如果找不到就抛出异常;
  • Slave:尝试连接到URL连接串中的备节点,如果找不到就抛出异常;
  • preferSlave:尝试连接到URL连接串中的备数据节点(如果有可用的话),否则连接到主数据节点;
  • any:尝试连接URL连接串中的任何一个数据节点,默认为“any”
##优先连接到备节点
jdbc:opengauss://node1,node2,node3/database?autoBalance=roundrobin&targetServerType=preferSlave
##只连接备机进行只读操作
jdbc:opengauss://node1,node2,node3/database?autoBalance=roundrobin&targetServerType=slave
##只连接主机进行读写操作
jdbc:opengauss://node1,node2,node3/database?autoBalance=false&targetServerType=master

OpenGauss数据库在一主多备的架构下,当主节点宕机的时候通过自主寻主的机制,找到主备切换后的新主。通过JDBC连接中的配置参数hostRecheckSecond,当主机状态发生更改时再次检查主机状态,快速地找到新的主机并恢复连接,默认值为10秒。JDBC连接流程如下:

在这里插入图片描述

  1. 设置参数开启targetServerType=xx。
  2. 判断输入的主机列表是否已经加入了候选主机列表中,如果不存在hostStatusMap中并且更新时间间隔超过10s(hostRecheckSecond)同时HostStatus状态也和现在一致,这时直接加入候选主机列表。
  3. 对列表进行遍历查找对应的节点信息,如果找到了,通过knownStatus查看状态是否一致,一致就执行sql查询语句,之后更新全局变量hostStatusMap及局部变量konwnStatus中存储。
  4. 当前操作的节点状态如果与用户选择的节点状态一致则返回主机连接,之后用户可以根据连接进行读或者写操作。
7、TiDB数据库

在TiDB数据库中读写分离通过follower read机制实现的,follower read是将TiKV读负载从Region的leader副本上offload到follower副本的负载均衡机制,以提升TiDB集群的吞吐能力并降低leader负载。TiDB的Follower Read功能开启通过变量tidb_replica_read配置:

set [session | global] tidb_replica_read = '<目标值>';
  • leader:默认值,将所有的读操作都发送给leader副本处理。
  • follower :选择Region的follower副本完成所有的数据读操作。
  • leader-and-follower:可以选择任意副本来执行读操作,此时读请求会在leader和follower之间负载均衡。
  • prefer-leader:优先选择leader副本执行读操作。当leader副本的处理速度明显变慢时,例如由于磁盘或网络性能抖动,TiDB将选择其他可用的follower副本来执行读操作。
  • closest-replicas:优先选择分布在同一可用区的副本执行读操作,对应的副本可以是leader或follower。如果同一可用区内没有副本分布,则会从leader执行读操作。
  • closest-adaptive:当一个读请求的预估返回结果大于或等于变量 tidb_adaptive_closest_read_threshold 的值时,TiDB会优先选择分布在同一可用区的副本执行读操作。
  • learner:选择learner副本执行读取操作。在读取时,如果当前Region没有learner副本,TiDB会报错。

Tidb数据库的Follower Read在实现机制上是follower节点使用Raft ReadIndex协议确保当前读请求可以读到当前leader上已经commit的最新数据。因此在TiDB层面,Follower Read只需根据负载均衡策略将某个Region的读取请求发送到follower节点。

8、总结

以上是几种国产数据库的读写分离实现,各个数据库厂商根据不同的业务场景在不同程度上实现了读写分离的功能,对比如下所示:

在这里插入图片描述

总体上而言,PolarDB、GaussDB和GoldenDB在读写分离的功能上更为完善,考虑到实例级别、SQL级别以及对事务的影响,还有在主备时延超过阈值时的处理。


参考资料:

  1. https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000000819334
  2. https://help.aliyun.com/zh/polardb/polardb-for-mysql/user-guide/read-or-write-splitting-2
  3. https://support.huaweicloud.com/usermanual-gaussdbformysql/gaussdbformysql_11_0017.html
  4. https://www.goldendb.com/#/docsIndex/docs/instruction_TenantManagement
  5. https://cloud.tencent.com/privatecloud/document/123248530626207744/12325495623387545
  6. https://zhuanlan.zhihu.com/p/445545196
  7. https://docs.pingcap.com/zh/tidb/stable/follower-read#follower-read

相关文章:

国产数据库中读写分离实现机制

在数据库高可用架构下会存在1主多备的部署&#xff0c;备节点可以根据业务场景分发一部分流量以充分利用资源&#xff0c;并减轻主库的压力&#xff0c;因此在数据库的功能上需要读写分离来实现。 充分利用备节点的资源&#xff0c;提升业务的吞吐量&#xff1b;防止运维等非业…...

kubernetes部署dashboard

kubernetes部署dashboard 1. 简介 Dashboard 是基于网页的 Kubernetes 用户界面。 你可以使用 Dashboard 将容器应用部署到 Kubernetes 集群中&#xff0c;也可以对容器应用排错&#xff0c;还能管理集群资源。 你可以使用 Dashboard 获取运行在集群中的应用的概览信息&#…...

FPGA早鸟课程第二弹 | Vivado 设计静态时序分析和实际约束

在FPGA设计领域&#xff0c;时序约束和静态时序分析是提升系统性能和稳定性的关键。社区推出的「Vivado 设计静态时序分析和实际约束」课程&#xff0c;旨在帮助工程师们掌握先进的设计技术&#xff0c;优化设计流程&#xff0c;提高开发效率。 课程介绍 关于课程 权威认证&…...

STM32项目分享:家庭环境监测系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.…...

华为HCIP Datacom H12-821 卷5

1.单选题 下列哪种工具不能被 route-policy 的 apply 子句直接引用? A、IP-Prefix B、tag C、community D、origin 正确答案: A 解析: 因route-policy工具中, apply 后面跟的是路由的相关属性。 但是ip-prefix是用来匹配路由的工具。 2.单选题...

Mongodb数据库基本操作

本文为在命令行模式下Mongodb数据库的基本操作整理。 目录 数据库操作 创建数据库 查看所有数据 查看当前数据库 删除数据库 断开连接 查看命令api 集合操作 查看当前数据库下集合 创建集合 删除当前数据库中的集合 文档操作 插入文档 insertOne()方法 insertMa…...

【机器学习】基于Softmax松弛技术的离散数据采样

1.引言 1.1.离散数据采样的意义 离散数据采样在深度学习中起着至关重要的作用&#xff0c;它直接影响到模型的性能、泛化能力、训练效率、鲁棒性和解释性。 首先&#xff0c;采样方法能够有效地平衡数据集中不同类别的样本数量&#xff0c;使得模型在训练时能够更均衡地学习…...

.NET+Python量化【1】——环境部署和个人资金账户信息查询

前言&#xff1a;量化资料很少&#xff0c;.NET更少。那我就来开个先河吧~ 以下是使用QMT进行量化开发的环境部署和基础信息获取有关操作。 1、首先自己申请券商的QMT权限&#xff0c;此步骤省略。 2、登陆QMT&#xff0c;选择极简模式&#xff0c;或者独立交易模式之类的。会进…...

洛谷 P10584 [蓝桥杯 2024 国 A] 数学题(整除分块+杜教筛)

题目 思路来源 登录 - Luogu Spilopelia 题解 参考了两篇洛谷题解&#xff0c;第一篇能得出这个式子&#xff0c;第二篇有比较严格的复杂度分析 结合去年蓝桥杯洛谷P9238&#xff0c;基本就能得出这题的正确做法 代码 #include<bits/stdc.h> #include<iostream&g…...

深入讲解C++基础知识(一)

目录 一、基本内置类型1. 类型的作用2. 分类3. 整型3.1 内存描述及查询3.2 布尔类型 —— bool3.3 字符类型 —— char3.4 其他整型 4. 有符号类型和无符号类型5. 浮点型6. 如何选择类型7. 类型转换7.1 自动类型转换7.2 强制类型转换7.3 类型转换总结 8. 类型溢出8.1 注意事项 …...

Python爬虫实战:批量下载网站图片

1.获取图片的url链接 首先&#xff0c;打开百度图片首页&#xff0c;注意下图url中的index 接着&#xff0c;把页面切换成传统翻页版&#xff08;flip&#xff09;&#xff0c;因为这样有利于我们爬取图片&#xff01; 对比了几个url发现&#xff0c;pn参数是请求到的数量。…...

使用 JavaScript 获取电池状态

在现代的移动设备和笔记本电脑上&#xff0c;了解电池状态是一项非常有用的功能。使用 JavaScript 可以轻松地获取电池的充电状态、电量百分比等信息。本文将介绍如何使用 JavaScript 访问这些信息&#xff0c;并将其显示在网页上。 1. HTML 结构 首先&#xff0c;我们需要一…...

java—类反射机制

简述 反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息&#xff08;如成员变量&#xff0c;构造器&#xff0c;成员方法等&#xff09;&#xff0c;并能操作对象的属性及方法。反射机制在设计模式和框架底层都能用到。 类一旦加载&#xff0c;在堆中会产生…...

浏览器-服务器架构 (BS架构) 详解

目录 前言1. BS架构概述1.1 BS架构的定义1.2 BS架构的基本原理 2. BS架构的优势2.1 客户端简化2.2 易于更新和维护2.3 跨平台性强2.4 扩展性高 3. BS架构的劣势3.1 网络依赖性强3.2 安全性问题3.3 用户体验局限 4. BS架构的典型应用场景4.1 企业内部应用4.2 电子商务平台4.3 在…...

微型操作系统内核源码详解系列五(四):cm3下svc启动任务

系列一&#xff1a;微型操作系统内核源码详解系列一&#xff1a;rtos内核源码概论篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列二&#xff1a;微型操作系统内核源码详解系列二&#xff1a;数据结构和对象篇&#xff08;以freertos为例&#xff09;-CSDN博客 系列…...

筛质数(暴力法、埃氏筛、欧拉筛)

筛质数&#xff08;暴力法、埃氏筛、欧拉筛&#xff09; 暴力法 思路分析&#xff1a; 直接双for循环来求解质数 如果不设置标记只是简单地执行了break会导致内部循环(由j控制)而不是立即打印i或者跳过它。如果打印语句写到内部循环中&#xff0c;也会导致每个 非素数也被打…...

使用USI作为主SPI接口

代码; lcd_drive.c //***************************************************************************** // // File........: LCD_driver.c // // Author(s)...: ATMEL Norway // // Target(s)...: ATmega169 // // Compiler....: AVR-GCC 3.3.1; avr-libc 1.0 // // D…...

AI播客下载:Eye on AI(AI深度洞察)

"Eye on A.I." 是一档双周播客节目&#xff0c;由长期担任《纽约时报》记者的 Craig S. Smith 主持。在每一集中&#xff0c;Craig 都会与在人工智能领域产生影响的人们交谈。该播客的目的是将渐进的进步置于更广阔的背景中&#xff0c;并考虑发展中的技术的全球影响…...

Flink 窗口触发器

参考&#xff1a; NoteWarehouse/05_BigData/09_Flink(1).md at main FGL12321/NoteWarehouse GitHub Flink系列 9. 介绍 Flink 窗口触发器、移除器和延迟数据等 | hnbian https://github.com/kinoxyz1/bigdata-learning-notes/blob/master/note/flink/Window%26%E6%97%B6…...

Java面试题:解释线程间如何通过wait、notify和notifyAll方法进行通信

在 Java 中&#xff0c;线程间的通信可以通过 wait()、notify() 和 notifyAll() 这三个方法实现。这些方法是 Java 线程 Thread 类的一部分&#xff0c;它们与 synchronized 关键字一起使用&#xff0c;以实现线程间的协调。 基本概念 wait()&#xff1a;当一个线程执行到 wa…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

初探Service服务发现机制

1.Service简介 Service是将运行在一组Pod上的应用程序发布为网络服务的抽象方法。 主要功能&#xff1a;服务发现和负载均衡。 Service类型的包括ClusterIP类型、NodePort类型、LoadBalancer类型、ExternalName类型 2.Endpoints简介 Endpoints是一种Kubernetes资源&#xf…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...

高保真组件库:开关

一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...