1.MySQL存储引擎

  插件式存储引擎是MySQL数据库最重要的特性之一,用户可以根据应用的需要选择如何存储和索引数据、是否使用事务等。MySQL默认支持多种存储引擎,以适用不同领域的数据库应用需要,用户可以通过选择使用不同的存储引擎提高应用的效率,提供灵活的存储,用户甚至可以按照自己的需要定制和使用自己的存储引擎,以实现最大程度的可定制性。MySQL5.5之前默认存储引擎是MyISAM,5.5之后默认为InnoDB。
  常用存储引擎的对比,如下图所示:

特点 MyISAM InnoDB MEMORY MERGE NDB
存储限制 64T 没有
事务安全 支持
锁机制 表锁 行锁 表锁 表锁 行锁
B树索引 支持 支持 支持 支持 支持
哈希索引 支持 支持
全文索引 支持 5.6版本开始支持
集群索引 支持
数据缓存 支持 支持 支持
索引缓存 支持 支持 支持 支持 支持
数据可压缩 支持
空间使用 N/A
内存使用
批量插入速度
支持外键 支持

1.1 MyISAM

  MyISAM不支持事务,也不支持外键,其优势是访问的速度快,对事务的完整性没有要求或者以SELECT、INSERT为主的应用基本上都可以使用这个引擎。每个MyISAM在磁盘上存储成3个文件,其文件名都和表名相同,但扩展名分别是:

.frm(表存储定义)
.MYD(MYData,存储数据)
.MYI(MYIndex,存储索引)

1.2 InnoDB

  InnoDB存储引擎提供了具有提交,回滚和崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些,并且会占用更多的磁盘空间以保留数据和索引。支持外键,也是唯一支持外键的存储引擎。在导入多个表时,如果需要忽略表之间的导入顺序,可以暂时关闭外键的检查。同样在执行LOAD DATA和ALTER TABLE操作的时候,可以通过暂时关闭外键约束来加快处理的速度。

关闭命令: SET FOREIGN_KEY_CHECKS = 0
启用命令: SET FOREIGN_KEY_CHECKS = 1

1.3 MEMORY

  MEMORY存储引擎使用存在于内存中的内容来创建表。每个MEMORY表只实现对应一个磁盘文件,格式是.frm。MEMORY表访问速度非常快,并且默认使用HASH索引,但一旦关闭服务,表中的数据就会丢失。在启动MySQL服务的时候使用–init-file选项,把INSERT INTO…SELECT或LOAD DATA INFILE这样的语句放入这个文件中,就可以在服务启动时从持久固定的数据源装载表。在定义MEMORY表的时候,可以通过MAX_ROWS指定表的最大行数。
  MEMORY类型的存储引擎主要用于那些内容变化不频繁的表,或者作为统计操作的中间结果表,便于高效地对中间结果进行分析并得到最终的统计结果。

1.4 MERGE

  MERGE存储引擎是一组MyISAM表的组合,这些MyISAM表必须结构完全相同,MERGE表本身并没有数据,对MERGE类型的表可以进行查询、更新、删除操作,这些操作实际上是对内部的MyISAM表进行的。对于MERGE表的插入操作,是通过INSERT_METHOD子句定义插入的表,可以有3个不同的值,使用FIRST或LAST值使得插入操作被相应地作用在第一或最后一个表上,不定义这个子句或者定义为NO,表示不能对这个MERGE表执行插入操作。
  可以对MERGE表进行DROP操作,这个操作只是删除MERGE的定义,对内部的表没有任何影响。

1.5 TokuDB

  前面介绍的都是MySQL自带的存储引擎,除了这些之外,还有一些常见的第三方存储引擎,比如列式存储引擎Infobright、高写性能高压缩的TokuDB。
  TokuDB是一个高性能、支持事务处理的MySQL和MariaDB的存储引擎、具有高扩展性、高压缩率、高效的写入性能,支持大多数在线DDL操作。

2.MySQL字符集

