当前位置:   article > 正文

Mysql数据表的索引类型(亲测)_mysql建表指定索引类型

mysql建表指定索引类型

一、索引

1.1 索引的概念

  • 索引是一个排序的列表,在这个列表中存储着索引的值和包含这个值的数据所在行的物理地址(类似于C语言的链表通过指针指向数据记录的内存地址)。
  • 使用索引后可以不用扫描全表来定位某行的数据,而是先通过索引表找到该行数据对应的物理地址然后访问相应的数据,因此能加快数据库的查询速度。
  • 索引就好比是一本书的目录,可以根据目录中的页码快速找到所需的内容。
  • 索引是表中一列或者若干列值排序的方法。
  • 建立索引的目的是加快对表中记录的查找或排序。
     

1.2 索引的作用

  • 设置了合适的索引之后,数据库利用各种快速定位技术,能够大大加快查询速度,这是创建索引的最主要的原因。
  • 当表很大或查询涉及到多个表时,使用索引可以成千上万倍地提高查询速度。
  • 可以降低数据库的IO成本,并且索引还可以降低数据库的排序成本。
  • 通过创建唯一(键)性索引,可以保证数据表中每一行数据的唯一性。
  • 可以加快表与表之间的连接。
  • 在使用分组和排序时,可大大减少分组和排序的时间。
     

副作用:

  • 索引需要占用额外的磁盘空间。(索引文件用于保存数据记录的地址)
  • 在插入和修改数据时要花费更多的时间,因为索引也要随之变动。


1.3 创建索引的原则

  • 创建索引也是要有条件的,不是所有情况都适合去创建索引。过度的创建索引只会消耗系统的资源,虽然在有索引的情况下数据库会优先进行索引查询。
  • 表的主键、外键必须有索引。因为主键具有唯一性,外键关联的是子表的主键,查询时可以快速定位
  • 记录数超过300行的表应该有索引。如果没有索引,需要把表遍历一遍,会严重影响数据库的性能。
  • 经常与其他表进行连接的表,在连接字段上应该建立索引。
  • 唯一性太差的字段不适合建立索引。
  • 更新太频繁地字段不适合创建索引。
  • 经常出现在 where 子句中的字段,特别是大表的字段,应该建立索引。
  • 索引应该建在选择性高的字段上。
  • 索引应该建在小字段上,对于大的文本字段甚至超长字段,不要建索引。
     

MySQL数据库的优化哪些字段场景适合创建索引?

  1. 小字段
  2. 唯一性强的字段
  3. 更新不频繁,但查询率很高的字段
  4. 表记录超过300+行
  5. 主键、外键、唯一键


1.4 索引的分类

  1. 普通索引 :针对所有字段,没有特殊的需求/规则
  2. 唯一索引 : 针对唯一性的字段,仅允许出现一次空值
  3. 组合索引 :可以是单列上创建的索引,也可以是在多列上创建的索引。需要满足最左原则,因为select语句的 where条件是依次从左往右执行的,所以在使用select 语句查询时where条件使用的字段顺序必须和组合索引中的排序一致,否则索引将不会生效。(多列/多字段组合形式的索引)
  4. 全文索引 :适合在进行模糊查询的时候使用,可用于在一篇文章中检索文本信息(varchar char text)
  5. 主键索引 :针对唯一性字段、且不可为空,同时一张表只允许包含一个主键索引


1.5 索引的创建

前提:

1.5.1 普通索引

创建索引

CREATE INDEX 索引名 ON 表名 (字段名);

  1. create index name_index on suoying (name);
  2. select name from suoying;
  3. show create table suoying;

 ②修改索引

ALTER TABLE 表名 ADD INDEX 索引名(字段名);

alter table suoying add index id_index (id);

 ③在创建表的同时去指定索引

  1. create table ceshi (id int(3) not null,name varchar(20) not null,age varchar (5) not null,index id_index(id));
  2. show create table ceshi;


 1.5.2 唯一索引

创建唯一索引

  1. create unique index address_index on suoying (address);
  2. show create table suoying; 

