位置: IT常识 - 正文

MySQL分区表详解(mysql分区分表原理)

编辑:rootadmin
MySQL分区表详解

推荐整理分享MySQL分区表详解(mysql分区分表原理),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:mysql分区分表原理,MySQL分区表详解,mysql的分区表,mysql5.7分区表,mysql5.7分区表,MySQL分区表详解,mysql的分区表,mysql的分区,内容如对您有帮助,希望把文章链接给更多的朋友!

通常情况下,同一张表的数据在物理层面都是存放在一起的。随着业务增长,当同一张表的数据量过大时,会带来管理上的不便。而分区特性可以将一张表从物理层面根据一定的规则将数据划分为多个分区,多个分区可以单独管理,甚至存放在不同的磁盘/文件系统上,提升效率。

分区表的优点:

数据可以跨磁盘/文件系统存储,适合存储大量数据。数据的管理非常方便,以分区为单位操作数据,不会影响其他分区的正常运行。数据查询上在某些条件可以利用分区裁剪(partition pruning)特性,将搜索范围快速定位到特性分区,提升查询性能。

对于应用来说,表依然是一个逻辑整体,但数据库可以针对不同的数据分区独立执行管理操作,不影响其他分区的运行。而数据划分的规则即称为分区函数,数据写入表时,会根据运算结果决定写入哪个分区。

MySQL的分区插件与存储引擎运行在不同的层,因此大部分存储引擎都可以利用MySQL的分区特性,只有少数存储引擎(merge,CSV,federated)不支持分区特性。若某张表使用的分区特性,则所有的分区都需要使用相同的存储引擎,且分区特性会同时应用到数据和索引上。

MySQL的分区类型:

一、分区的类型

1. Range partition(范围分区)

2. List partition(列表分区)

3. Hash partition(哈希分区)

4. Key partition(键值分区)

二、subparitioning(子分区)

三、分区的基本维护

一、分区的类型1. Range partition(范围分区)

Range partition是按照分区表达式的运算结果,判断结果落在某个范围内,从而将数据存储在对应的分区。各个分区定义之间需要连续且不能重叠,范围分区通过partition by range子句定义,而分区的范围通过values less than子句划分。

例:定义一个员工表,根据员工ID分区,110号员工一个分区,1120号员工一个分区,依次类推,共建立4个分区:

create table employees ( id int not null primary key, first_name varchar(30), last_name varchar(30)) partition by range(id)( partition p0 values less than (11), partition p1 values less than (21), partition p2 values less than (31), partition p3 values less than (41) );

现在随便插入几条数据:

insert into employees values(1,'Vincent','Chen');insert into employees values(6,'Victor','Chen');insert into employees values(11,'Grace','Li');insert into employees values(16,'San','Zhang');commit;

分区查询:

如果在查询时候明确的知道数据所在的分区,我们可以直接指定分区:

select * from employees partition(p0); -- 查询p0分区select * from employees partition(p0,p1); -- 查询p0和p1分区

分区删除:

如果某分区数据不再需要的时候,我们可以用alter table … drop partition来删除指定分区,例如删除分区p1,采用drop partition的方式可以快速清除历史数据:

alter table employees drop partition p1;

分区p1被删除后,所有p1分区的数据都已丢失,此时原p1分区的范围将由p2覆盖。

分区新增:

对于range分区来说,分区新增只能在最大范围之上增加分区,因此p1分区被删除后就无法通过新增分区的方式加回了,下例试图对ID10~20的员工新增一个分区,系统会返还错误。

alter table employees add partition (partition n1 values less than(21));

而在最大的分区范围之上是可以的:

alter table employees add partition (partition p4 values less than(51));

分区重组织:

如果一定要在分区之间插入新的分区,则可以采用重组织的方式,将已有分区的数据重新划分,达到创建新分区的效果:

例如我要将p2划分为2个分区,分别是1120,2130:

alter table employees reorganize partition p2 into (partition p1 values less than(21),partition p2 values less than(31));

此时原p2分区被拆分为了p1,p2,数据也在2个分区间重新分布,保证不会丢失。效果就像我们在中间插入了一个分区一样。