2.1字符集选择

  MySQL目前支持几十种字符集,包括UCS-2、UTF-16、UTF-16LE、UTF-32、UTF-8和utf8mb4等unicode字符集。面对众多的字符集,我们该如何选择?

(1)满足应用支持语言的需求,如果应用要处理各种各样的文字,或者将发布到使用不同语言的国家或地区,就应该选择Unicode字符集。对MySQL来说,目前就是UTF-8。
(2)如果应用中涉及已有数据的导入,就要充分考虑数据库字符集对已有数据的兼容性。如已有数据是GBK文字,如果选择GB2312-80位数据库字符集,就很可能出现某些文字无法正确导入的问题。
(3)如果数据库只需要支持一般中文,数据量很大,性能要求也很高,就应该选择双字节定长编码的中文字符集,比如GBK。因为相对于UTF-8而言,GBK比较“小”,每个汉字只占2个字节,而UTF-8汉字编码需要3个字节,这样可以减少磁盘I/O、数据库Cache以及网络传输时间,从而提高性能。相反,如果应用主要处理英文字符,仅有少量汉字数据,那么选择UTF-8更好,因为GBK、UCS-2、UTF-16的英文字符编码都是2个字节,会造成不必要的开销。
(4)如果数据库需要做大量的字符运算,如比较、排序等,那么选择定长字符集可能更好,因为定长字符集的处理速度要比变长字符集的处理速度快。
(5)如果所有客户端程序都支持相同的字符集,则应该优先选择该字符集作为数据库字符集。这样可以避免因字符转换带来的性能开销和数据损失。

表2-1 常用字符集比较
字符集 是否定长 编码方式 其他说明
ASCII 单字节7位编码 最早的奠定性字符
ISO-8859-1/latin1 单字节8位编码 西欧字符集,经常被一些程序员用来转码
GB 2312-80 双字节编码 早期标准,不推荐再使用
GBK 双字节编码 虽然不是国标,但支持的系统不少
GB 18030 2字节或4字节编码 开始有一些支持,但数据库支持的还少见
UTF-32 4字节编码 UCS-4原始编码,目前很少采用
UCS-2 2字节编码 Windows 2000内部用UCS-2
UTF-16 2字节或4字节编码 Java和Windows XP/NT等内部使用UTF-16
UTF-8 1~4字节编码 互联网和UNIX/Linux广泛支持的Unicode字符集

2.2字符集设置

  MySQL服务器支持多种字符集,在同一台服务器、同一个数据库甚至同一个表的不同字段都可以指定使用不同的字符集,相比Oralce等其他数据库管理系统,在同一个数据库只能使用相同的字符集,MySQL明显存在更大的灵活性。
  MySQL的字符集和校对规则(用来比较字符串的方式)有4个级别的默认设置:服务器级、数据库级、表级和字段级。他们分别在不同的地方设置,作用也不相同。

3.MySQL索引

3.1索引的存储分类

  索引是在MySQL的存储引擎层中实现的,而不是在服务器层实现的。所以每种存储引擎的索引都不一定完全相同,也不是所有的存储引擎都支持所有的索引类型。MySQL目前提供了以下4中索引:

1、B-TREE索引:最常见的索引类型,大部分引擎都支持B树索引。
2、HASH索引:只有Memory引擎支持,使用场景简单。
3、R-TREE索引(空间索引):空间索引是MyISAM的一个特殊索引类型,主要用于地 理空间数据类 型,通常使用较少。
4、Full-text(全文索引):全文索引也是MyISAM的一个特殊索引类型,主要用于全文 索引,InnoDB 从MySQL5.6版本开始提供对全文索引的支持。

  比较常用的索引就是B-Tree索引和Hash索引。Hash索引相对简单,只有Memory/Heap引擎支持Hash索引。Hash索引适用于Key-Vlaue查询,Hash索引要比B-Tree索引查询更迅速,但不适合范围查询,例如<、>、<=、>=这些操作。如果不使用“=”进行查询,那么不会用到索引。
  B-Tree索引较为复杂,下面详细介绍B-Tree索引。