修改表

ALTER TABLE 表名 ADD UNIQUE 索引名 (列名);

alter table member add unique age_index (age);

创建表时候创建

CREATE TABLE 表名  (字段1 数据类型,字段2 数据类型[,...],UNIQUE 索引名 (列名));

  1. create table ceshi1 (id int(2),age varchar(20),unique id_index (id));
  2. show create table ceshi1;

修改表索引

alter table 表名 change 旧列名 新列名 数据类型 [unique key];

 1.5.3 主键索引

创建表时指定

CREATE TABLE 表名 ([...],PRIMARY KEY (列名));

  1. create table ceshi2 (id int,name varchar(20),primary key (id));
  2. show create table ceshi2;

修改表去创建

ALTER TABLE 表名 ADD PRIMARY KEY(字段名);


1.5.4 组合索引

可以是单列上创建索引,也可以在多列上创建索引。但是搜索时要满足最左的原则。

创建索引

create table 表名 (列名1 类型,列名2 类型, 等等,index 索引名(列名1,列名2));

create table zuhe (id int not null,name varchar(40),age varchar(4),index index_zuhe(id,name));

例如上面创建的索引是(id,name)所以查询的时候要按照顺序

如果你不按照顺序去查询就不会发挥出索引的效果

  1. insert into zuhe values (1,'zhang','20');
  2. select * from zuhe where id='1' and name='zhang';

1.5.5 全文索引

适合在模糊查询时使用,可以用于在一篇文章中检索文本信息。

①创建直接索引

create fulltext index 索引名 on 表名 (列名);

show create table zuhe;

②修改表方式去创建

alter table 表名 add fulltext 索引名 (列名);

创建表的时候去指定索引

 create table 表名 (字段1 类型,字段2 类型,等等,fulltext 索引名(列名) );

用全文索引去查询

  1. select * from 表名 where 条件
  2. select * from zuhe where name='zhang';

1.6 查看索引

show index from 表名;

show index from 表名\G   以竖形得方式展现

show keys from 表名;

show keys from 表名\G。


1.7 删除索引

①直接删除索引

drop index 索引名 on 表名;

 ②删除主键索引

alter table 表名 drop index primary key;

二、MySQL得死锁、悲观锁、乐观锁


2.1 Mysql死锁、悲观锁、乐观锁            

                                                                  
锁机制是为了避免在数据库有并发事务的时候,可能会产生数据的不一致而诞生的的一个机制。


锁的分类:

  •  共享锁:又叫做读锁,当用户要进行数据的读取时,对数据加上共享锁,共享锁可以同时加上多个。
  •  排他锁:又叫做写锁,当用户要进行数据的写入时,对数据加上排他锁,排他锁只可以加一个,他和其他的排他锁,共享锁都相斥。


MySQL有三种锁的级别:页级、表级、行级。

  • 表级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。
  • 行级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
  • 页面锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度


2.1.1 死锁


MyISAM中是不会产生死锁的,因为MyISAM总是一次性获得所需的全部锁,要么全部满足,要么全部等待。

而在InnoDB中,锁是逐步获得的,就造成了死锁的可能。两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。


产生死锁的原因主要是

  1. 系统资源不足。
  2. 进程运行推进的顺序不合适。
  3. 资源分配不当等。


如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。其次,进程运行推进顺序与速度不同,也可能产生死锁。
 

产生死锁的四个必要条件:

  1. 互斥条件:一个资源每次只能被一个进程使用。
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
  3. 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。


解决方法:

  1. 撤消陷于死锁的全部进程;
  2. 逐个撤消陷于死锁的进程,直到死锁不存在;
  3. 从陷于死锁的进程中逐个强迫放弃所占用的资源,直至死锁消失;
  4. 从另外一些进程那里强行剥夺足够数量的资源分配给死锁进程,以解除死锁状态。


