赞
踩
是按照数据结构来组织、存储和管理数据的仓库。
每个数据库都有一个或多个不同的API用于创建、访问、管理、搜索和复制所保存的数据。
关系数据库管理系统,由瑞典MySQL AB公司开发,目前属于Oracle公司。
是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。
特点:
use mysql; insert into user() values()。
USE 数据库名
:选择要操作的MySQL数据库,使用该命令后所有MySQL命令都只针对该数据库。SHOW DATABASES
:列出MySQL数据库管理系统的数据库列表。SHOW TABLES
:显示指定数据库的所有表,使用该命令前需要使用use命令来选择要操作的数据库。SHOW COLUMNS FROM 数据表名
:显示数据表的属性,属性类型,主键信息,是否NULL,默认值等其它信息。SHOW INDEX FROM 数据表名
: 显示数据表的详细索引信息,包括 - PRIMARY KEY(主键)。SHOW TABLE STATUS FROM 数据库名 LIKE ‘pattern’\G
:该命令输出MySQL数据库管理系统的性能及统计信息。CREATE DATABASE 数据库名
:创建数据库。(或者使用root权限创建:mysqladmin -u root -p create数据库名
)。DROP DATABASE 数据库名
:删除数据库。(或者使用root权限删除:mysqladmin -u root -p drop 数据库名
)。SHOW CREATE TABLE 数据库名
:获取创建数据表(CREATE TABLE)语句,该语句包含了原数据表的结构、索引。CREATE TABLE tb_name (column_name column_type);
例:
create tale tb_student(
s_id int PRIMARY KEY AUTO_INCREMENT,
s_name varchar(20) NOT NULL,
s_sex varchar(10),
s_age int
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
NOT NULL :在操作数据库时如果输入该字段的数据为NULL,则报错。
AUTO_INCREMENT :定义列为自增的属性,一般用于主键,数值会自动加1。
PRIMARY_KEY :定义列为主键,(也可以使用多列定义主键,以逗号分隔)(PRIMARY_KEY s_id, s_sex)。
ENGINE :设置存储引擎,不定义则使用默认存储引擎(MySQL默认存储引擎是 InnoDB)。
CHARSET :设置编码格式,默认是UTF-8。
DROP TABLE tb_name;
ALTER TABLE tb_name DROP column;
ALTER TABLE tb_name ADD column 类型; # 添加到最后一列。
ALTER TABLE tb_name ADD column 类型 FIRST; # 添加到第一列。
ALTER TABLE tb_name ADD column 类型 AFTER column; # 添加到某个字段之后。
ALTER TABLE tb_name MODIFY column 类型; # 使用MODIFY修改。
ALTER TABLE tb_name CHANGE old_column new_column 类型; # 使用CHANGE修改。
ALTER TABLE tb_name MODIFY column 类型 NOT NULL DEFAULT 100; # 指定字段非空,默认值100。
ALTER TABLE tb_name ALTER column SET DEFAULT 1000; # 修改字段默认值。
ALTER TABLE tb_name ALTER column DROP DEFAULT; # 删除字段默认值。
ALTER TABLE old_tb_name RENAME TO new_tb_name;
ALTER TABLE tb_name engine=InnoDB;
ALTER TABLE tb_name DROP FOREIGN KEY keyname;
INSERT INTO table_name (field1, field2, ……, fieldN) VALUES (value1, value2, ……, valueN);
UPDATE tb_name SET field1=new-value1, field2=new-value2 WHERE … ;
WHERE子句中可以指定任何条件。
DELETE FROM table_name WHERE … ;
WHERE 子句中可以指定任何条件。
SELECT column_name1, column_name2 FROM table_name1, table_name2
WHERE [condition1 [AND [OR]] condition2 …… ;
查询语句中可以使用一个或者多个表,表之间使用逗号(,)分割,并使用WHERE语句设定查询条件。
WHERE 子句中可以指定任何条件。
可以使用AND或者OR指定一个或多个条件。
可以使用LIKE子句代替等号(=),LIKE通常与(%)一同使用,类似于一个元字符的搜索(LIKE“ACC%”)。
WHERE子句也可以运用SQL的DELETE或UPDATE命令。
可以使用LIMIT属性来设定返回的记录数。
可以使用OFFSET指定SELECT语句开始查询的数据偏移量,默认偏移量为0。
WHERE子句类似于程序语言中的if条件,根据MySQL表中的字段值来读取指定的数据。
WHERE子句操作符:
= :等于
<>, != :不等于
> :大于
< :小于
>= :大于等于
<= :小于等于
UNION操作符用于连接两个以上的SELECT语句的结果组合到一个结果集合中。
多个SELECT语句会删除重复的数据。
SELECT expression1, expression2, ……, expressionN FROM tb_name1 WHERE ……
UNION [ ALL | DISTINCT ]
SELECT expression1, expression2, ……, expressionN FROM tb_name2 WHERE ……;
ALL :可选项,返回所有结果集,包含重复数据。
DISTINCT :可选项,删除结果集中重复的数据。
ORDER BY :对查询结果进行排序。
SELECT column1, column2 FROM tb_name WHERE …… ORDER BY field1 [ASC/DESC],field2 [ASC/DESC]。
ASC/DESC :升序/降序,默认ASC。
GROUP BY :对查询结果进行分组。
SELECT column1, column2 FROM tb_name WHERE …… GROUP BY column1, column2。
JOIN :在两个或多个表中查询数据。
INNER JOIN(内连接,或等值连接):获取两个表中字段匹配关系的记录。
内连接使用比较运算符对两个表中的数据进行比较,
并列出与连接条件匹配的数据行,组合成新的记录,
结果只保留满足条件的记录。
SELECT column1, … FROM tb_name1 INNER JOIN tb_name2 ON tb_name1.id = tb_name2.id;
LEFT JOIN(左连接):获取左表所有记录,即使右表没有对应匹配的记录。
左表保持不动,右表在右侧滑动,用右表匹配左表,结果保留左表的所有行,右表中不匹配的行默认填充为空值NULL。
SELECT column1, … FROM tb_name1 LEFT JOIN tb_name2 ON tb_name1.id=tb_name2.id;
RIGHT JOIN(右连接):获取右表所有记录,即使左表没有对应匹配的记录。
右表保持不动,左表在左侧滑动,用左表匹配右表,结果保留右表的所有行,左表中不匹配的行默认填充为空值NULL。
SELECT column1, … FROM tb_name1 RIGHT JOIN tb_name2 ON tb_name1.id=tb_name2.id;
IS NULL :当列的值是NULL,此运算符返回true。
IS NOT NULL :当列的值不为NULL,此运算符返回true。
<=> :比较操作符(不同于 = 运算符) :当比较的两个值相等或者都为NULL时,返回true。
MySQL中支持所有标准SQL数值数据类型。
包括严格数值数据类型(INTEGER,SMALLINT,DECIMAL,NUMERIC)。
以及近似数值数据类型(FLOAT,REAL,DOUBLE PRECISION)。
表示时间值的日期和时间类型(DATETIME,DATE,TIMESTAMP,TIME,YEAR)。
每个时间类型有一个有效值范围和一个“零”值,当指定不合法的MySQL不能表示的值时使用“零”值。
字符串类型(CHAR,VARCHAR,BINARY,VARBINARY,BLOB,TEXT,ENUM,SET)。
索引是应用在SQL查询语句的条件,一般作为WHERE子句的条件。
CREATE INDEX indexName ON tb_name(column(length)); # 直接创建索引。
ALTER TABLE tb_name ADD INDEX indexName(column); # 修改表结构时添加索引。
CREATE TABLE tb_name( # 创建表的时候直接指定。
id int NOT NULL,
username VARCHAR(16) NOT NULL,
INDEX [indexName] (username(length))
);
CREATE UNIQUE INDEX indexName ON tb_name(column(length)); # 直接创建索引。
ALTER TABLE tb_name ADD UNIQUE [indexName] (column(length)); # 修改表结构时创建索引。
CREATE TABLE tb_name( # 创建表时添加索引。
id int NOT NULL,
username VARCHAR(16) NOT NULL,
UNIQUE [indexName] (username(length))
);
DROP INDEX [indexName] ON tb_name;
ALTER TABLE tb_name ADD PRIMARY KEY (column); # 该语句添加一个主键,索引值必须是唯一的,且不能为NULL。
ALTER TABLE tb_name ADD UNIQUE indexName (column); # 该语句创建索引的值必须是唯一的(除NULL外)。
ALTER TABLE tb_name ADD INDEX indexName (column); # 添加普通索引,索引值可出现多次。
ALTER TABLE tb_name ADD FULLTEXT indexName (column); # 该语句指定索引为FULLTEXT,用于全文索引。
ALTER TABLE tb_name DROP INDEX column; # 删除索引。
ALTER TABLE tb_name ADD PRIMARY KEY (column); # 添加主键。
ALTER TABLE tb_name DROP PRIMARY KEY; # 删除主键。
SHOW INDEX FROM tb_name; # 显示索引信息。
查看执行计划,使用explain关键字可以模拟优化器执行SQL语句,查看使用到的索引列及其它信息。
explain select * from tb_name;
最常用的索引底层存储结构是棵 B- Tree 或 B+ Tree。
第一个字段是有序的。
当第一个字段值相等的时候,第二个字段也是有序的。
当第一个字段值相等、第二个字段值也相等时,第三个字段也是有序的。
例:在字段 a, b, c上创建一个联合索引,索引顺序会首先按照a字段排序,然后再按照b字段排序,最后是c字段。
下面的SQL语句是按照((a),(a, b),(a, b, c))的顺序用到索引。
select * from tb_name where a = 0;
select * from tb_name where a = 0 and b = 1;
select * from tb_name where a = 0 and b = 1 and c = 2;
下面的SQL语句只用到一个索引a 。
select * from tb_name where a = 0 and c = 2;
下面的SQL语句未使用到索引,因未遵循最左匹配原理。
select * from tb_name where b = 1 and c = 2;
以MySQL为例,下面的SQL语句也能使用到索引,查询优化器会重新编译,不建议这样使用。
select * from tb_name where b = 1 and c = 2 and a = 0;
聚集索引 与 非聚集索引底层引用的都是 B+ 树索引。
SELECT … INTO OUTFILE
将一个数据库的数据写入一个文件,输出不能是一个已存在的文件。
SELECT * FROM tb_name INTO OUTFILE ‘/tmp/tmp.txt’;
LOAD DATA INFILE
将文件读回数据库。
mysql命令导入
mysql -u用户名 -p密码 < 要导入的数据库数据(tmp.sql)
例:mysql -uroot -p123456 < tmp.sql # 将备份的整个数据库tmp.sql导入
source 命令导入
create database abc; # 创建数据库。
use abc; # 使用已创建的数据库。
set names utf8; # 设置编码。
source /home/abc/abc.sql; # 导入备份数据库。
LOAD DATA命令导入
LOAD DATA LOCAL INFILE ‘tmp.txt’ INTO TABLE myTable
FIELDS TERMINATED BY ‘:’
LINES TERMINATED BY ‘\r\n’; # 将tmp.txt文件中的数据导入myTable表中
如果指定LOCAL关键词,则表明从客户主机上按路径读取文件。
如果没有指定,则文件在服务器上按路径读取文件。
在MySQL中,只有使用了Innodb引擎的数据库或表才支持事务。
事务处理可以用来维护数据库的完整性,保证SQL语句要么全部执行,要么全部不执行。
事务用来管理 insert, update, delete 语句。
一个事务中的操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。
事务在执行过程中发生错误,会被回滚到事务开始前的状态。
在事务开始之前和事务结束之后,数据库的完整性没有被破坏。
这表示写入的资料必须完全符合所有的预设规则。
数据库允许多个并发事务同时对其数据进行读写和修改的能力。
隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。
事务处理结束后,对数据的修改是永久的,即便系统故障也不会丢失。
MySQL数据库事务默认都是自动提交的,即执行完SQL语句后就会马上执行COMMIT操作。然后显式地开启、提交事务
BEGIN / START TRANSACTION : 开启一个事务。 COMMIT / COMMIT WORK :提交事务,使已对数据库进行的所有修改成为永久性的。 ROLLBACK / ROLLBACK WORK :回滚事务,撤销正在进行的所有未提交的修改。 SAVEPOINT identifier :允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT。 RELEASE SAVEPOINT identifier :删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常。 ROLLBACK TO identifier :把事务回滚到标记点。 SAVEPOINT 是在数据库事务处理中实现“子事务”,也称嵌套事务的方法。 事务可以回滚到SAVEPOINT而不影响SAVEPOINT创建前的变化,不需要放弃整个事务。 SAVEPOINT savepoint_name; # 声明一个savepoint 。 ROLLBACK TO savepoint_name; # 回滚到 savepoint 。 RELEASE SAVEPOINT savepoint_name; # 删除指定保留点 。 SET TRANSACTION :用来设置事务的隔离级别。 MySQL事务处理的两种方法 1) 用BEGIN, ROLLBACK, COMMIT实现。 BEGIN 开始一个事务。 ROLLBACK 回滚事务。 COMMIT 提交事务。 2)直接用SET来改变MySQL的自动提交模式 SET AUTOCOMMIT = 0 :禁止自动提交。 SET AUTOCOMMIT = 1 :开启自动提交。
不同的数据库隔离级别不同,使用加锁的方式也不同。
MySQL支持四种事务隔离级别,默认隔离级别是(RR, Repeatable Read)。
Oracle 支持两种事务隔离级别(RC 与 Serializable),默认隔离级别是(RC, Read Committed)。
就是对脏数据的读取,脏数据指的是未提交的数据。
一个事务先后读取同一条记录,两次读取的数据不同。
一个事务按相同的查询条件重新读取以前检索过的数据,却发现其它事务插入了满足其查询条件的新数据。
T1读取了已经被T2修改但还未提交的字段,由于某种原因,T2事务回滚,则T1读取的内容是临时且无效的;这就是脏读。
T1读取一个字段,之后T2更新了该字段,T1再次读取该字段值时则读取到的是被T2更新后的新值;这就是不可重复读。
T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行,之后T1再次读取该表时会多出几行。这就是幻读。
常见的数据库存储引擎有:(1)MyISAM (2)InnoDB 。
锁主要用于多用户环境下保证数据库完整性和一致性。
每次去拿数据的时候都认为别人不会修改,所以不会上锁。
但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
如果发生冲突了,则返回用户错误信息,让用户决定如何去做。
大多是基于数据版本(Version)记录机制实现。
每次去拿数据的时候都认为别人会修改,所以每次拿数据的时候都会上锁。
共享锁(Share Lock),S锁,也叫读锁,用于所有的只读数据操作。
共享锁是非独占的,允许多个并发事务读取其锁定的资源。
排他锁(Exclusive Lock),X锁,也叫写锁,用于对数据进行写操作。
如果一个事务对对象加了排他锁,其它事务就不能再给它加任何锁了。
意向锁是InnoDB自动加的,不需要用户干预。
表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的IS锁。
表示事务准备给数据行加入排它锁,也就是说一个数据行加排它锁前必须先取得该表的IX锁。
行级锁不是锁记录,而是锁索引;只有通过索引条件检索数据,才能使用行级锁。
SELECT * FROM tb_name WHERE …… LOCK IN SHARE MODE; # 加共享锁。
SELECT * FROM tb_name WHERE …… FOR UPDATE; # 加排他锁。
LOCK TABLE tb_name WRITE; # 加写锁
LOCK TABLE tb_name READ; # 加读锁
UNLOCK TABLES; # 释放锁
行锁,单条索引记录加锁,Record Lock
锁住的是索引,而非记录本身。
innodb_locks_unsafe_for_binlog
参数为0,默认采用Next-key Lock。死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,他们都将无法推进下去。
MVCC:Multi-Version Concurrency Control 多版本并发控制。
MVCC是一种并发控制的方法,一般在数据库管理系统中实现对数据库高并发场景下的吞吐性能。
例:事务1、事务2, DATA_TRX_ID, DATA_ROLL_PTR, DB_ROW_ID。
事务1。
执行新增一条数据 insert操作。
此时DB_ROW_ID = 1, DATA_TRX_ID = 1(系统版本号), DATA_ROLL_PTR = NULL。
事务 2 执行update操作过程。
对DB_ROW_ID = 1这行记录加排它锁。
把该行copy前的值拷贝到undo log中。
修改该行的值,这时会产生一个新版本号,更新DATA_TRX_ID为修改记录的事务ID。
将DATA_ROLL_PTR指向刚刚copy到undo log 链中的旧版本记录,这样就能通过DATA_ROLL_PTR找到这条记录的历史版本;如果对同一行记录执行连续的UPDATE, undo log会组成一个链表,遍历这个链表可以看到这条记录的变迁。
记录redo log,包括undo log中的修改。
InnoDB核心:(1)日志 (2)内存(缓存池(Buffer Pool)) (3) 磁盘(Datafile)
InnoDB存储引擎有多个内存块,这些内存块组成了一个大的内存池。
后台线程主要负责刷新内存池中的数据,将已修改的数据刷新到磁盘。
当某个事务进行一次写操作时,InnoDB引擎将数据写入redo log后就会提交事务。
而非写入到磁盘(Datafile),之后InnoDB再异步地将新事务的数据异步地写入Datafile,真正存储起来。
用来恢复事务所对应的脏数据块的日志文件。
未提交的事务,即该事务未被执行commit命令。
重做日志,提供前滚操作。
Redo log 通常是物理日志,记录的是数据页的物理修改,用来恢复提交后的物理数据页。
恢复数据页、且只能恢复到最后一次提交的位置。
内存中的日志缓冲(redo log buffer),该部分日志是易失性的。
磁盘上的重做日志文件(redo log file),该部分日志是持久的。
二进制日志,记录对数据发生或潜在发生更改的SQL语句,并以二进制的形式保存在磁盘中。
可以用来查看数据库的变更历史(具体的时间点所做的操作),数据库增量备份和恢复。
Show variables like %log_bin% ;
最核心的线程,主要负责将缓存池中的数据异步刷新到磁盘,保证数据的一致性。
IO Thread 主要负责大量的异步IO来处理写IO请求。
Purge Thread回收已经使用并分配的undo页,InnoDB支持多个Purge Thread,这样做可以加快undo页的回收。
Page Cleaner Thread是将之前版本中脏页的刷新操作都放入单独的线程中来完成,减轻Master Thread的工作及对于用户查询线程的阻塞。
InnoDB引擎使用缓存池技术来提高数据库的整体性能。
InnoDB中缓存池页的大小默认为16KB。
当一个数据被用到时,其附近的数据也通常会马上被使用,程序运行期间所需要的数据通常比较集中。
对于数据库中页的修改操作,首先修改在缓存池中的页,然后再以一定的频率刷新到磁盘。
为了更好的管理这些被缓存的页,InnoDB为每一个缓存页都创建了一些控制信息(控制块)。
这些控制信息包括该页所属的表空间编号、页号、页在Buffer Pool中的地址、锁信息、LSN信息等。
每个缓存页对应一个控制块,每个控制块占用的内存大小是相同的,它们都被放到Buffer Pool中,如下图。
启动MySQL服务器时,需要对Buffer Pool进行初始化,将Buffer Pool划分成若干对控制块和缓存页。
随着程序的运行,会不断的将磁盘上的页缓存到Buffer Pool中,但如何管理Buffer Pool中空闲的缓存页呢?
使用Free List(空闲链表)来管理空闲的缓存页,如下图。
Free List控制信息:包含链表的头结点地址、尾结点地址、以及当前链表中结点的数量。
每个Free List的结点中都记录了某个缓存页控制块的地址。
每个缓存页控制块都记录着对应的缓存页地址。
每当需要从磁盘中加载一个页到Buffer Pool中时,就从Free List中取一个空闲的缓存页,并且把该缓存页对应的控制块的信息填上,然后把该缓存页对应的Free List结点从Free List链表中删除。
缺点
InnoDB存储引擎对传统的LRU算法做了一些优化。
在InnoDB中加入了midpoint,新读到的页,虽然是最新访问的页。
但并不直接插入到LRU列表的首部,而是插入到LRU列表的midpoint位置。
默认配置插入到列表长度的 5/8 处,midpoint由参数innodb_old_blocks_pct控制。
Midpoint之前的列表称之为new列表,之后的列表称之为old列表。
可以简单的将new列表中的页理解为最活跃的热点数据。
InnoDB存储引擎还引入了 innodb_old_blocks_time 来表示页读取到mid位置之后需要等待多久才会被加入到LRU列表的热端,可以通过设置该参数保证热点数据不轻易被刷出。
FLUSH链表用来管理将页刷新回磁盘,缓存池中通过FLUSH 链表存储需要被刷新到磁盘上的页(脏页)。
这里的脏页指的是此页被加载进Buffer Pool后第一次修改后的页。
只有第一次修改时才需要加入FLUSH链表(第二次修改时已经存在了)。
redo log中记录了Checkpoint的位置。
这个点之前的页已经被刷新回磁盘,只需要对Checkpoint之后的redo log进行恢复。
根据LRU算法,溢出最近最少使用页。
如果页为脏页,强制执行Checkpoint,将脏页刷新回磁盘。
由于redo log是循环使用的,这部分对应的数据还未刷新到磁盘。
数据库恢复时,如果不需要这部分日志即可被覆盖。
如果需要,必须强制执行Checkpoint,将缓存池中的页至少刷新到当前重做日志的位置。
Sharp Checkpoint发生在数据库关闭时,将所有的脏页都刷新回磁盘。
缺点:不适用于数据库运行时的刷新。
Fuzzy Checkpoint适用于数据库运行时刷新脏页,只刷新一部分脏页。
MasterThread Checkpoint
异步刷新,每秒或每10秒从缓存池脏页列表刷新一定比例的页回磁盘。
FLUSH_LRU_LIST Checkpoint
若Buffer Pool中没有足够的空间时,根据LRU算法、溢出LRU列表尾端的页。
如果这些页有脏页,需要进行Checkpoint(Page Cleaner Thread线程就是做这个事的)。
InnoDB存储引擎需要保证LRU列表中差不多有100个空闲页可供使用。
Innodb_lru_scan_dept :控制LRU列表中可用页的数量,默认1024。
Asnc / Sync Flush Checkpoint
指重做日志不可用时,需要强制刷新页回磁盘。
此时的页是脏页列表(FLUSH LIST)中选取的。
事务日志中每条记录的编号。
InnoDB存储引擎,通过LSN(Log Sequence Number)来标记版本,LSN是8字节的数字。
Checkpoint_age = redo_lsn – checkpoint_lsn。
Async_water_mark = 75% * total_redo_file_size。
Sync_water_mark = 90% * total_redo_file_size。
即脏页太多,强制checkpoint,保证缓存池中有足够可用的页。
参数设置:innodb_max_dirty_pages_pct = 75。
表示:当缓存池中脏页的数量占75%时,强制checkpoint。1.0x之后默认75。
Insert Buffer的设计,对于非聚集索引的插入和更新操作,不是每次都直接插入到索引页中。
而是先判断插入非聚集索引页是否在缓存池中。
若存在则直接插入,若不存在则先放入一个Insert Buffer对象中。
数据库这个非聚集的索引并没有插入到叶子结点(因为B+树只有叶子结点才存储数据),而是存放在另一个位置。
然后再以一定的频率和情况进行Insert Buffer和辅助索引页子结点的merge(合并)操作。
这时通常能将多个插入合并到一个操作中(因为在一个索引页中),这就大大提高了对于非聚集索引插入的性能。
假设有这样一个场景,当数据库正在从内存向磁盘写一个数据页时,数据库宕机。
从而导致这个页只写了部分数据,这就是部分写失效,它会导致数据丢失。
这时是无法通过重做日志(redo log)恢复的。
因为重做日志记录是对页的物理修改,如果页本身已损坏,重做日志也无能为力。
如何解决以上问题 ?
为了解决以上问题,可以使用两次写操作。
因为在磁盘共享表空间中已有数据页副本拷贝,如果数据库在页写入数据文件的过程中宕机。
在实例恢复时,可以从共享表空间中找到该页副本,将其拷贝覆盖原有的数据页,再应用重做日志即可。
InnoDB默认开启两次写功能,可以通过skip_innodb_doublewrite禁用两次写功能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。