一、基本了解
mysql实现读写分离方式:
- 程序修改mysql操作,直接和数据库通信,简单快捷的读写分离和随机的方式实现的负载均衡,权限独立分配,需要开发人员协助。
- amoeba,直接实现读写分离和负载均衡,不用修改代码,有很灵活的数据解决方案,自己分配账户,和后端数据库权限管理独立,权限处理不够灵活。
- mysql-proxy,直接实现读写分离和负载均衡,不用修改代码,master和slave用一样的帐号,效率低。
- mycat中间件,需要开发介入。
- proxysql中间件,推荐使用。
ProxySQL是什么?
- ProxySQL 是一款可以实际用于生产环境的 MySQL 中间件,它有官方版和 percona 版两种。percona版是在官方版的基础上修改的,添加了几个比较实用的工具。生产环境建议用官方版。
- ProxySQL 是用 C++ 语言开发的,虽然也是一个轻量级产品,但性能很好(据测试,能处理千亿级的数据),功能也足够,能满足中间件所需的绝大多数功能,包括:
- 最基本的读/写分离,且方式有多种
- 可定制基于用户、基于schema、基于语句的规则对SQL语句进行路由。换句话说,规则很灵活。基于schema和与语句级的规则,可以实现简单的sharding(分库分表)。
- 可缓存查询结果。虽然ProxySQL的缓存策略比较简陋,但实现了基本的缓存功能,绝大多数时候也够用了。此外,作者已经打算实现更丰富的缓存策略。
- 监控后端节点。ProxySQL可以监控后端节点的多个指标,包括:ProxySQL和后端的心跳信息,后端节点的read-only/read-write,slave和master的数据同步延迟性(replication lag)
ProxySQL常用端口:
- ProxySQL 启动后,将监听两个端口。admin管理接口是一个使用 MySQL 协议的接口,默认用户名和密码均为 admin。
- admin管理接口,默认端口为6032。该端口用于查看、配置ProxySQL。
- 接收SQL语句的接口,默认端口为6033,这个接口类似于MySQL的3306端口。
二、安装部署
- 下载proxysql安装包有三个路径,分别为官网、Percona网站和github社区
- proxysql默认配置文件为/etc/proxysql.cnf,有两个端口。6032是管理proxysql服务本身的,6033是通过proxysql管理后端mysql服务的。
主机版本 | 主机名 | IP | 要安装的服务 |
---|---|---|---|
CentOS7 | proxysql | 192.168.161.129 | proxysql mysql客户端 |
CentOS7 | master | 192.168.161.130 | mysql主 |
CentOS7 | slave | 192.168.161.131 | mysql从 |
1.下载安装。
wget https://github.com/sysown/proxysql/releases/download/v2.5.4/proxysql-2.5.4-1-centos7.x86_64.rpm
yum -y localinstall proxysql-2.5.4-1-centos7.x86_64.rpm
2.启动服务。
systemctl enable --now proxysql
3.连接proxysql服务,通过mysql客户端连接,默认账号/密码:admin/admin。
yum -y install mariadb
mysql -uadmin -padmin -P6032 -h127.0.0.1
三、proxysql管理配置
- ProxySQL使用的是SQLite3数据库,通过SQLite3引 擎进行解析、操作的。与MySQL关系型数据库的语法稍有不同,但ProxySQL会对不兼容的语法自动进行调整,比如desc命令就玩不了,需要使用命令查看pragma table_info(“表名”);
- peoxysql中的数据都是以文件方式保存,文件里都是key/value形式保存数据,不能通过vim编辑器修改,只能通过特有的sql语句对其配置项修改。
- 每次修改数据后,需要load、save保存到磁盘,不然重启服务修改的数据会消失。
3.1 内置库
库名 | 作用 | 备注 |
---|---|---|
main | 最重要的库,是一个内存数据库系统。 用于存放后端db实例、用户认证、路由规则等信息。 |
修改main库中的配置后,必须将其持久化到disk上才能永久保存。 |
disk | 磁盘数据库,该数据库结构和内存数据库完全一致。 当持久化内存数据库中的配置时,其实就是写入到disk库中。 |
磁盘数据库的默认路径为 $DATADIR/proxysql.db |
stats | 统计信息库。包括到后端各命令的执行次数、流量、processlist、查询各类汇总、执行时间等。 | 这个库中的数据一般是在检索其内数据时临时填充的,它保存在内存中。<>br因为没有相关的配置项,所以无需持久化 |
monitor | 是监控后端MySQL节点相关的库,主要是对后端db的健康、延迟检查。 | 该库中只有几个log类的表, 监控模块收集到的监控信息全都存放到对应的log表中 |
stats_history | 是1.4.4版新增的库,用于存放历史统计数据。 | 默认路径为 $DATADIR/proxysql_stats.db |
3.1.1 main库表
表名 | 作用 |
---|---|
runtime_global_variables | global_variables的运行时版本 |
runtime_mysql_group_replication_hostgroups | mysql_group_replication_hostgroups的运行时版本 |
runtime_mysql_query_rules | mysql_query_rules的运行时版本 |
runtime_mysql_replication_hostgroups | mysql_replication_hostsgroups的运行时版本 |
runtime_mysql_servers | mysql_servers的运行时版本 |
runtime_mysql_users | mysql_users的运行时版本 |
runtime_scheduler | scheduler调度程序的运行时版本 |
mysql_replication_hostgroups | 定义hostgroup的主从关系。 |
mysql_server表字段 | 释义 |
---|---|
hostgroup_id | ProxySQL通过hostgroup的形式组织后端db实例,一个hostgroup代表同属于一个角色。 表的主键是(hostgroup_id, hostname, port),以hostname:port在多个hostgroup中存在。 一个hostgroup可以有多个实例,即是多个从库,可能通过weight分配权重。 hostgroup_id 0是一个特殊的hostgroup,路由查询的时候,没有匹配到规则则默认选择hostgroup 0。 |
status | ONLINE:当前后端实例状态正常。 SHUNNED:临时被剔除,可能因为后端too many connection error,或者超过了max_replication_lag。 OFFLINE_SOFT:软离线状态,不再接受新的连接,但已建立的连接会等待活跃事务完成。 OFFLINE_HARD:硬离线状态,不再接受新的连接,已建立的连接或被强制中断,当后端实例宕机或网络不可达,会出现。 |
max_connections | 允许连接到该后端实例的最大连接数,不能大于MySQL的max_connections。 如果后端实例hostname:port在多个hostgroup里,以较大者为准,而不是各自独立允许的最大连接数。 |
max_replication_lag | 允许的最大延迟,主库不受影响,默认为0。 如果>0,monitor模块监控主从延迟大于阈值时,会临时把它的状态变更为SHUNNED。 |
max_latency_ms | mysql_ping响应时间,大于这个阈值会把它从连接池剔除,即使是ONLINE。 |
comment | 备注,不建设为空。 |
mysql_users表字段 | 释义 |
---|---|
username、password | 连接到后端MySQL或ProxySQL实例的凭证,参考密码管理。 密码可插入明文,也可通过PASSWORD()插入密文,proxysql以*开头判断插入是否是密文。 但是runtime_mysql_users里统一是密文,所以明文插入,再SAVE MYSQL USERS TO MEM,此时看到的也是HASH密文。 |
active | 是否生效该用户,active=0的用户将在数据库中被跟踪,但不会加载到内存中的数据结构中。 |
default_hostgroup | 这个用户的请求没有匹配到规则时,默认发到hostgroup,默认0。 |
default_schema | 这个用户连接时没有指定schema时,默认使用的schema。 默认为NULL,实际上受变量mysql-default_schema的影响,默认为information_schema。 |
transaction_persistent | 如果设置为1,连接上ProxySQL的会话后,若在一个hostgroup上开启了事务,那么后续的sql都继续维持在这个hostgroup上,不论是否会匹配上其它路由规则,直到事务结束。 |
frontend | 如果设置为1,则用户名、密码对ProxySQL进行身份验证。 |
backend | 如果设置为1,则用户名、密码根据任何主机组向mysqld服务器进行身份验证。 |
mysql_query_rules查询规则表 | 释义 |
---|---|
rule_id | 表主键,自增,规则处理是以rule_id为顺序进行。 |
active | 只有active=1时的规则才会参与匹配。 |
username | 过滤匹配用户名的条件,如果是非空值,则仅当连接使用正确的用户名时,查询才匹配。 |
schemaname | 匹配schemaname的过滤条件,如果是非空值,则仅当连接schemaname用作默认模式时,查询才匹配。 |
flagIN,flagOUT,apply | 用来定义路由链chains of rules。 首先会检查flagIN=0的规则,以rule_id的顺序;如果没有匹配上,则走这个用户的default_hostgroup。 当匹配一条规则后,会检查flagOUT。 如果不为NULL,并且flagIN!=flagOUT,则进入以flagIN为上一个flagOUT值的新规则链。 如果不为NULL,并且flagIN=flagOUT,则应用这条规则。 如果为NULL,或者apply=1,则结束,应用这条规则。 如果最终没有匹配到,则找到这个用户的default_hostgroup。 |
client_addr | 匹配客户端来源IP。 |
proxy_addr,proxy_port | 匹配本地proxysql的ip、端口。 |
digest | 精确匹配的查询。 |
match_digest | 正则匹配查询。query,digest是指对查询去掉具体值后进行”模糊化“后的查询,类似pt-query-digest的效果。 |
match_pattern | 正则匹配查询。 |
negate_match_pattern | 反向匹配,相当于对match_digest/match_pattern的匹配取反。 |
re_modifiers | 修改正则匹配的参数,比如默认的:忽略大小写CASELESS、禁用GLOBAL。 |
replace_pattern | 查询重写,默认为空。 |
destination_hostgroup | 路由查询到这个hostgroup,当然如果用户显式start transaction且transaction_persistent=1, 那么即使匹配到了,也依然按照事务里第一条sql的路由规则去走的。 |
cache_ttl | 查询结果缓存的毫秒数。 |
timeout | 这一类查询执行的的最大时间(毫秒),超时则自动kill。后端DB的保护机制 |
retries | 语句在执行失败时,重试次数。默认由mysql-query_retries_on_failure变量指定,为1。建议不要重试,有风险。 |
delay | 查询延迟执行,这是ProxySQL提供的限流机制,会让其它的查询优先执行。 默认值mysql-default_query_delay为0。 |
mirror_flagOUT,mirror_hostgroup | 与镜像相关的设置。 |
error_msg | 默认为NULL,如果指定了则这个查询直接被block掉,将error_msg返回给客户端。 |
multiplex | 连接是否利用,请参考文章。 |
log | 是否记录查询日志,可以看到log是否记录的对象是根据规则。 |
scheduler调度表 | 释义 |
---|---|
id | 调度程序作业的唯一标识符。 |
active | 如果设置为1,则作业处于活动状态。 |
interval_ms | 工作的开始频率(以毫秒为单位),最小interval_ms为100毫秒 |
filename | 可执行文件的完整路径。 |
arg1-arg5 | 传递作业的参数。最多5个。 |
comment | 注释。 |
3.1.2 stats库表
stats_mysql_commands_counters表 | 释义 |
---|---|
command | 已执行的SQL命令的类型,如FLUSH、INSERT、KILL、SELECT FOR UPDATE等。 |
Total_Time_us | 执行该类型命令的总时间(以毫秒为单位)。 |
Total_cnt | 执行该类型的命令的总数。 |
cnt_100us-cnt_INFs | 在指定的时间限制内执行的给定类型的命令总数和前一个命令的总数。 |
stats_mysql_connection_pool表 | 释义 |
---|---|
hostgroup | 后端服务器所属的主机组,单个后端服务器可以属于多个主机组。 |
srv_host,srv_port | mysqld后端服务器正在侦听连接的TCP端点的IP和Port。 |
status | 后端服务器的状态。可以有ONLINE,SHUNNED,OFFLINE_SOFT,OFFLINE_HARD。 |
ConnUsed | ProxySQL当前使用多少个连接来向后端服务器发送查询。 |
ConnFree | 目前有多少个连接是空闲。 |
ConnOK | 成功建立了多少个连接。 |
ConnERR | 没有成功建立多少个连接。 |
Queries | 路由到此特定后端服务器的查询数。 |
Bytes_data_sent | 发送到后端的数据量。 |
Bytes_data_recv | 从后端接收的数据量。 |
Latency_ms | 从Monitor报告的当前ping以毫秒为单位的延迟时间。 |
stats_mysql_global表 | 释义 |
---|---|
Variable_Name | 代表与MySQL相关的代理级别的全局统计。 如Client_Connections_aborted:由于无效凭据或max_connections而导致的前端连接数已达到。 如Client_Connections_connected:当前连接的前端连接数。 如Client_Connections_created:到目前为止创建的前端连接数。等等。 |
Variable_Value | 统计所对应的值。 |
stats_mysql_processlist表 | 释义 |
---|---|
ThreadID | ProxySQL线程的内部ID。 |
SessionID | ProxySQL会话ID,通过这个ID可以进行kill操作。 |
user | 与MySQL客户端连接到ProxySQL的用户。 |
db | 当前选择的数据库。 |
cli_host,cli_port | 连接ProxySQL的IP和TCP端口。 |
hostgroup | 当前主机组。如果正在处理查询,则是查询已被路由或将要路由的主机组,或默认主机组。 可以通过这个查看该SQL到底是到哪个HG里。 |
l_srv_host,l_srv_port | ProxySQL的IP和TCP端口。 |
srv_host,srv_port | 后端MySQL服务器的IP和端口。 |
command | 正在执行的MySQL查询的类型。 |
time_ms | 命令执行的时间(以毫秒为单位)。 |
info | 正在执行的SQL。 |
stats_mysql_query_digest表 | 释义 |
---|---|
hostgroup | 发送查询的主机组。值-1表示查询查询缓存。 |
schemaname | 查询的数据库。 |
user | 连接ProxySQL的用户名。 |
digest | 一个十六进制散列,表示其参数剥离的SQL。 |
digest_text | 参数剥离的实际SQL文本 |
count_star | 执行查询的总次数(参数的值不同)。 |
first_seen | unix时间戳,是通过代理路由查询的第一时刻。 |
last_seen | unix时间戳,当查询通过代理路由时的最后一刻(到目前为止)。 |
sum_time | 执行此类查询的总时间(以微秒为单位)。 |
min_time,max_time - | 执行此类查询时期望的持续时间范围。 min_time是到目前为止所看到的最小执行时间。 max_time表示最大执行时间,以微秒为单位。 |
stats_mysql_query_rules表 | 释义 |
---|---|
rule_id | 路由规则的ID与main.mysql_query_rules的id对应。 |
hits | 此路由规则的匹配总数。 如果当前传入的查询符合规则,则会记录一次命中。 |
3.1.3 monitor库
表名 | 作用 |
---|---|
mysql_server_connect/mysql_server_connect_log | 连接到所有MySQL服务器以检查它们是否可用,该表用来存放检测连接的日志。 由变量mysql-monitor_connect_interval来控制其检测的时间间隔, 由参数mysql-monitor_connect_timeout控制连接是否超时(默认200毫秒)。 |
mysql_server_ping/mysql_server_ping_log | 使用mysql_ping API ping后端MySQL服务器检查它们是否可用,该表用来存放ping的日志。 由变量mysql-monitor_ping_interval控制ping的时间间隔,默认值:10000(毫秒,相当于10秒)。 |
mysql_server_replication_lag_log | 后端MySQL服务主从延迟的检测。 由参数mysql-monitor_replication_lag_interval控制检测间隔时间, 如果复制滞后太大,可以暂时关闭从。 由mysql_servers.max_replication_lag列控制。默认值:10000(毫秒,相当于10秒)。 |
3.2 常用管理变量
区分admin管理接口的用户名和mysql_users中的用户名:
- admin管理接口的用户是连接到管理接口(默认端口6032)上用来管理、配置ProxySQL的。
- mysql_users表中的用户名是应用程序连接ProxySQL(默认端口6033),以及ProxySQL连接后端MySQL Servers使用的用户。它的作用是发送、路由SQL语句,类似于MySQL Server的3306端口。所以,这个表中的用户必须已经在后端MySQL Server上存在且授权了。
- admin管理接口的用户必须不能存在于mysql_users中,这是出于安全的考虑,防止通过admin管理接口用户猜出mysql_users中的用户。
注意事项:
- 命令中的有些单词可以简写。比如disk/memory/runtime/config可以缩写,只要能识别即可。
- 例如memory可以缩写为mem,runtime可以缩写为run。
3.2.1 添加管理用户
基本了解:
- admin-admin_credentials变量控制的是admin管理接口的管理员账户。
- 默认的管理员账户和密码为admin/admin,但是这个默认的用户只能在本地使用。
- 如果想要远程连接到ProxySQL必须自定义一个管理员账户。
1.连接本地proxysql,查看当前用户密码。
mysql -uadmin -padmin -P6032 -h127.0.0.1
select @@admin-admin_credentials;
2.添加新管理员帐户qingjun/citms。
set admin-admin_credentials='admin:admin;qingjun:citms';
//刷新,保存到磁盘。
load admin variables to runtime;
save admin variables to disk;
3.其他服务器连接prosysql。
mysql -uqingjun -pcitms -P6032 -h192.168.161.129
4.查看proxysql用户表。
//根据表的字段来查看,与mysql语法相同。
select * from global_variables where variable_name='admin-admin_credentials';
//根据proxysql语法查看。
select @@admin-admin_credentials;
3.2.2 添加普通用户
基本了解:
- admin-stats_credentials 变量控制admin管理接口的普通用户,这个变量中的用户没有超级管理员权限,只能查看monitor库和main库中关于统计的数据,其它库都是不可见的,且没有任何写权限。
- 默认的普通用户名和密码均为 stats ,与admin一样,默认只能用于本地登录,若想远程查看则要添加查看的专有用户。
1.只能查看到monitor、main库的内容。
mysql -ustats -pstats -P6032 -h127.0.0.1
2.设置远程连接用户。
set admin-stats_credentials='stats:stats;baimu:123456';
load admin variables to runtime;
save admin variables to disk;
3.使用远程用户在其他机器上登录proxysql。
mysql -ubaimu -p123456 -h192.168.161.129 -P6032
3.2.3 修改监听套接字
基本了解:
- admin-mysql_ifaces 变量指定admin接口的监听地址,格式为冒号分隔的hostname:port列表。默认监听在 0.0.0.0:6032
1.默认的监听套接字是0.0.0.0:6032。
select @@admin-mysql_ifaces;
2.修改监听端口。
//修改监听端口为8888,之后连接proxysql就是用8888连接。
set admin-mysql_ifaces='0.0.0.0:8888';
load admin variables to runtime;
save admin variables to disk;
3.去除-h参数登录。
//创建默认套接字存放目录。
mkdir /var/lib/mysql/
chown -R proxysql.proxysql /var/lib/mysql/
//使用管理员用户修改。
mysql -uadmin -padmin -P8888 -h127.0.0.1
set admin-mysql_ifaces='0.0.0.0:8888;/var/lib/mysql/mysql.sock';
load admin variables to runtime;
save admin variables to disk;
四、多层配置系统
多层匹配系统的好处:
- 可以在线修改几乎所有配置(仅有的两个需要重启才能生效的变量为 mysql-threads 和 mysql-stacksize ),并在线生效、持久化保存。
4.1 系统结构
- 最底层是disk库和主配置文件/etc/proxysql.cnf 。ProxySQL第一次启动时,读取的是主配置文件,此时会生成proxysql.db文件。此后就是从disk 库中读取配置,并加载到内存,最终加载到runtime 生效。
- 中间层的是 memory ,表示内存数据库,就是指main 库中不以runtime开头的表。通过管理接口修改的所有配置,都保存在内存数据库(main)中。当 ProxySQL 重启或者崩溃时,这个内存数据库中的数据会丢失,所以需要 save 到 disk 库中。
- 最上层的是 runtime ,指main 库中以runtime开头的表,是已生效的配置。所以,修改了 main 库中的配置后,必须 load 到 runtime 数据结构中才能使其生效。
+-------------------------+
| RUNTIME |
+-------------------------+
/|\ |
| |
[1] | [2] |
| \|/
+-------------------------+
| MEMORY |
+-------------------------+ _
/|\ | |\
| | \
[3] | [4] | \ [5]
| \|/ \
+-------------------------+ +---------------+
| DISK | | CONFIG FILE |
+-------------------------+ +---------------+
序号[1] :将内存数据库中的配置加载到RUNTIME数据结构中
LOAD XXX FROM MEMORY
LOAD XXX TO RUNTIME (常用)
序号[2] :将RUNTIME数据结构中的配置持久化到内存数据库中
SAVE XXX FROM RUNTIME
SAVE XXX TO MEMORY
序号[3] :将磁盘数据库中的配置加载到内存数据库中
LOAD XXX FROM DISK
LOAD XXX TO MEMORY
序号[4] :将内存数据库中的配置持久化到磁盘数据库中
SAVE XXX FROM MEMORY
SAVE XXX TO DISK (常用)
序号[5] :从传统配置文件中读取配置加载到内存数据库中
LOAD XXX FROM CONFIG
XXX表示要加载、保存的是哪类配置,目前支持一下几类:
1.mysql users
2.mysql servers
3.mysql variables
4.mysql query rules
5.admin variables
6.scheduler
7.proxysql_servers:目前ProxySQL集群功能还处于实验阶段,所以该类配置不应该去使用
4.2 修改变量加载配置
MySQL [(none)]> show tables from disk;
+------------------------------------+
| tables |
+------------------------------------+
| global_variables | # (1)
| mysql_collations | # (N)
| mysql_group_replication_hostgroups | # (2)
| mysql_query_rules | # (3)
| mysql_query_rules_fast_routing | # (4)
| mysql_replication_hostgroups | # (5)
| mysql_servers | # (6)
| mysql_users | # (7)
| proxysql_servers | # (8)
| scheduler | # (9)
+------------------------------------+
(1)中包含两类变量,以amdin-开头的使用admin variables修改;以mysql-开头的使用mysql variables修改。
(2,5,6)使用mysql servers修改。
(3,4)使用mysql query rules修改。
(7)使用mysql users修改。
(9)使用scheduler修改。
(N)只是一张表,保存的是ProxySQL支持的字符集和排序规则,它是不用修改的
(8)是ProxySQL的集群配置表,该功能目前还处于实验阶段。如果想要配置该功能,则load/save proxysql_servers to/from ...
4.3 启动加载流程
- 若ProxySQL 是刚安装的,或者磁盘数据库文件为空(甚至不存在),或者启动 ProxySQL 时使用了选项 --initial,这几种情况启动 ProxySQL 时,都会从传统配置文件 config file 中读取配置加载到内存数据库,并自动 load 到 runtime 数据结构、save到磁盘数据库,这是初始化 ProxySQL 运行环境的过程。
- 若不是第一次启动ProxySQL ,由于已经存在磁盘数据库文件,这时 ProxySQL 会从磁盘数据库中读取几乎所有的配置(即使传统配置文件中配置了某项,也不会去解析)。
- 有3项是必须从传统配置文件中读取,分别是:
- datadir:ProxySQL启动时,必须从配置文件中确定它的数据目录,因为磁盘数据库文件、日志以及其它一些文件是存放在数据目录下的。如果使用/etc/init.d/proxysql管理ProxySQL,则除了修改/etc/proxysql.cnf的datadir,还需要修改该脚本中的datadir。
- restart_on_missing_heartbeats:MySQL线程丢失多少次心跳,就会杀掉这个线程并重启它。默认值为10。
- execute_on_exit_failure:如果设置了该变量,ProxySQL父进程将在每次ProxySQL崩溃的时候执行已经定义好的脚本。建议使用它来生成一些崩溃时的警告和日志。注意,ProxySQL的重启速度可能只有几毫秒,因此很多其它的监控工具可能无法探测到ProxySQL的一次普通故障,此时可使用该变量
总结: proxysql启动时,会从配置文件/etc/proxysql.cnf加载配置项,当服务启动后会生成/var/lib/proxysql/proxysql.db文件,此后读取的大部分配置都是从/var/lib/proxysql/proxysql.db中读取。
1.proxysql服务已启动,已生成在proxysql.db文件,连接mysql服务器的端口是默认的6033。
2.将proxysql.db删除或移走,修改主配置文件,重启服务,6033变成3306
mv /var/lib/proxysql/proxysql.db /opt/
vim /etc/proxysql.cnf
......
interfaces="0.0.0.0:3306" //将6033修改成3306
systemctl restart proxysql
3.此时不移走proxysql.db文件,在修改主配置文件该参数为3307,重启服务,发现依然是3306。是因为此时读取的是proxysql.db文件里的参数配置。文章来源:https://www.toymoban.com/news/detail-606609.html
vim /etc/proxysql.cnf
......
interfaces="0.0.0.0:3307" //将3306改成3307
systemctl restart proxysql
文章来源地址https://www.toymoban.com/news/detail-606609.html
到了这里,关于mysql进阶1——proxysql中间件的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!