2.1.2 如何避免死锁

  1. 使用事务时,尽量缩短事务的逻辑处理过程,及早提交或回滚事务;
  2. 设置死锁超时参数为合理范围,如:3分钟-10分种;超过时间,自动放弃本次操作,避免进程悬挂;
  3. 优化程序,检查并避免死锁现象出现;
  4. 对所有的脚本和SP都要仔细测试,在正式版本之前;
  5. 所有的SP都要有错误处理(通过@error);
  6. 一般不要修改SQL SERVER事务的默认级别。不推荐强行加锁。
  7. 以固定的顺序访问表和行:

        分为两种情景:

                    对于不同事务访问不同的表,尽量做到访问表的顺序一致;

                    对于不同事务访问相同的表,尽量对记录的id做好排序,执行顺序一致;

  8. 大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
  9.  在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
  10.  降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
  11.  为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大


三、 MySQL的事务


3.1 事务的概念

MySQL事务主要用于处理操作量大的操作和复杂的数据。比如在数据库中需要删除某个人基本信息,又要删除有关本人的其他信息。这些sql语句就构成了一个事务。

        事务是一种机制、一个操作序列,包含了一组数据库操作命令,并且把所有的命令作为一个整体一起向系统提交或撤销操作请求,即这一组数据库命令要么都执行,要么都不执行。

        事务是一个不可分割的工作逻辑单元,在数据库系统上执行并发操作时,事务是最小的控制单元。

        事务适用于多用户同时操作的数据库系统的场景,如银行、保险公司及证券交易系统等等。

        事务通过事务的整体性以保证数据的一致性。

总结:

事务就是一个操作的顺序,这些顺序要么都执行,要么都不去执行。


3.2 事务ACID的特点


3.2.1 原子性

指事务是一个不可分割的单位,事务操作要么都执行,要么都不执行。

  •     事务是一个完整的操作,事务的各元素是不可分的。
  •     事务中的所有元素必须作为一个整体提交或回滚。
  •     如果事务中的任何元素失败,则整个事务将失败。

3.2.2 一致性

指在事务开始之前和结束后,整个数据库是完整的没有受到破坏。

  •     当事务完成时,数据必须处于一致状态。
  •     在事务开始前,数据库中存储的数据处于一致状态。
  •     在正在进行的事务中,数据可能处于不一致的状态。
  •     当事务成功完成时,数据必须再次回到已知的一致状态。

3.2.3 隔离性

指在并发情况下,不同事务同时去操作相同的数据时,每一个事务都有自己的完整数据空间。

  •     对数据进行修改的所有并发事务是彼此隔离的,表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
  •     修改数据的事务可在另一个使用相同数据的事务开始之前访问这些数据,或者在另一个使用相同数据的事务结束之后访问这些数据。

3.2.3 持久性

指在事务完成后,事务对数据库的更改会持久的保存在数据库中不会被回滚。

  •     指不管系统是否发生故障,事务处理的结果都是永久的。
  •     一旦事务被提交,事务的效果会被永久地保留在数据库中。

3.3 不同事物之间的影响


3.3.1 脏读

指读取了其他事物未提交的数据,说明读取的数据最终有可能不会存到数据库中。

例如:事务1在执行过程中修改了数据A,在未提交的情况下,事务2读取了数据A

此时事务1回滚了数据,事务2就变成了脏读


3.3.2 不可重复读

指一个事务去查询两次得到了两个不同结果。是因为查询时系统中其他事务在修改提交后导致的。

例如:事务1 第一次查询到一个数据X,事务2修改后提交。

事务1再去查询数据X发现内容发生了改变


3.3.3 幻读

指一个事务1在修改数据时在新的一行插入数据,而事务2也正好在修改此表的数据也是去插入一行新的数据。此时事务1就会发现表中还有没有修改的数据行

例如:事务1对数据表中做出了修改但未提交,此时事务2插入了一条和事务1更改前相同的记录行,并且在事务1之前提交。此时事务1 在查询时就会发现数据不一样。


3.3.4丢失更新

指两个事务在同时读取一个数据时,事务1先修改了数据,事务2也修改了数据,事务2提交后覆盖了事务1修改的结果。