目前定义的分区都是有上限的,如果有大于分区上限的值想插入表中,系统会返还错误,为了兼容这种情况,我们可以新增一个分区,上限为maxvalue。所有大于当前上限的值都会放入这个分区:

alter table employees add partition(partition pmax values less than(maxvalue));

范围分区的条件除了直接用值,还可以用函数来定义。一个常用的场景就是按时间分区,例如:在create table中使用partition by range(year(hire_date)),可以按照年份来进行分区。这种分区方式在需要定期清理过期数据的场景会非常方便。

Range columns分区:

Range分区还有一个变种,叫做range columns分区。此分区方式允许使用多个column来作为分区范围条件。但是此分区方式不能接受函数,只能直接用列的名称。但是对分区列的类型不再限制为整数,可以使用string,date等类型。

使用range columns对多个列进行分区:

CREATE TABLE rc2 ( a INT, b INT)PARTITION BY RANGE COLUMNS(a,b) ( PARTITION p0 VALUES LESS THAN (0,10), PARTITION p1 VALUES LESS THAN (10,20), PARTITION p2 VALUES LESS THAN (10,30), PARTITION p3 VALUES LESS THAN (MAXVALUE,MAXVALUE) );

使用range columns直接对date类型进行分区:

CREATE TABLE members ( firstname VARCHAR(25) NOT NULL, lastname VARCHAR(25) NOT NULL, username VARCHAR(16) NOT NULL, joined DATE NOT NULL)PARTITION BY RANGE COLUMNS(joined) ( PARTITION p0 VALUES LESS THAN ('1960-01-01'), PARTITION p1 VALUES LESS THAN ('1970-01-01'), PARTITION p2 VALUES LESS THAN ('1980-01-01'), PARTITION p3 VALUES LESS THAN ('1990-01-01'), PARTITION p4 VALUES LESS THAN MAXVALUE);

Range partition和null:

对range分区来说,如果插入数据分区键为null,是可以成功的,数据会被放到第一个分区中。

2. List partition(列表分区)

列表分区和范围分区类似,主要区别是list partition的分区范围是预先定义好的一系列值,而不是连续的范围。列表分区采用partition by list和values in子句定义。

示例,创建一张员工表按照ID进行列表分区:

CREATE TABLE employees ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30))PARTITION BY LIST(id) ( PARTITION p0 VALUES IN (1,3,5,7,9), PARTITION p1 VALUES IN (2,4,6,8,10));

和range分区一样,可以使用alter table … add/drop partition新增/删除分区:

ALTER TABLE employees ADD PARTITION(PARTITION p2 VALUES IN (11,12,13,14,15));ALTER TABLE employees DROP PARTITION p0;

List partition和非事务引擎:

如果插入的值在list分区范围中不存在的话,语句会返还错误。如果表使用的是事务型引擎,如innodb。则这个事务会完全回滚。如果使用的是非事务引擎,若MyISAM,虽然也会报错,但是已插入的行无法回滚。

下面新建两张表,一张使用innodb,一张使用myisam:

CREATE TABLE employees_innodb ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30)) ENGINE=InnoDB -- 事务型引擎PARTITION BY LIST(id) ( PARTITION p0 VALUES IN (1,3,5,7,9), PARTITION p1 VALUES IN (2,4,6,8,10));CREATE TABLE employees_myisam ( id INT NOT NULL, fname VARCHAR(30), lname VARCHAR(30)) ENGINE=MyISAM -- 非事务型引擎PARTITION BY LIST(id) ( PARTITION p0 VALUES IN (1,3,5,7,9), PARTITION p1 VALUES IN (2,4,6,8,10));insert into employees_innodb values(1,'Vincent','Chen'),(11,'Grace','Li'),(2,'Victor','Chen');insert into employees_myisam values(1,'Vincent','Chen'),(11,'Grace','Li'),(2,'Victor','Chen');select * from employees_innodb;select * from employees_myisam;

MySQL分区表详解(mysql分区分表原理)

我们在中间放了一个不存在对应分区的记录来触发错误,可以看到innodb引擎数据没有数据插入(回滚)。但MyISAM引擎报错前的数据已经保留了下来,但是报错之后的记录未执行插入。

