一、存储引擎
存储引擎,MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善你的应用的整体功能。InnoDB存储引擎是5.5版本后Mysql的默认数据库,事务型数据库的首选引擎,支持ACID事务,支持行级锁定。另外还有常见的MyISAM存储引擎,它拥有较高的插入,查询速度,但不支持事务。所以,很明显:插入不频繁,查询非常频繁,没有事务,用MyISAM;可靠性要求高,表更新频繁,事务多,用InnoDB。
//#查看本机MySQL提供的什么存储引擎
//show ENGINES;
//
//#查看Mysql当前默认的存储引擎
//show variables like '%storage_engine%';
//
//#查看当前表用什么存储引擎(DDL最后)
//show create table idc_work_order_main;
//#修改当前表的存储引擎
//ALTER TABLE idc_work_order_main ENGINE = 'MyISAM'
MySQL官方对InnoDB是这样解释的,InnoDB给MySQL提供了具有提交、回滚和奔溃恢复能力的事务安全存储引擎。InnoDB是为处理巨大数据量时的最大性能设计,它的CPU效率可能是任何其它基于磁盘的关系数据库引擎所不能匹敌的。InnoDB存储引擎被完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。
如果使用innodb存储引擎,我们知道该引擎最主要的特点是transactional和row lock(行级锁)。按理说不会出现表锁才对,但是事实上还是会出现锁表的的情况,也会比较严重,下面主要就是来探讨一下这个问题。查看mysql文档会发现,虽然innodb使用的的row lock(行级锁),但是在处理具有auto increment字段的表的时候,会使用一种特殊的表锁:AUTO-INC。简单来说就是Innodb会在内存里保存一个计数器用来记录 auto_increment的值,当插入数据时,就会用一个表锁来锁住这个计数器,直到插入结束。一条一条插入问题不大,但是如果高并发插入,就会造成 sql阻塞。
解决方案:1.不使用auto increment字段,自己维护主键生成。该方法中选择主键生成策略很重要, 要综合考虑简单和效率问题。假设使用uuid,虽然简单但是会造成该表的主键效率很低(innodb的主键是特殊的index,其他的index会引用主键)。2.升级到最新的5.2版本。
//#MySQL5.1.22版本之前,这种方式的特点就是“表级锁定”,并发性较差
//innodb_autoinc_lock_mode = 0 (“traditional” lock mode:全部使用表锁)
//
//#推荐使用“consecutive”,并发性相对较高,特点是,即保证同一条insert语句中新插入的auto_increment id都是连续的
//innodb_autoinc_lock_mode = 1 (“consecutive” lock mode)
//
//#这种模式是来一个分配一个,而不会锁表,只会锁住分配id的*过程*,和innodb_autoinc_lock_mode = 1的区别在于
//#不会预分配多个,这种方式并发性最高。但是在replication中当binlog_format为statement-based时
//#(简称SBR statement-based replication)存在问题,因为是来一个分配一个,这样当并发执行时,
//#“Bulk inserts”在分配时会同时向其他的INSERT分配,会出现主从不一致(从库执行结果和主库执行结果不一样),因为binlog只会记录开始的 insert id。
//innodb_autoinc_lock_mode = 2 (“interleaved” lock mode:全部使用新方式,不安全,不适合replication)
关于数据的拷贝问题,常用的数据表引擎是MyISAM和
InnoDB。
MyISAM
的数据表的后缀名是.frm
(表结构)、.myd
(数据)和.myi
(索引),其索引和数据是分开的,可以直接拷贝;InnoDB
的数据表的后缀名是.frm
(表结构)和.ibd
(数据),索引和数据都在同个文件ibdata*
,不能直接拷贝,需要先导出再导入。拷贝完之后别忘了重启数据库服务。
既然是存储引擎,那么我们看看这些数据库的存储是什么样的。block是相对于磁盘来讲的,page是相对于内存来讲的。第一幅图是新建一个txt文档,文件写入1,然后再属性中看占用空间的大小,也就是一个block的大小是4k个字节。通过右图的方式可以用来查看内存中页的大小。
磁盘是分block块的,同一表的数据页是以链表的形式串联在一起的,数据库数据按行存在各个block中,并且以block为单位来存取数据。执行一条SQL的时候,从命令解析、确定执行计划、增删改查。这样磁盘的I/O带来了性能问题。如何减少磁盘的I/O次数呢?
// 1.保证读取数据量在合理大小
// 2.保证存取数据能够顺序读取
// 3.减少需要扫描数据占用空间
保证读取数据量在合理大小;保证存取数据能够顺序读取;减少需要扫描数据占用空间。解决措施,就是使用index索引。dense index是稠密索引,也叫全索引。sparse index是稀疏索引。dense indexes是通过对每一个record在磁盘上持久保存一些额外的数据,用于提高查询的效率。Sparse index结合sequential file和dense index file的优点,通过保存部分key K作为它的record,能很好的支持二分查找快速查找record,并且能进一步减少所需的磁盘I/O。
二、索引
索引是帮助MySQL高效获取数据的数据结构。介绍MySQL的索引结构,索引原理,进而学习索引的优化。MySQL的索引结构包括:B-tree索引、Tree索引、哈希索引(Hash)、位图索引(Bitmap)、跳表。
//#查看表的当前索引 执行结果显示(Index_type: BTREE)
//SHOW INDEX FROM idc_work_order_main
命令用于查看我们的数据库中表的当前索引,执行结果显示当前表结构使用功能的索引是BTREE。通常我们通过如下方式给表建索引:
//#查询表当前使用的索引(表的主键自动建立唯一索引unique index)
//SHOW INDEX FROM idc_work_order_main;
//
//#创建索引index
//CREATE INDEX aaa ON idc_work_order_main(remark)
//DROP INDEX aaa ON idc_work_order_main
//
//#创建唯一索引unique INDEX(唯一的索引意味着两个行不能拥有相同的索引值,否则创建失败)
//CREATE UNIQUE INDEX aaa ON idc_work_order_main(id)
//DROP INDEX aaa ON idc_work_order_main
//
//#创建组合索引
//CREATE INDEX aaa ON idc_work_order_main(id,remark)
//DROP INDEX aaa ON idc_work_order_main
我们知道索引并不是随便乱建的,在考虑是否建索引时,我们一般考虑如下的一些情况:
1.表记录太少。如果一个表只有5条记录,采用索引去访问记录的话,那首先需访问索引表,再通过索引表访问数据表,一般索引表与数据表不在同一个数据块,这种情况下至少要往返读取数据块两次。而不用索引的情况下ORACLE会将所有的数据一次读出,处理速度显然会比用索引快。
2.经常插入、删除、修改的表。对一些经常处理的业务表应在查询允许的情况下尽量减少索引。
3.数据重复,且分布平均的表字段。假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。
建立索引,一般是在对针对百万级以上的数据才建立索引的,以期来提高性能。在创建索引时,首先要考虑表空间和磁盘空间是否足够。我们知道索引也是一种数据,在建立索引的时候势必也会占用大量表空间。因此在对一大表建立索引的时候首先应当考虑的是空间容量问题。其次,在对建立索引的时候要对表进行加锁,因此应当注意操作在业务空闲的时候进行。其次考虑因素便是磁盘I/O。物理上应当尽量把索引与数据分散到不同的磁盘上。逻辑上,数据表空间与索引表空间分开。这是在建索引时应当遵守的基本准则。
//一般情况下不鼓励使用like操作,如果非使用不可,如何使用也是一个问题。like “%aaa%” 不会使用索引而like “aaa%”可以使用索引。
//NOT IN和操作都不会使用索引将进行全表扫描。NOT IN可以NOT EXISTS代替,id3则可使用id>3 or id
索引结构,当前Mysql版本只有BTree和Hash两种索引类型,默认为BTree。Oracle或其他类型数据库中会有Bitmap索引(位图索引)。下面将主要介绍B树索引、哈希索引、位图索引这三种索引结构。
B树(Blance Tree)索引,数据结构原型是多路搜索树,它是一种常见的数据结构,常用做数据库的索引。使用BTree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。
// Blance Tree索引不适合的场景:
// 1.单列索引的列不能包含null的记录,复合索引的各个列不能包含同时为null的记录,否则会全表扫描;
// 2.不适合键值较少的列(重复数据较多的列,is_deleted "y" "n");
// 3.前导模糊查询不能利用索引(like '%XX'或者like '%XX%')
图片展示了B树索引,在插入和删除的时候,对Blance Tree的影响。
哈希索引(Hash),Hash散列索引是根据HASH算法来构建的索引。虽然 Hash 索引效率高,但是 Hash 索引本身由于其特殊性也带来了很多限制和弊端,主要有以下这些:精确查找非常快(包括= <> 和in),其检索效率非常高,索引的检索可以一次定位,不像BTree 索引需要从根节点到枝节点,所以 Hash 索引的查询效率要远高于 B-Tree 索引。
// Hash 索引不适合的场景:
// 1.不适合模糊查询和范围查询(包括like,>,<,between……and等),由于 Hash索引比较的是进行 Hash运算之后的 Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤
// 因为经过相应的 Hash 算法处理之后的 Hash值的大小关系,并不能保证和Hash运算前完全一样;
// 2.不适合排序,数据库无法利用索引的数据来提升排序性能,同样是因为Hash值的大小不确定;
// 3.复合索引不能利用部分索引字段查询,Hash索引在计算 Hash值的时候是组合索引键合并后再一起计算Hash值,而不是单独计算Hash值,
// 所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。
// 4.同样不适合键值较少的列(重复值较多的列)
位图索引(Bitmap),就是用位图表示的索引,对列的每个键值建立一个位图。相对于BTree索引,占用的空间非常小,创建和使用非常快。位图索引由于只存储键值的起止Rowid和位图,占用的空间非常少。
如test表中有state这样一列,10行数据如下:
10 20 30 20 10 30 10 30 20 30
那么会建立三个位图,如下:
BLOCK1 KEY=10 1 0 0 0 1 0 1 0 0 0
BLOCK2 KEY=20 1 0 0 0 1 0 1 0 0 0
BLOCK3 KEY=30 1 0 0 0 1 0 1 0 0 0
//位图索引适合场景:
//1.适合决策支持系统;
//2.当select count(XX) 时,可以直接访问索引中一个位图就快速得出统计数据;
//3.当根据键值做and,or或 in(x,y,..)查询时,直接用索引的位图进行或运算,快速得出结果行数据。
//位图索引不适合场景:
//1.不适合键值较多的列(重复值较少的列);
//2.不适合update、insert、delete频繁的列,代价很高。
跳表,利用了链表的结构图。一个节点存储了下下一个节点的信息,让性能提升了一倍。
三、MySQL优化
1.limit start, count分页的优化,limit语句的查询时间与起始位置(start)的位置成正比。建议加索引,利用MySQL提供的索引缓存,不要直接去找数据地址,而是去索引地址先去查索引。
2.表的数据类型,能小就小,能用char(1)就不用varchar。避免使用null,count(列)不统计值为null的行数,且不利于索引。
3.char是固定大小,varchar可以动态存储数据。优先用tinyint、smallint,再用int、bigint。
4.在存储相同数值范围的数据时,浮点数类型float通常都会比decimal类型使用更少的空间。float字段使用4字节存储数据。double类型需要8个字节并拥有更高的精确度和更大的数值范围,decimal类型的数据将会转换成double类型。
附录:
1、全文索引,全文索引技术是目前搜索引擎的关键技术。试想在1M大小的文件中搜索一个词,可能需要几秒,在100M的文件中可能需要几十秒,如果在更大的文件中搜索那么就需要更大的系统开销,这样的开销是不现实的所以在这样的矛盾下出现了全文索引技术。InnoDB不支持,Myisam支持性能比较好,一般在 CHAR、VARCHAR 或 TEXT 列上创建。
2.聚集索引,该索引中键值的逻辑顺序决定了表中相应行的物理顺序。 聚集索引确定表中数据的物理顺序。Mysql中myisam表是没有聚集索引的,innodb有(主键就是聚集索引),聚集索引在下面介绍innodb结构的时有详细介绍。