例如:事务1 把数据10修改成20

事务2 也把数据10修改成30

但最终的结果时事务2 修改的30


3.4 MySQL事务隔离级别


3.4.1 MySQL事务的四种隔离

事务的隔离级别决定了事务之间可见的级别。MySQL事务隔离用于控制事务所做的修改。

  • read uncommitted(未提交读):读取尚未提交的数据,不解决脏读

        允许脏读,其他事务只要修改了数据,即使未提交,本事务也能看到修改后的数据值。也就是可能读到其他回话中未提交事务修改的数据。

  • read committed (提交读):读取已经提交的数据,可以解决脏读

        只能读取到已经提交的数据。Oracle 等多数数据库默认都是该级别(不重复读)。

  • repeatable read (可重复读):重复读取,可以解决脏读和不可重复读(mysql默认的)

        无论其他事务是否修改并提交了数据,在这个事务中看到的数据始终不受其他的事务影响

  • serializable:串行化,可以解决脏读、不可重复读、虚读

        完全串行化的读,每次读都需要获得表级共享锁,读写相互都会堵塞。

mysql默认的事务处理级别是 repeatable read ,而Oracle和SQL Server是 read committed 。

3.4.2 事务隔离级别的范围

事务隔离级别的作用范围分为:

①全局级:对所有的会话有效

②会话级:只对当前的会话有效

3.4.3 查询全局事务隔离级别

  1. show global variables like '%isolation%';
  2. select @@global.tx_isolation;

3.4.4 查询会话事务隔离级别

  1. show session variables like '%isolation%';
  2. SELECT @@session.tx_isolation;
  3. SELECT @@tx_isolation; 

3.4.5 设置全局事务隔离级别

  1. set global transaction isolation level serializable;
  2. set global transaction isolation level read committed;

3.4.6 设置会话级别事务隔离级别

set session transaction isolation level read committed;

 3.5 事务的控制语句

 MySQL的事务 默认是自动提交,当sql语句提交事务便自动提交。

语句:

BEGIN 或者 START TRANSACTION :显式的开启一个事务

COMMIT 或者COMMIT WORK:提交事务

ROLLBACK 或者 ROLLBACK WORK:回滚会结束用户事务,并撤销正在进行的所有未提交的修改

SAVEPOINT S1:使用 SAVEPOINT 允许在事务中创建一个回滚点,一个事务中可以有多个 SAVEPOINT;“S1”代表回滚点名称。

ROLLBACK TO [SAVEPOINT] S1:把事务回滚到标记点


3.5.1 begin和commit的使用

先创建一个表结构插入数据

  1.     begin
  2.     update shiwu set name=zw where id=2;
  3.     select * from shiwu;
  4.     commit

 


 3.5.2 事务的回滚

  1.     begin;
  2.     update shiwu set name=‘ww’ where id=2;
  3.     select * from shiwu;
  4.     rollvback;
  5.     select * from shiwu;

 
 

3.5.3 测试创建回滚点并进行回滚

  1.     begin
  2.     update shiwu set name='we' where id=2;
  3.     select *from shiwu;
  4.     savepoint s1;
  5.     update shiwu set name='ni' where id=2;
  6.     savepoint s2;
  7.     rollback to s1;
  8.     select * from shiwu;

 

 

3.5.4 使用set设置控制事务

 MySQL中当我们输入命令时,是系统在自动的begin和commit,这是因为MySQL数据库中设置了自动提交功能

    查看MySQL中的autocommit值

    show variables like 'autocommit';

  1.     // 禁止自动提交
  2.     set autocommit=0;
  3.     // 开启自动提交
  4.     set autocommit=1;

如果没有开启自动提交,当前会话连接的mysql的所有操作都会当成一个事务直到你输入rollback|commit;当前事务才算结束。当前事务结束前新的mysql连接时无法读取到任何当前会话的操作结果。

如果开起了自动提交,mysql会把每个sql语句当成一个事务,然后自动的commit。