如果要忽略此错误,可以在insert语句中使用ignore关键字来忽略此错误,只插入符合分区条件的值:

insert ignore into employees_innodb values(1,'Vincent','Chen'),(11,'Grace','Li'),(2,'Victor','Chen');insert ignore into employees_myisam values(1,'Vincent','Chen'),(11,'Grace','Li'),(2,'Victor','Chen');

可以看到符合条件的记录被插入,不符合条件的自动被过滤,不返回错误。

List columns分区:

和range分区类似,list partition也有一个变种的list columns 分区,此分区类型可以使用多个列同时作为分区条件,且不再限制整数。并且可以使用string,date等数据类型作为分区条件。

list partition和null:

对于列表分区来说,必须有1个分区显示的指定可以包含null,否则插入会失败。

3. Hash partition(哈希分区)

Hash partition主要的应用场景是将数据平均的分布在指定数量的hash分区中。在range和list分区类型中,根据分区条件的计算结果,数据可以确定存储在哪个分区,而在hash分区中,数据存储在某个分区是由数据库自己决定的,你只需要指定分区的数量。

要创建hash分区,你需要使用create table的partition by hash(expr)子句,其中expr是整数类型的列或返还整数的表达式。另外还需要使用partions num来指定hash分区的数量(若忽略partitions子句则默认只创建1个hash分区)。

例:创建一个具有4个hash分区的表,按照ID进行分区:

CREATE TABLE employees ( id INT NOT NULL, first_name VARCHAR(30), last_name VARCHAR(30))PARTITION BY HASH(id)PARTITIONS 4;

Hash分区的性能考虑:

对于hash分区,数据库决定将数据存储在哪个分区是采用取余的方式进行运算的。存储分区的编号n = mod(expr, num),其中expr是分区值,num为定义的分区数量。

例如:对于4个hash分区,ID为5的记录,会存储在mod(5,4)=1,即1号分区中。

因此,最佳的分区键值的变化方式应该是线性变化,此时使用hash分区的效率最高,分布也会均匀。由于分区键值expr在每次insert/update/delete时都会运算,太复杂的表达式也会带来负面的性能影响,在选择时也需要考虑。

Liner hash partition(线性哈希分区):

以上的分区类型即普通hash分区,另外还有一类变种叫做liner hash partition(线性哈希分区),线性哈希分区的区别是其使用了更复杂的计算方法来确定数据的分布:

1. 对于分区数量为num的分区表,先计算V: V = POWER(2, CEILING(LOG(2, num)))2. 对于分区值与V-1进行位与运算 N = expr & (V - 1) -- 位与运算3. 对于N>num的情况,再次计算 N = N & ((v/2)-1)

例:假设分区数量为4

1. 先根据分区数量num计算V的值,先log再power,如果分区数量如果是2的次方,则此公式计算结果不变。

V = power(2,ceilling(log(2,4))) = power(2,ceillling(2)) = power(2,2) = 4

2. 对于ID为5的记录:

N = 5 & (V-1) = 5 & 3 = 1

3. 第二步计算出的结果为1,N<=num,不再需要第三步计算,数据存储在1号分区。

计算机的位计算效率是非常高的,因此Liner hash在新增/删除/合并/分裂分区场景(需要重新计算并分布数据)速度会快很多,非常适合特别大的数据存储场景(TB级别)。

Liner hash的缺点是数据的分布可能没有普通hash均匀。

管理hash分区数量:

Hash分区无法像range和list那样添加和删除分区,但是你可以用alter table的coalesce partition子句来减少hash分区的数量,用add partition partitions N来增加指定数量的分区。

例:将employees的hash分区数量由4调整为3:

ALTER TABLE employees COALESCE PARTITION 1; -- 移除一个分区

例:为employees表新增3个hash分区:

ALTER TABLE employees ADD PARTITION PARTITIONS 3; -- 新增3个hash分区

hahs partition和null:

对于hash partition和key partition,任何表达式对null运算,都会被当做返回为0.

4. Key partition(键值分区)

Key paritition与hash分区类似,主要区别在于key partition的hash函数是由MySQL server提供的,且使用主键(或非空唯一键)作为分区列:

例如:创建一个2个分区的key partition table:

CREATE TABLE k1 ( id INT NOT NULL PRIMARY KEY, name VARCHAR(20))PARTITION BY KEY() -- 未指定分区列,自动使用主键PARTITIONS 2;CREATE TABLE k1 ( id INT NOT NULL, -- 如果未定义not null,创建表会失败 name VARCHAR(20), UNIQUE KEY (id))PARTITION BY KEY() -- 未定义主键,自动使用unique keyPARTITIONS 2;

另外对于key partition,paritition key也不像其他分区类型那样限制为整数类型,例如,可以使用字符型作为分区键:

CREATE TABLE tm1 ( s1 CHAR(32) PRIMARY KEY -- 字符型主键,同时作为partition key)PARTITION BY KEY(s1)PARTITIONS 10;

对于key partition,由于primary key需要同时作为partition key,所以执行alter table … drop partition会报错(NDB引擎表会重组织并生成隐藏的primary key,不受此限制)。

mysql> alter table k1 drop primary key;ERROR 1488 (HY000): Field in list of fields for partition function not found in table-- 主键删除失败

Key partition和hash partition一样,也有liner key分区。

CREATE TABLE tk ( col1 INT NOT NULL, col2 CHAR(5), col3 DATE)PARTITION BY LINEAR KEY (col1) -- Liner key partitionPARTITIONS 3;

Key partition数量的管理方法与hash partition相同。

二、subparitioning(子分区)

subpartitioning可以在原有分区表的基础上,对每个分区再次进行分区(子分区)。子分区的分区类型可以和父分区不同,因此也叫复合分区。

下面的示例即对range partition的每个分区再次进行hash partition。父分区p0,p1,p2将各包含2个子分区,因此最终分区的数量为3*2=6个

CREATE TABLE ts (id INT, purchased DATE) PARTITION BY RANGE( YEAR(purchased) ) -- 父分区采用range partition SUBPARTITION BY HASH( TO_DAYS(purchased) ) -- 子分区采用hash partition SUBPARTITIONS 2 -- 子分区数量为2 ( PARTITION p0 VALUES LESS THAN (1990), PARTITION p1 VALUES LESS THAN (2000), PARTITION p2 VALUES LESS THAN MAXVALUE );

你也可以显示的定义每个子分区的名称,但如果采用这种方式定义,则必须显示指定所有子分区。且子分区的数量必须相同,子分区名称不能重复:

CREATE TABLE ts (id INT, purchased DATE) PARTITION BY RANGE( YEAR(purchased) ) --父分区为range partition SUBPARTITION BY HASH( TO_DAYS(purchased) ) --子分区为hash partition ( PARTITION p0 VALUES LESS THAN (1990) ( SUBPARTITION s0, -- 显式指定子分区名称和数量 SUBPARTITION s1 ), PARTITION p1 VALUES LESS THAN (2000) ( SUBPARTITION s2, SUBPARTITION s3 ), PARTITION p2 VALUES LESS THAN MAXVALUE ( SUBPARTITION s4, SUBPARTITION s5 ) );三、分区的基本维护

分区表的一大优势就是各个分区可以独立存储和管理。因此大部分在表级别上的管理操作都可以应该在分区上。

重建分区:

重建分区功能相当于清除分区中所有记录,然后在重新插入。对于频繁的更新的分区,可以定期使用重建的方式清除碎片。

ALTER TABLE employees REBUILD PARTITION p0, p1; -- 重建p0,p1分区ALTER TABLE employees REBUILD PARTITION ALL; -- 重建所有分区

检查分区:

你可以用check table语句来检查指定分区是否存在损坏:

ALTER TABLE employees CHECK PARTITION p1; -- 检查p1分区ALTER TABLE employees CHECK PARTITION all; -- 检查所有分区

键值分布统计:

使用alter table … analyze partition可以统计指定分区的键值分布,已便更好的生成执行计划:

ALTER TABLE employees ANALYZE PARTITION p0; -- 分析p0分区ALTER TABLE employees ANALYZE PARTITION ALL; -- 分析所有分区

分区修复:

使用alter table … repair partition来修复损坏的分区:

ALTER TABLE employees REPAIR PARTITION p0; -- 分析p0分区ALTER TABLE employees REPAIR PARTITION ALL; -- 修复所有分区

优化分区:

如果分区存在大量的数据更新,你可以使用optimize partition来回收空间,收集统计信息。其效果相当于在分区上执执行check partition、analyze partition 和 repair partition一样。

ALTER TABLE employees OPTIMIZE PARTITION p0;

但是对于innodb存储引擎来说,并不支持optimize partition操作,对单一分区执行会导致所有的分区都重建,谨慎使用(Table does not support optimize on partitions. All partitions will be rebuilt and analyzed.)。

先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦

本文链接地址:https://www.jiuchutong.com/zhishi/290867.html 转载请保留说明!

上一篇:阿尔卑斯山Cheran峡谷中流淌的河流,法国萨瓦 (© Jean-Philippe Delobelle/Minden)(阿尔卑斯山城市)

下一篇:Asian elephants in West Bengal, India (© Avijan Saha/Minden Pictures)

  • 雅迪乐骑plus怎么连接手机(雅迪乐骑plus怎么下架了)

    雅迪乐骑plus怎么连接手机(雅迪乐骑plus怎么下架了)

  • 联想小新pro16键盘灯怎么开(联想小新pro16键盘功能介绍)

    联想小新pro16键盘灯怎么开(联想小新pro16键盘功能介绍)

  • 华为mate30听筒是屏幕发声的吗

    华为mate30听筒是屏幕发声的吗

  • 怎么关闭支付宝刷脸(怎么关闭支付宝刷脸支付功能)

    怎么关闭支付宝刷脸(怎么关闭支付宝刷脸支付功能)

  • 抖音里的订单怎么找(抖音里的订单怎么删除)

    抖音里的订单怎么找(抖音里的订单怎么删除)

  • 支付宝二维码查手机号(支付宝二维码查手机号怎么查)

    支付宝二维码查手机号(支付宝二维码查手机号怎么查)

  • 手机壳黄了怎么能变白(手机壳黄了怎么清洗变干净透明的)

    手机壳黄了怎么能变白(手机壳黄了怎么清洗变干净透明的)

  • return是哪个按键(return键盘上哪个键)

    return是哪个按键(return键盘上哪个键)

  • 插了OTG但设备没显示(插了OTG但设备没显示vivo)

    插了OTG但设备没显示(插了OTG但设备没显示vivo)

  • c盘没有权限保存文件(c盘没有权限保护怎么办)

    c盘没有权限保存文件(c盘没有权限保护怎么办)

  • 10658000是什么短信(10658000是什么短信怎么取消)

    10658000是什么短信(10658000是什么短信怎么取消)

  • oppoa9手机怎么关机啊(oppoa9手机怎么关闭开发者模式)

    oppoa9手机怎么关机啊(oppoa9手机怎么关闭开发者模式)

  • itunes与appstore关打不开怎么办(appstore与itunes关闭)

    itunes与appstore关打不开怎么办(appstore与itunes关闭)

  • 怎么发起群聊视频(怎么发起群聊视频会议)

    怎么发起群聊视频(怎么发起群聊视频会议)

  • 怎么唤醒华为手机语音助手(怎么唤醒华为手机助手)

    怎么唤醒华为手机语音助手(怎么唤醒华为手机助手)

  • 在计算机内多媒体数据最终是以什么形式存在的(在计算机内多媒体数据)

    在计算机内多媒体数据最终是以什么形式存在的(在计算机内多媒体数据)

  • 视频高清线的视频是哪些线(视频高清线的视频怎么弄)

    视频高清线的视频是哪些线(视频高清线的视频怎么弄)

  • bn45电池小米什么型号(小米bn54电池是什么型号手机)

    bn45电池小米什么型号(小米bn54电池是什么型号手机)

  • 平板可以插内存卡吗(平板可以插内存卡下载歌曲吗)

    平板可以插内存卡吗(平板可以插内存卡下载歌曲吗)

  • 快手戳一戳什么意思(快手戳一戳功能什么时候有的)

    快手戳一戳什么意思(快手戳一戳功能什么时候有的)

  • 数据挖掘用什么软件(数据挖掘用什么语言)

    数据挖掘用什么软件(数据挖掘用什么语言)

  • 如何裁剪视频上的部分内容(如何裁剪视频上的图片)

    如何裁剪视频上的部分内容(如何裁剪视频上的图片)

  • 华为bklal00什么型号(华为bzkl00是什么型号)

    华为bklal00什么型号(华为bzkl00是什么型号)

  • 绿钻怎么关闭话费自动续费(怎么关闭绿钻的自动续费)

    绿钻怎么关闭话费自动续费(怎么关闭绿钻的自动续费)

  • 笔记本权限设置(笔记本设置权限管理在哪)

    笔记本权限设置(笔记本设置权限管理在哪)

  • Vue使用axios进行get请求拼接参数的几种方式(axios在vue中的使用慕课笔记)

    Vue使用axios进行get请求拼接参数的几种方式(axios在vue中的使用慕课笔记)

  • 行政事业单位卖废品怎么处理
  • 企业所得税零申报表怎么填写
  • 商品进销差价属于流动资产吗
  • 研发费用扣除比例是百分75吗
  • 个税专项附加扣除
  • 计算土地增值税时增值税可以扣除吗
  • 在建工程试运行收入和成本怎么处理
  • 报销餐费有增值税吗
  • 股东退股可以支付现金吗
  • 个人去税务局开居间费发票
  • 个人劳务报酬所得汇算清缴
  • 分公司如何缴纳公积金
  • 地税三方协议是什么意思
  • 预缴的企业所得税可以税前扣除吗
  • 小规模附加税怎么计提分录
  • 哪些项目可以免征个人所得税
  • &quot;明股实债“的税务风险,你真的清楚吗?
  • 汽车加油专票可以抵扣税款吗
  • 什么情况下企业不能辞退员工
  • 发票勾选了是否就抵扣了
  • 或有事项确认资金怎么填
  • win7系统怎么进
  • 用盈余公积弥补亏损会影响所有者权益总额吗
  • 关闭通知横幅
  • 电器维修公司买什么保险
  • 端游上古世纪
  • php对象赋值给变量
  • 结转销售原材料会计分录
  • 前端几种下载文件
  • 盈余公积转增资本对会计要素的影响
  • 集群怎么写
  • 流动资产损失
  • php curl代理
  • yolo v5 可视网络结构
  • read命令 linux
  • 利润分配的顺序依次是什么
  • php实现图片上传
  • 跨年的增值税普通发票怎么冲红
  • python中的装饰器的使用实战
  • 选择mysql数据库为当前数据库
  • 资产负债表和利润表的认定
  • 商品流通企业流程图
  • 小规模纳税人都是3%吗
  • 餐饮娱乐服务员
  • 出口货物预收账款分录
  • 长期应付账款是什么
  • 装订凭证需要注意事项
  • 非居民企业股权转让特殊性税务处理
  • 汇票和本票的区别是
  • 小规模纳税人退税政策
  • 车票如何抵扣增值税
  • 车船税没发票只交可以吗
  • 金税盘的维护费每年怎么抵扣
  • 新设企业如何建账
  • 销售空调并提供安装如何征增值税
  • 应付账款核算的项目
  • 利用pt-heartbeat监控MySQL的复制延迟详解
  • 安装sql server需要注意什么
  • 如何win8.1升级win10正式版
  • ubuntu火狐浏览器怎么改成中文
  • ubuntu怎么编辑文件
  • mac如何设置启动系统
  • mac 显示器
  • win7系统任务栏跑到右侧怎么办
  • win7安装软件提示停止工作
  • administrator帐户已锁定
  • win8开机启动项
  • cocos2dx webview
  • android之Java+html+javascript混合开发
  • 编写批处理
  • socket.io与pm2(cluster)集群搭配的解决方案
  • node.js的理解
  • perl常用函数
  • 重庆市医疗保障局
  • 火灾损失进项税额怎么处理
  • 税务局取消办税人员
  • 网上如何申请
  • 税务局发票邮寄费用谁承担
  • 9月份公积金什么时候扣
  • 广州代理记账公司注册
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设