3.2索引使用

  B-Tree索引是最常见的索引,构造类似二叉树,能根据键值提供一行或者一个行集的快速访问,通常只需要很少的读操作就可以找到正确的行。不过,需要注意B-Tree索引中的B不代表二叉树(binary),也是代表平衡树(balance)。B-Tree索引并不是一颗二叉树。

MySQL进阶知识

图3-1 B-Tree索引结构

  MySQL中能够使用索引的典型场景

1、匹配全值(Match the full value),复合索引所有列都有等值匹配条件。
2、匹配最左前缀(Match a
leftmost prefix),使用复合索引中的最左边列进行查询。
3、匹配值的范围查询(Match a range of values)
4、仅仅对索引进行查询(Index only query),查询的列都在索引的字段中。
5、匹配列前缀(Match a column prefix),仅仅使用索引中的第一列,并且只包含索引第一列的开头一部分进行查询。

  MySQL中不能够使用索引的典型场景

1、以%开头的LIKE查询。
2、数据类型出现隐式转换,例如last_name 为字符类型,where last_name =1不使用索引,where last_name ='1’使用索引。
3、复合索引,查询条件不包含索引列最左边部分。
4、MySQL预估使用索引比全表扫描更慢。

4.MySQL数据库对象优化

  数据库对象设计的好坏是一个数据库设计的基础,而且一旦数据库对象设计完毕并投入使用,将来再进行是修改就比较麻烦,因此在进行数据库设计的时候一定要尽可能地考虑周全。

4.1优化表的数据类型

  表需要使用何种数据类型是需要根据应用来判断的。虽然应用设计的时候需要考虑字段的长度有一定的冗余,但是不推荐让很多字段都留有大量的冗余,这样既浪费磁盘存储空间,同时在应用程序操作时也浪费物理内存。
  MySQL中,可以使用函数PROCEDURE ANALYSE()对当前应用的表进行分析,该函数可以对数据表中的数据类型提出优化建议。

SELECT * FROM table_name PROCEDURE ANALYSE();

4.2通过拆分提高表的访问效率

(1)垂直拆分:把主码和一些列放到一个表,然后把主码和另外的列放到另一个表中。
  如果一个表中的某些列常用,而另一些列不常用,可以采用垂直拆分。垂直拆分可以使得数据行变小,一个数据页就能存放更多的数据,在查询时就会减少I/O次数。其缺点是需要管理冗余列,查询所有数据需要联合查询。

(2)水平拆分:即根据一列或多列数据的值把数据行放到两个独立的表中。水平拆分通常在以下几种情况下使用:

●表很大,分割后可以降低查询时需要读的数据和索引的页数,同时也降低了索引的层数,提高查询速度。
●表中的数据本来就有独立性,例如,表中分别记录各个地区的数据或者不同时期的数据,特别是有些数据常用,而另外一些数据不常用。
●需要把数据存放到多个介质上。

  例如,移动电话的账单表就可以分成两个表或者多个表。最近3个月的账单数据存在一个表中,3个月前的数据存在另外一张表中,超过1年的历史数据可以存储到单独的存储介质上,这种拆分是最常用的水平拆分方法。

4.3逆规范化

  数据库设计要满足规范化这个道理大家都非常清楚,但是否数据的规范化程度越高越好呢?这还是有实际需求来决定。因为规范化越高,那么产生的关系就越多,导致查询时表间的连接操作越频繁,从而降低性能,直接赢下查询速度。所以,对于查询较多的应用就需要根据实际情况运用逆规范化对数据进行设计,提高查询效率。
  逆规范化的好处是降低连接操作,降低外码和索引的数目,还可能减少表的数目,加快查询速度,但会降低修改速度,可能出现数据的完整性问题。需要根据具体需求,权衡利弊谨慎使用。如果好的索引能够解决问题,不必使用逆规范化。

  对于数据量较大的表,在其上进行统计查询通常效率会很低,并且还要考虑统计查询是否会对在线的应用产生负面影响。通常这种情况使用中间表可以提高查询的效率,下面通过对session表的统计来介绍中间表的使用。
  (1)session表记录了客户每天的消费记录,表结构如下:

CREATE TABLE session(
  cust_id varchar(10), --客户编号
  cust_amount decimal(16,2), --客户消费金额
  cust_date date, --客户消费时间
  cust_ip varchar(20) --客户IP地址

  (2)由于每天都会产生大量的客户消费记录,所以session表的数据量很大,现在业务部门有一具体需求:希望了解最近一周客户的消费总金额和近一周每天不同时段用户的消费总金额。针对这一需求我们通过2种方法来得出业务部门想要的结果。
  方法1:在session表上直接进行统计,得出结果。

MySQL> select sum(cust_amount) from session where cust_date>adddate(now(),-7);
±--------------------------+
| sum(cust_amount) |
±--------------------------+
| 161699200.64 |
±--------------------------+
1 row in set(3.95 sec)

  方法2:创建中间表tmp_session,表结构和源表结构完全相同。

CREATE TABLE tmp_session(
  cust_id varchar(10), --客户编号
  cust_amount decimal(16,2), --客户消费金额
  cust_date date, --客户消费时间
  cust_ip varchar(20) --客户IP地址

  转移要统计的数据到中间表,然后在中间表上进行统计,得出结果。

MySQL> insert into tmp_session select * from session where > cust_date>adddate(now(),-7);;
Query OK, 1573328 rows affected (6.67 sec)
Records: 1573328 Duplicates: 0 Warnings: 0
MySQL> select sum(cust_amount) from tmp_session;
±--------------------------+
| sum(cust_amount) |
±--------------------------+
| 161699200.64 |
±--------------------------+
1 row in set(0.73 sec)

  从上面的2种实现方法上看,在中间表中做统计花费的时间少(这里不计算数据转移的时间)。另外,针对这个统计需求,源表的数据量大而且cust_date没有索引,所以按时间统计效率低,中间表更适合这种统计查询。

5.MySQL分区

  分区有利于管理非常大的表,它采用“分而治之”的逻辑,分区引入了分区键(partition key)的概念,分区键用于根据某个区间值(或者范围值),特定值列表或者HASH函数值执行数据的聚集,让数据根据规则分布在不同的分区中,让一个对象变成一些小对象。
  分区类型主要有以下4种:

●RANGE分区:基于一个给定连续区间范围,把数据分配到不同的分区。
●LIST分区:类似于RANGE分区,区别在LIST分区是基于枚举出的值列表分区,RANGE是基于给定的连续区间范围分区。
●HASH分区:基于给定的分区个数,把数据分配到不同的分区。
●KEY分区:类似于HASH分区

5.1 RANGE分区

  按照RANGE分区的表是利用取值范围将数据分成分区,区间要连续并且不能互相重叠,使用VALUES LESS THEN操作符进行分区定义。
  例如,雇员表emp中按商店ID(store_id)进行RANGE分区:

CREATE TABLE emp(
  id INT NOT NULL,
  ename VARCHAR(30),
  hired DATE NOT NULL DEFAULT ‘1970-01-01’,
  sparated DATE NOT NULL DEFAULT 9999-12-31,
  job VARCHAR(30) NOT NULL,
  store_id INT NOT NULL
)
PARTITION BY RANGE(store_id)(
  PARTITION p0 VALUES LESS THEN(100),
  PARTITION p1 VALUES LESS THEN(200),
  PARTITION p2 VALUES LESS THEN(300)
);

5.2 LIST分区

  LIST分区是建立离散的值列表告诉数据库特定的值属于哪个分区,LIST分区在很多方面类似于RANGE分区,区别在LIST分区是从属于一个枚举列表的值的集合,RANGE分区是从属于一个连续区间值的集合。

CREATE TABLE expenses(
  expense_date DATE NOT NULL,
  category INT,
  amount DECIMAL(10,3)
)PARTITION BY LIST(category)(
  PARTITION p0 VALUES IN (1,2),
  PARTITION p1 VALUES IN (3,4,5),
  PARTITION p2 VALUES IN (6),
  PARTITION p3 VALUES IN (7,8),
  PARTITION p4 VALUES IN (9)
);

5.3 HASH分区

  HASH分区主要用来分散热点读,确保数据在预先确定个数的分区中尽可能平均分布。对一个表执行HASH分区,MySQL会对分区键应用一个散列函数,以此确定数据应当放在N个分区中的哪个分区中。
  MySQL支持两种HASH分区,常规HASH分区和线性HASH分区(LINEAR HASH 分区)。常规HASH分区使用的是取模算法,线性HASH分区使用的是一个线性的2的幂的运算法则。
  例,常规HASH分区:

CREATE TABLE emp(
  id INT NOT NULL,
  ename VARCHAR(30),
  hired DATE NOT NULL DEFAULT ‘1970-01-01’,
  sparated DATE NOT NULL DEFAULT 9999-12-31,
  job VARCHAR(30) NOT NULL,
  store_id INT NOT NULL
)
PARTITION BY HASH(store_id) PARTITIONS 4;

  例如,插入store_id列值为234,计算分区MOD(234,4) =2,这条数据将被保存到第二个分区中。

  例,线性HASH分区:

CREATE TABLE emp(
  id INT NOT NULL,
  ename VARCHAR(30),
  hired DATE NOT NULL DEFAULT ‘1970-01-01’,
  sparated DATE NOT NULL DEFAULT 9999-12-31,
  job VARCHAR(30) NOT NULL,
  store_id INT NOT NULL
)
PARTITION BY LINEAR HASH(store_id) PARTITIONS 4;

  同样的,使用线性HASH分区时,指定记录保存在哪个分区也是可以计算出来的,假设将要保存记录的分区编号为N,num表示分割成分区的数量,那么N可以通过以下算法得到:

●首先,找到下一个大于等于num的2的幂,这个值设为V,V可以通过下面公式得到:
V=Power(2,Ceiling(Log(2,num)))
例如,刚才创建的emp表预先定义了4个分区,也就是num=4。
V=Power(2,Ceiling(Log(2,num)))
=Power(2,Ceiling(Log(2,4)))
=Power(2,Ceiling(2))
=Power(2,2)
=4
●其次,设置N=F(column_list)&(V-1)
例如,我们刚才计算出V=4,现在计算store_id=234对应的N值。
N=F(column_list)&(V-1)
=234&(4-1)
=2
●当N>=num时,设置V= Ceiling(V/2),设置N=N&(V-1)
对于store_id=234这条记录,由于N=2<4,所以直接能够判断出这条记录会被保存在第 二个分区中。
线性HASH分区的优点是,在分区维护(包含增加,删除,合并,拆分分区时),MySQL能够处理得更加迅速;缺点是,对比常规HASH分区(取模)的时候,线性HASH分区各个分区之间数据的分布不太均衡。

5.4 KEY分区

  按照KEY进行分区非常类似于按照HASH进行分区,只不过HASH分区允许使用用户自定义的表达式,而KEY分区不允许使用用户自定义的表达式,需要使用MySQL服务器提供的HASH函数;同时HASH分区只支持整数分区,而KEY分区支持使用除BLOB or Text类型外其他类型的列作为分区键。

CREATE TABLE emp(
  id INT NOT NULL,
  ename VARCHAR(30),
  hired DATE NOT NULL DEFAULT ‘1970-01-01’,
  sparated DATE NOT NULL DEFAULT 9999-12-31,
  job VARCHAR(30) NOT NULL,
  store_id INT NOT NULL
)
PARTITION BY KEY(store_id) PARTITIONS 4;

6.MySQL表的导入导出

6.1导出

  在某些情况下,为了一些特定的目的,经常需要将表里的数据导出为某些符号分割的纯数据文本,而不是SQL语句。
  方法一:使用SELECT … INTO OUTFILE …命令来导出数据,具体语法如下。

SELECT * FROM tablename INTO OUTFILE ‘target_file’ [option];
其中option参数可以是以下选项:
fields terminated by ‘string’(字段分隔符,默认为制表符’\t’)
fields enclosed by ‘char’(字段引用符)
fields escaped by ‘char’(转义字符,默认’’)
lines starting by ‘string’(每行前都加此字符,默认’’)
lines terminated by ‘string’(行结束符,默认’\n’)
其中char表示只能是单个字符,string表示可以是字符串。
例:select * from emp into outfile ‘D:/tmp/emp.txt’ fields terminated by ‘,’ enclosed by ‘"’;

  方法二:使用MySQLdump导出数据为文本的具体语法如下。

MySQLdump -u username -T target_dir dbname tablename [option];
其中option参数可以是以下选项:
–fields-terminated-by=‘string’(字段分隔符)
–fields-enclosed-by=‘char’(字段引用符)
–fields-optionally -enclosed-by=‘char’(字段引用符,只用在char,varchar和text等字符型字段上)
–fields - escaped-by=‘char’(转义字符)
–lines-terminated-by=‘string’(记录结束符)
例:MySQLdump -uroot -T D:/tmp test emp --fields-terminated-by=’,’ --fields-enclosed-by=’"’;
其中char表示只能是单个字符,string表示可以是字符串。

6.2导入

  对通过SELECT … INTO OUTFILE和MySQLdump导出的数据文件,进行导入。
  方法一:使用LOAD DATA INFILE …命令,具体语法如下。

LOAD DATA [LOCAL] INFILE ‘filename’ INTO TABLE tablename [option];
其中option参数可以是以下选项:
fields terminated by ‘string’(字段分隔符,默认为制表符’\t’)
fields enclosed by ‘char’(字段引用符)
fields escaped by ‘char’(转义字符,默认’’)
lines starting by ‘string’(每行前都加此字符,默认’’)
lines terminated by ‘string’(行结束符,默认’\n’)
ignore number lines(忽略输入文件中的前n行数据)
(col_name_or_user_var,…)(按照列出的字段顺序和字段数量加载数据)
set col_name = expr,…(将列做一定的数值转换后再加载)
其中char表示只能是单个字符,string表示可以是字符串。
field,lines和前面SELECT … INTO OUTFILE …的含义完全相同,不同的是多了几个不同的选项。
例:load data infile ’ D:/tmp/emp.txt ’ into table emp field terminated by ‘,’ enclosed by ‘"’;

  方法二:用MySQLimport来实现,具体命令如下。

MySQLimport -u root -p*** [–LOCAL] dbname order_tab.txt [option]
其中option参数可以是以下选项:
–fields-terminated-by=‘string’(字段分隔符)
–fields-enclosed-by=‘char’(字段引用符)
–fields-optionally -enclosed-by=‘char’(字段引用符,只用在char,varchar和text等字符型字段上)
–fields - escaped-by=‘char’(转义字符)
–lines-terminated-by=‘string’(记录结束符)
–ignor-lines(忽略输入文件中的前n行数据)
例:MySQLimport -uroot test D:/tmp/emp.txt --fields-terminated-by =’,’ --fields-enclosed-by =’"’;

相关文章:

  • 2021-12-12
  • 2021-11-05
  • 2021-06-01
  • 2021-08-21
  • 2022-12-23
  • 2021-12-31
  • 2021-06-08
  • 2021-08-23
猜你喜欢
  • 2022-12-23
  • 2021-12-12
  • 2021-06-01
  • 2021-06-24
  • 2021-12-15
  • 2021-08-16
  • 2021-10-04
相关资源
相似解决方案