当然无论开启与否,begin; commit;rollback; 都是独立的事务。

 
四、MySQL的存储引擎


4.1 什么是存储引擎

MySQL中的数据采用不同的技术去存储在文件中,不同的技术都会存在不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力。这些不同的技术以及配套的功能在MySQL中称为存储引擎。

存储引擎就是指MySQl将数据存储在文件中所用到的存储方式和存储的格式。

常用的存储引擎:MyISAM 和 InnoDB

MySQL系统中,存储引擎处于文件系统之上,在数据保存到数据文件之前会传输到存储引擎,之后按照各个存储引擎的存储格式进行存储。


4.2 MyISAM的介绍


4.2.1 概念

MyISAM不支持事务,也不支持外键约束,只支持全文索引,数据文件和索引文件是分开保存的所以访问速度快,对事务完整性没有要求。适合查询、插入为主,在数据进行更新的时候会锁定整个表

MyISAM在磁盘上存储成三个文件分别为:

.frm  文件存储表结构的定义

数据文件的是   .MYD

索引文件的是   .MYI

所以总结:

MyISAM是表级锁定,读和写是无法同时进行的

所以优点就是分开执行时,速度快,资源占用的较少


4.2.2 MyISAM支持的三种不同的存储格式

静态(固定长度表)

静态表是默认的存储格式。

静态表中的字段都是非可变字段,这样每个记录都是固定长度的,这种存储方法的优点是存储的速度快,容易缓存,出现故障时容易恢复

动态表

动态表包括可变字段,记录不是固定长度的,这样的存储优点是占用的空间较小,但是平凡的更新数据会导致数据出现碎片,并且出现故障的时候恢复相对困难

压缩表

压缩表由myisanmchk工具所创建,因为每一条的记录都被压缩所以占据的内存空间十分小。


4.2.3 myisam使用的场景

公司业务不需要事务支持的

单方面读取或写入数据比较多的业务,不适合读写都比较频繁的

数据修改较少的业务

对数据业务一致性要求不是非常高得业务

服务器硬件资源较差的


4.3 InnoDB介绍


4.3.1 概念

支持事务,支持4个事务隔离级别

MySQL从5.5.5版本开始,默认的存储引擎为InnoDB

读写阻塞与事务隔离级别相关

能非常高效的缓存索引和数据

表与主键以簇的方式存储

支持分区、 表空间,类似oracle数据库

支持外键约束,5.5前不支持全文索引,5.5后支持全文索引

对硬件资源要求还是比较高的场合

行级锁定,但是全表扫描仍然会是表级锁定,  如 update table set a=1 where user like '%zhang%';

InnoDB 中不保存表的行数,如 select count() from table; 时,InnoDB 需要扫描一遍整个表来计算有多少行,但是 MyISAM 只要简单的读出保存好的行数即可。需要注意的是,当 count()语句包含 where 条件时 MyISAM 也需要扫描整个表

当你去清空一个表的时候,InnoDB是一行一行的去删除所以效率慢


4.3.2 使用场景

业务需要事务的支持

行级锁定对高并发有很好的适应能力,但需确保查询是通过索引来完成

业务数据更新较为频繁的场景
如:论坛,微博等

业务数据一致性要求较高
如:银行业务

硬件设备内存较大,利用InnoDB较好的缓存能力来提高内存利用率,减少磁盘IO的压力


4.4 不同场景选择不同的存储引擎

支持的字段和数据类型

        所有引擎都支持通用的数据类型

        但不是所有的引擎都支持其它的字段类型,如二进制对象

锁定类型:不同的存储引擎支持不同级别的锁定

        表锁定: MyISAM 支持

        行锁定:InnoDB 支持

索引的支持

        建立索引在搜索和恢复数据库中的数据时能显著提高性能

        不同的存储引擎提供不同的制作索引的技术

        有些存储引擎根本不支持索引

事务处理的支持

        提高在向表中更新和插入信息期间的可靠性

        可根据企业业务是否要支持事务选择存储引擎


