一、生成400万数据
在说MySql之前,我们需要准备一个Mysql数据库以及数据库生成数据的脚步(大概需要百万以上的数据),后面篇幅也需要用到该脚本
脚本:
1.1创建表结构sql脚本
/*部门表*/
CREATE TABLE dept(
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
dname VARCHAR(20) NOT NULL DEFAULT "", /*名称*/
loc VARCHAR(13) NOT NULL DEFAULT "" /*地点*/
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
/*员工表*/
CREATE TABLE emp
(empno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2) NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
)ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
/*薪水*/
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2) NOT NULL,
hisal DECIMAL(17,2) NOT NULL
)ENGINE=MyISAM DEFAULT CHARSET=utf8;
1.2创建自定义函数脚本
create function rand_string(n INT)
returns varchar(255) #该函数会返回一个字符串
begin
declare chars_str varchar(100) default 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ';
declare return_str varchar(255) default '';
declare i int default 0;
while i < n do
set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));
set i = i + 1;
end while;
return return_str;
end
create FUNCTION rand_num()
RETURNS int(5)
BEGIN
DECLARE i int default 0;
set i =floor(10+RAND()*500);
return i;
END;
1.3存储过程
delimiter $$
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
#set autocommit =0 把autocommit设置成0
set autocommit = 0;
repeat
set i = i + 1;
insert into emp values ((start+i) ,rand_string(6),FLOOR(1 + (RAND() * 2)),0001,curdate(),FLOOR(1000 + (RAND() * 1000)),FLOOR(400 + (RAND() * 400)),rand_num());
until i = max_num
end repeat;
commit;
end $$
1.4生成数据
/*测试数据*/
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);
INSERT INTO dept VALUES (128,'研发部','北京');
INSERT INTO dept VALUES (129,'市场部','上海');
INSERT INTO dept VALUES (130,'运营部','深圳');
INSERT INTO dept VALUES (131,'技术部','广州');
INSERT INTO dept VALUES (132,'运维部','重庆');
call insert_emp (100001,40000000);
update emp set job='BOSS' WHERE job='1';
update emp set job='SALESMAN' WHERE job!='1';
二、MySQL逻辑架构
如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器。下图展示了MySQL的逻辑架构图。
MySQL逻辑架构整体分为三层,最上层为客户端层,并非MySQL所独有,诸如:连接处理、授权认证、安全等功能均在这一层处理。
MySQL大多数核心服务均在中间这一层,包括查询解析、分析、优化、缓存、内置函数(比如:时间、数学、加密等函数)。所有的跨存储引擎的功能也在这一层实现:存储过程、触发器、视图等。
最下层为存储引擎,其负责MySQL中的数据存储和提取。和Linux下的文件系统类似,每种存储引擎都有其优势和劣势。中间的服务层通过API与存储引擎通信,这些API接口屏蔽了不同存储引擎间的差异。
每一个客户端发起一个新的请求都由服务器端的连接/线程处理工具负责接收客户端的请求并开辟一个新的内存空间,在服务器端的内存中生成一个新的线程,当每一个用户连接到服务器端的时候就会在进程地址空间里生成一个新的线程用于响应客户端请求,用户发起的查询请求都在线程空间内运行, 结果也在这里面缓存并返回给服务器端。线程的重用和销毁都是由连接/线程处理管理器实现的。
综上所述:用户发起请求,连接/线程处理器开辟内存空间,开始提供查询的机制。
三、MySQL查询过程
当向MySQL发送一个请求的时候,MySQL到底做了些什么呢?下图展示了MySQL的查询过程。
3.1客户端/服务端通信协议
MySQL客户端/服务端通信协议是“半双工”的:在任一时刻,要么是服务器向客户端发送数据,要么是客户端向服务器发送数据,这两个动作不能同时发生。一旦一端开始发送消息,另一端要接收完整个消息才能响应它,所以我们无法也无须将一个消息切成小块独立发送,也没有办法进行流量控制。
客户端用一个单独的数据包将查询请求发送给服务器,所以当查询语句很长的时候,需要设置max_allowed_packet参数。但是需要注意的是,如果查询实在是太大,服务端会拒绝接收更多数据并抛出异常。
与之相反的是,服务器响应给用户的数据通常会很多,由多个数据包组成。但是当服务器响应客户端请求时,客户端必须完整的接收整个返回结果,而不能简单的只取前面几条结果,然后让服务器停止发送。因而在实际开发中,尽量保持查询简单且只返回必需的数据,减小通信间数据包的大小和数量是一个非常好的习惯,这也是查询中尽量避免使用SELECT *以及加上LIMIT限制的原因之一。
3.2查询缓存
在解析一个查询语句前,如果查询缓存是打开的,那么MySQL会检查这个查询语句是否命中查询缓存中的数据。如果当前查询恰好命中查询缓存,在检查一次用户权限后直接返回缓存中的结果。这种情况下,查询不会被解析,也不会生成执行计划,更不会执行。
MySQL将缓存存放在一个引用表(不要理解成table,可以认为是类似于HashMap的数据结构),通过一个哈希值索引,这个哈希值通过查询本身、当前要查询的数据库、客户端协议版本号等一些可能影响结果的信息计算得来。所以两个查询在任何字符上的不同(例如:空格、注释),都会导致缓存不会命中。
如果查询中包含任何用户自定义函数、存储函数、用户变量、临时表、mysql库中的系统表,其查询结果都不会被缓存。比如函数NOW()或者CURRENT_DATE()会因为不同的查询时间,返回不同的查询结果,再比如包含CURRENT_USER或者CONNECION_ID()的查询语句会因为不同的用户而返回不同的结果,将这样的查询结果缓存起来没有任何的意义。
既然是缓存,就会失效,那查询缓存何时失效呢?MySQL的查询缓存系统会跟踪查询中涉及的每个表,如果这些表(数据或结构)发生变化,那么和这张表相关的所有缓存数据都将失效。正因为如此,在任何的写操作时,MySQL必须将对应表的所有缓存都设置为失效。如果查询缓存非常大或者碎片很多,这个操作就可能带来很大的系统消耗,甚至导致系统僵死一会儿。而且查询缓存对系统的额外消耗也不仅仅在写操作,读操作也不例外:
1. 任何的查询语句在开始之前都必须经过检查,即使这条SQL语句永远不会命中缓存
2. 如果查询结果可以被缓存,那么执行完成后,会将结果存入缓存,也会带来额外的系统消耗
基于此,我们要知道并不是什么情况下查询缓存都会提高系统性能,缓存和失效都会带来额外消耗,只有当缓存带来的资源节约大于其本身消耗的资源时,才会给系统带来性能提升。如果系统确实存在一些性能问题,可以尝试打开查询缓存,并在数据库设计上做一些优化,比如:
1. 多个小表代替一个大表,注意不要过度设计
2. 批量插入代替循环单条插入
3. 合理控制缓存空间大小,一般来说其大小设置为几十兆比较合适
4. 可以通过SQL_CACHE和SQL_NO_CACHE来控制某个查询语句是否需要进行缓存
最后的忠告是不要轻易打开查询缓存,特别是写密集型应用。如果你实在是忍不住,可以将query_cache_type设置为DEMAND,这时只有加入SQL_CACHE的查询才会走缓存,其他查询则不会,这样可以非常自由地控制哪些查询需要被缓存。
3.3语法解析和预处理
MySQL通过关键字将SQL语句进行解析,并生成一颗对应的解析树。这个过程解析器主要通过语法规则来验证和解析。比如SQL中是否使用了错误的关键字或者关键字的顺序是否正确等等。预处理则会根据MySQL规则进一步检查解析树是否合法。比如检查要查询的数据表和数据列是否存在等等。
3.4查询优化
经过前面的步骤生成的语法树被认为是合法的了,并且由优化器将其转化成查询计划。多数情况下,一条查询可以有很多种执行方式,最后都返回相应的结果。优化器的作用就是找到这其中最好的执行计划。
MySQL使用基于成本的优化器,它尝试预测一个查询使用某种执行计划时的成本,并选择其中成本最小的一个。在MySQL可以通过查询当前会话的last_query_cost的值来得到其计算当前查询的成本。
示例中的结果表示优化器认为大概需要做6391个数据页的随机查找才能完成上面的查询。这个结果是根据一些列的统计信息计算得来的,这些统计信息包括:每张表或者索引的页面个数、索引的基数、索引和数据行的长度、索引的分布情况等等。
有非常多的原因会导致MySQL选择错误的执行计划,比如统计信息不准确、不会考虑不受其控制的操作成本(用户自定义函数、存储过程)、MySQL认为的最优跟我们想的不一样(我们希望执行时间尽可能短,但MySQL值选择它认为成本小的,但成本小并不意味着执行时间短)等等。
有非常多的原因会导致MySQL选择错误的执行计划,比如统计信息不准确、不会考虑不受其控制的操作成本(用户自定义函数、存储过程)、MySQL认为的最优跟我们想的最优并不一样(我们希望执行时间尽可能短,但MySQL值选择它认为成本小的,但成本小并不意味着执行时间短)等等。
MySQL的查询优化器是一个非常复杂的部件,它使用了非常多的优化策略来生成一个最优的执行计划:
1. 重新定义表的关联顺序(多张表关联查询时,并不一定按照SQL中指定的顺序进行,但有一些技巧可以指定关联顺序)
2. 优化MIN()和MAX()函数(找某列的最小值,如果该列有索引,只需要查找B+Tree索引最左端,反之则可以找到最大值,具体原理见下文)
3. 提前终止查询(比如:使用Limit时,查找到满足数量的结果集后会立即终止查询)
4. 优化排序(在老版本MySQL会使用两次传输排序,即先读取行指针和需要排序的字段在内存中对其排序,然后再根据排序结果去读取数据行,而新版本采用的是单次传输排序,也就是一次读取所有的数据行,然后根据给定的列排序。对于I/O密集型应用,效率会高很多)
四、什么是存储引擎
数据库存储引擎是数据库底层软件组件,数据库管理系统使用数据引擎进行创建、查询、更新和删除数据操作。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎还可以获得特定的功能。现在许多数据库管理系统都支持多种不同的存储引擎。MySQL 的核心就是存储引擎。
4.1ISAM(Indexed Sequential Access Method)
ISAM 是一个定义明确且历经时间考验的数据表格管理方法,它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。因此,ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM 的两个主要不足之处在于,它不支持事务处理,也不能够
容错。如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把 ISAM 用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,MYSQL 能够支持这样的备份应用程序。
注意: 使用 ISAM 注意点:必须经常备份所有实时数据。
4.2MyISAM
MyISAM 是 MySQL 的 ISAM 扩展格式(MySQL5.5 之前版本的缺省数据库引擎)数据库引擎。除了提供 ISAM 里所没有的索引和字段管理的大量功能,MyISAM 还使用一种表格锁定的机制,来优化多个并发的读写操作,其代价是你需要经常运行 OPTIMIZE TABLE 命令,来恢复被更新机制所浪费的空间。MyISAM 还有一些有用的扩展,例如用来修复数据库文件的 MyISAMCHK 工具和用来恢复浪费空间的 MyISAMPACK 工具。MYISAM 强调了快速读取操作,这可能就是为什么 MySQL 受到了 WEB 开发如此青睐的主要原因:在 WEB 开发中你所进行的大量数据操作都是读取操作。所以,大多数虚拟主机提供商和 INTERNET 平台提供商只允许使用 MYISAM 格式MyISAM 格式的一个重要缺陷就是不能在表损坏后恢复数据 。MyISAM 引擎使用注意:必须经常使用 Optimize Table 命令清理空间;必须经常备份所有实时数据。工具有用来修复数据库文件的 MyISAMCHK 工具和用来恢复浪费空间的MyISAMPACK 工具。不支持事务。数据越多,写操作效率越低。 因为要维护数据和索引信息 。(索引列越多,相对效率月底。 。 )
如果使用该数据库引擎,会生成三个文件:
.frm:表结构信息
.MYD:数据文件
.MYI:表的索引信息
4.3InnoDB
InnoDB 数据库引擎都是造就 MySQL 灵活性的技术的直接产品,这项技术就是 MYSQL++API。在使用 MYSQL 的时候,你所面对的每一个挑战几乎都源于 ISAM 和 MyISAM 数据库引擎不支持事务处理(transaction process)也不支持外键。尽管要比 ISAM 和 MyISAM 引擎慢很多,但是 InnoDB 包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。是现在的 MySQL(5.5 以上版本)常用版本默认引擎MySQL 官方对 InnoDB 是这样解释的:InnoDB 给 MySQL 提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID 兼容)存储引擎。InnoDB 锁定在行级并且也在 SELECT 语句提供一个 Oracle 风格一致的非锁定读,这些特色增加了多用户部署的性能。没有在 InnoDB 中扩大锁定的需要,因为在 InnoDB 中行级锁定适合非常小的空间。InnoDB 也支持 FOREIGN KEY强制。在 SQL 查询中,你可以自由地将 InnoDB 类型的表与其它 MySQL 的表的类型混合起来,甚至在同一个查询中也可以混合。
InnoDB 是为处理巨大数据量时的最大性能设计,它的 CPU 效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。InnoDB 存储引擎被完全与 MySQL 服务器整合,InnoDB 存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB 存储它的表&索引在一个表空间中,表空间可以包含数个文件(或原始磁盘分区)。这与 MyISAM 表不同,比如在 MyISAM 表中每个表被存在分离的文件中。InnoDB 表可以是任何尺寸,即使在文件尺寸被限制为 2GB 的操作系统上。在 MySQL5.7 版本中,InnoDB 存储引擎管理的数据文件为两个:分别是 frm,idb 文件。
InnoDB 特点:
- 支持事务
- 数据多版本读取( InnoDB+MyISAM+ISAM )
- 锁定机制的改进
- 实现外键
4.4innodb 与 myisam 区别
1. InnoDB 支持事务,MyISAM 不支持,对于 InnoDB 每一条 SQL 语言都默认封装成事务,自动提交,这样会影响速度,所以最好把多条 SQL 语言放在 begin transaction 和 commit 之间,组成一个事务;
2. InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;
3. InnoDB 是聚集索引,数据文件是和索引绑在一起的,必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。
4. InnoDB 不保存表的具体行数,执行 select count(*) from table 时需要全表扫描。而MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;
5. Innodb 不支持全文索引,而 MyISAM 支持全文索引,查询效率上 MyISAM 要高;
4.5如何选择
1. 是否要支持事务,如果要请选择 innodb,如果不需要可以考虑 MyISAM
2. 如果表中绝大多数都只是读查询,可以考虑 MyISAM,如果既有读写也挺频繁,请使用 InnoDB。
3. 系统崩溃后,MyISAM 恢复起来更困难,能否接受;
4. MySQL5.5 版本开始 Innodb 已经成为 Mysql 的默认引擎(之前是 MyISAM),说明其优势是有目共睹的,如果你不知道用什么,那就用 InnoDB,至少不会差。
4.6Memory 存储引擎
Memory 存储引擎,通过名字就很容易让人知道,他是一个将数据存储在内存中的存储引擎。Memory 存储引擎不会将任何数据存放到磁盘上,仅仅存放了一个表结构相关信息的.frm 文件在磁盘上面。所以一旦 MySQLCrash 或者主机 Crash 之后,Memory 的表就只剩下一个结构了。Memory 表支持索引,并且同时支持 Hash 和 B-Tree 两种格式的索引。由于是存放在内存中,所以 Memory 都是按照定长的空间来存储数据的,而且不支持 BLOB 和 TEXT类型的字段。Memory 存储引擎实现页级锁定。
4.7NDBCluster 存储引擎
NDB 存储引擎也叫 NDBCluster 存储引擎,主要用于 MySQLCluster 分布式集群环境, Cluster是 MySQL 从 5.0 版本才开始提供的新功能。
4.8Merge 存储引擎
MERGE 存储引擎,在 MySQL 用户手册中也提到了,也被大家认识为 MRG_MyISAM 引擎。Why ?因为 MERGE 存储引擎可以简单的理解为其功能就是实现了对结构相同的 MyISAM 表,通过一些特殊的包装对外提供一个单一的访问入口,以达到减小应用的复杂度的目的。要创建 MERGE 表,不仅仅基表的结构要完全一致,包括字段的顺序,基表的索引也必须完全一
致。
4.9BDB 存储引擎
BDB 存储引擎全称为 BerkeleyDB 存储引擎,和 Innodb 一样,也不是 MySQL 自己开发实现的一个存储引擎,而是由 SleepycatSoftware 所提供,当然,也是开源存储引擎,同样支持事务安全。
4.10FEDERATED 存储引擎
FEDERATED 存储引擎所实现的功能,和 Oracle 的 DBLINK 基本相似,主要用来提供对远程 MySQL 服务器上面的数据的访问接口。如果我们使用源码编译来安装 MySQL ,那么必须手工指定启用 FEDERATED 存储引擎才行,因为 MySQL 默认是不起用该存储引擎的。
4.11ARCHIVE 存储引擎
ARCHIVE 存储引擎主要用于通过较小的存储空间来存放过期的很少访问的历史数据。ARCHIVE 表不支持索引,通过一个 .frm 的结构定义文件,一个 .ARZ 的数据压缩文件还有一个 .ARM 的 meta 信息文件。由于其所存放的数据的特殊性, ARCHIVE 表不支持删除,修改操作,仅支持插入和查询操作。锁定机制为行级锁定。
4.12BLACKHOLE 存储引擎
BLACKHOLE 存储引擎是一个非常有意思的存储引擎,功能恰如其名,就是一个“黑洞”。就像我们 unix 系统下面的“ /dev/null ”设备一样,不管我们写入任何信息,都是有去无回。
4.13CSV 存储引擎
CSV 存储引擎实际上操作的就是一个标准的 CSV 文件,他不支持索引。起主要用途就是大家有些时候可能会需要通过数据库中的数据导出成一份报表文件,而 CSV 文件是很多软件都支持的一种较为标准的格式,所以我们可以通过先在数据库中建立一张 CSV 表,然后将生成的报表信息插入到该表,即可得到一份 CSV 报表文件了。
五、存储引擎管理
5.1查看数据库支持的存储引擎
show engines
5.2 查看数据库当前使用的存储引擎
就是默认引擎是什么。
show variables like '%storage_engine%'也可以在 MySQL 配置文件中查看。 windows - my.ini。 Linux - my.cnf
5.3 查看数据库表所用的存储引擎
show create table table_name
5.4 创建表指定存储引擎
create table table_name (column_name column_type) engine = engine_name
5.5 修改表的存储引擎
alter table table_name engine=engine_name
5.6 修改默认的存储引擎
在 MySQL 配置文件中修改下述内容:
default-storage-engine=INNODB
MySQL 配置文件:
windows 系统 - MySQL 安装目录/my.ini (5.7 版本 my.ini 文件在数据目录中。C:/programdata/MySQL Server 5.7/mysql/)
linux 系统 - /etc/my.cnf
六、总结存储引擎的选择
不同的存储引擎都有各自的特点,以适应不同的需求,如下表所示:
- 如果要提供提交、回滚、崩溃恢复能力的事物安全(ACID兼容)能力,并要求实现并发控制,InnoDB是一个好的选择
- 如果数据表主要用来插入和查询记录,则MyISAM引擎能提供较高的处理效率
- 如果只是临时存放数据,数据量不大,并且不需要较高的数据安全性,可以选择将数据保存在内存中的Memory引擎,MySQL中使用该引擎作为临时表,存放查询的中间结果
- 如果只有INSERT和SELECT操作,可以选择Archive,Archive支持高并发的插入操作,但是本身不是事务安全的。Archive非常适合存储归档数据,如记录日志信息可以使用Archive
- 使用哪一种引擎需要灵活选择,一个数据库中多个表可以使用不同引擎以满足各种性能和实际需求,使用合适的存储引擎,将会提高整个数据库的性能