4.5 MySQL配置文件

MySQL 数据库的数据文件存放在/usr/local/mysql/data目录下,每个数据库对应一个子目录,用于存储数据表文件。每个数据表对应三个文件,扩展名分别为‘“.frm”,".MYD",".MYI"。

".MYD"文件:是MyISAM存储引擎专用,存放MyISAM表的数据。每一个MyISAM 表都会又一个“.MYD”文件与之对应,同样存放于所属数据库的文件夹下,和“.frm”文件在一起。

".MYI"文件:也是专属于 MyISAM 存储引擎的,主要存放MyISAM 表的索引相关的信息。对于MyISAM 存储来说,可以被  cache  的内容主要是来源于“.MYI ”文件中。每一个MyISAM 表对应一个“.MYI”文件,存放位置和“.frm”以及“.MYD”一样。

MyISAM 存储引擎的表在数据库中,每一个表都存放为三个表名命名的物理文件(frm,myd,myi)。每个表都有且仅有这样三个文件为MyISAM 存储类型的表的表存储,也就是说不管这个表又多少个索引,都是存放在同一个.MYI 文件中。

“.ibd”和“.ibdata ”这两个文件都是用来存放Innodb 数据的,之所以又着两种文件来存放 Innodb 数据(包括索引),是因为 Innodb 的数据存储方式能够通过配置来绝定是使用共享表空间存放存储数据,还是独享表空间存放存储数据。独享表空间存储方式使用 “.ibd ”文件来存放数据,且每个表又一个“.ibd” 文件,文件存放在和MyISAM 数据相同的位置。如果选用共享空间存放数据,则会使用 ibdata 文件来存放,所有表共同使用一个(或者多个,可自行配置)idbata 文件。


 
4.6 查看和设置存储引擎


4.6.1 查看系统的存储引擎

    show engines;

4.6.2 查看表使用的存储引擎

    ①show table status from 库名 where name='表名'\G;

    ②use 库名;

    show create table 表名\G

 
4.6.3 修改存储引擎

  1.     alter table engine=MyISAM; 
  2.      vim /etc/my.cnf
  3.     ......
  4.     [mysqld]
  5.     ......
  6.     default-storage-engine=INNODB
  7.      
  8.     systemctl restart mysqld

  1.      use 库名;
  2.      create table 表名(字段1 数据类型,...) engine=MyISAM;

4.7 innodb行锁和索引的关系

1) InnoDB行锁是通过给索引项加锁来实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁。

delete from t1(表名) where id=1;

2) 如果id字段是主键,innodb对 于主键使用了聚簇索引,会直接锁住整行记录。

delete from t1 where name='aaa' ;

3) 如果name字段是普通索引,会先锁住索引的两行,接着会锁住相应主键对应的记录。

delete from t1 where age=23;

如果age字段没有索引,会使用全部打描过滤,这时表上的各个记录都将加上锁。
 

总结

1.索引分类

① 普通索引 :针对所有字段,没有特殊的需求/规则

② 唯一索引 : 针对唯一性的字段,仅允许出现一次空值

③ 组合索引 :可以是单列上创建的索引,也可以是在多列上创建的索引。需要满足最左原则,因为select语句的 where条件是依次从左往右执行的,所以在使用select 语句查询时where条件使用的字段顺序必须和组合索引中的排序一致,否则索引将不会生效。(多列/多字段组合形式的索引)

④ 全文索引 :适合在进行模糊查询的时候使用,可用于在一篇文章中检索文本信息(varchar char text)

⑤ 主键索引 :针对唯一性字段、且不可为空,同时一张表只允许包含一个主键索引

2.MYSQL及事务隔离级别

①read uncommitted     未提交读

②read committed       提交读

③repeatable read      可重复读

④serialization            串行化

3.事务相互影响分为四种

①脏读

②不可重复复

③幻读

④丢失更新

4.MyISAM 和innoDB的区别
————————————————
版权声明:本文为CSDN博主「zzn0109」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zzn0109/article/details/125321747

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号