高性能mysql读书笔记之二 – 架构优化和索引

第三章 架构优化和索引

第三章的主要是说合理使用不一样的数据类型和索引。主要须要注意的内容有以下: mysql

1. 通用原则 sql

1.1. 数据类型更小一般更好。 数据库

1.2. 数据类型越简单越好 缓存

1.3. 尽可能避免使用NULL,要是有必要用NULL,那也可考虑使用0来进行代替。 建立表的时候定义好not null default 0。 服务器

1.4. DATETIME和TIMESTAMP都能保持一样数据类型:日期和时间,精度为妙。并且TIMESTAMP使用的空间只是DATETIME的一半,TIMESTAMP使用4个字节,DATETIME使用8个字节。并且TIMESTAMP还能保存时区,拥有特殊的自动更新能力,可是TIMESTAMP的范围要比DATETIME要小的多。TIMESTAMP类型只能保存1970年1月1日零点到2038年。而DATETIME却能保存1001年到9999年。 架构

2. VARCHAR和CHAR 并发

2.1. 你们都知道VARCHAR是可变长度的,而CHAR是定长的。 数据库设计

2.2. 使用VARCHAR(5)和VARCHAR(200)保存’hello’占用的空间都是同样的。可是VARCHAR(5)只会使用较小的内存空间,由于MySQL一般会分配固定大小的内存块来保存值。 函数

3. 索引对于查询的性能影响是很是大的。下面先介绍下索引类型。 布局

3.1. B-TREE索引,大部分MySQL存储引擎都支持B-TREE。除了ARCHIVE直到5.1才支持。

3.1.1. 存储引擎使用了不一样的方式把索引保存在磁盘上,它们会影响必定的性能。例如MyISAM使用前缀压缩以减小索引,而InnoDB不会压缩索引,由于它不能把压缩索引用于某些优化。

3.1.2. B-TREE一般意味着存储是有序的。

3.1.3. B-TREE索引可以很好的用户全键值,键值范围或者键前缀进行查找(最左前缀)。

3.1.3.1. 匹配全名,如 where name=’timo’

3.1.3.2. 匹配最左前缀, 如 where name like ‘tim%’

3.1.3.3. 匹配范围值,如 where name between ‘tim’ and ‘timo’

3.1.3.4. 精确匹配一部分而且匹配某个范围内的另外一部分,如 where name=’timo’ and age between 25 and 30

3.1.3.5. B-TREE索引一般能支持至访问索引的查询,它不会访问数据行

3.1.4. B-TREE索引的局限性

3.1.4.1. 假设有以下的索引 key(first_name, last_name, age)

3.1.4.2. 若是查找没有从索引的最左边开始,它就没有什么用处。好比where first_name like ‘%mo’ 这样的查找是不走索引的。

3.1.4.3. 不能跳过索引中的列, 如查找 where first_name=’timo’ and age = 25, 若是创建的是上面这样的联合索引,又跨了last_name,那就不会走索引了。

3.1.4.4. 存储引擎不能优化访问任何在一个范围条件右边的列。如查找

where first_name=’timo’ and last_name like ‘s%’ and age=25。访问就只能使用索引的前2列,由于like是范围条件。

3.1.4.5. 一些局限并非B-TREE固有的,而是MySQL查询优化器和存储引擎使用索引的方式形成的。

3.2. 哈希索引(hash index)

3.2.1. 它值对使用索引中的每一列的精确查找有用。因此不多用,在MySQL中是有Memory存储引擎支持显式的哈希索引。

3.2.2. 因为hash index是给每一个键值创建一个哈希表,因此它的查找速度是很是快的,可是也会有不少局限性。

3.2.2.1. 由于索引只包含了哈希码和行指针,而不是指自己,MySQL不能使用索引中的值来避免读取行。

3.2.2.2. 不能进行排序

3.2.2.3. 不支持部分键匹配

3.2.2.4. 只支持使用 =, in() 和<=>的相等比较。

3.2.2.5. 发生碰撞的时候存储引擎必须访问链表中的每一个指针,而后逐行进行数据比较,以肯定正确的数据。

3.2.2.6. 若是有不少碰撞,一些索引维护操做就会很慢。

3.3. 空间(R-TREE)索引

3.3.1. 只有MyISAM支持,可使用GEOMETRY这样的地理空间数据类型,必须使用MySQL GIS函数进行查找。

3.4. 全文索引

3.4.1. 全文索引只有MyISAM支持。是从文本中直接找关键字,而不是从索引中进行比较。全文索引用户MATCH AGAINST操做。

3.5. 高性能索引策略

3.5.1. 隔离列

3.5.1.1. 下面2条语句是不会使用索引的

1 where actor_id + 1 = 5
2  
3 where TO_DAYS(CURRENT_DATE) - TO_DAYS(date_col) <= 10

3.5.1.2. 下面2条是针对上面2条进行修改使用索引的

1 where actor_id = 4
2  
3 where date_col >= DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)

3.5.2. 前缀索引和索引选择性

3.5.2.1. 当某一列特别长的状况下,若是给所有长度建索引,那样会增长索引的大小,而只作很短的前缀索引,虽然节约了空间,可是会增长选择性。因此建前缀索引必须让选择性接近于所有长度的选择性。

3.5.2.2. 平均来讲前缀的选择性能接近于0.31就能够了。

1 select count(distinct city)/count(*) from db_name.table_name; selectcount(distinct left(city, 3))/count(*) AS sel3, count(distinct left(city, 4))/count(*) AS sel4 from db_name.table_name;

3.5.2.3. 只看平均选择率在特殊状况是不够的,好比在数据分布很是不均的状况下。

3.5.2.4. Alter table db_name.table_name add KEY (city(7)) 这句就是只对city这一列的前7个字母进行索引。

3.5.3. 汇集索引(clustered indexes)

3.5.3.1. 在InnoDB中汇集索引实际上在一样的结构中保存了B-TREE索引和数据行。汇集的含义就是指实际的数据行和相关的键值保存在一块儿。每一个表只能有一个汇集索引,由于不可能一次把行保存在两个地方

3.5.3.2. 在MySQL中只有SolidDB和InnoDB是支持汇集索引的。

3.5.3.3. InnoDB是按照主键(Primary Key)列进行汇集。若是没有定义主键,InnoDB会试着使用惟一的非空索引来代替。

3.5.3.4. 汇集索引有助于性能,可是也会致使严重的性能问题。总的来讲它有以下的优势:因为把索引和数据都保存在一棵B-TREE中,所以查找数据会比一般的要快。

3.5.3.5. 汇集索引也有以下的缺点:会致使I/O密集,插入速度慢,更新索引列慢,插入新行会进行分页,这样致使占用更多的磁盘空间。第二索引会比预想的大,第二索引访问须要两次索引查找。

3.5.3.6. 在InnoDB中是根据主键来进行顺序插入的(这个跟InnoDB的数据布局有关),因此主键最好是一个自增的值,与应用程序无关。

3.5.4. 覆盖索引(covering indexes)

3.5.4.1. 包含(或者覆盖)了全部知足查询须要的数据的索引叫覆盖索引。

3.5.4.2. 索引记录一般远小于全行大小,所以只读取索引就能极大的减小数据访问量(这个跟汇集索引的优势同样)

3.5.4.3. 索引是按照索引值进行排序的。

3.5.4.4. 大部分存储引擎缓存索引比缓存数据更好(除了Falcon)。

3.5.4.5. 对于InnoDB覆盖了查询的第二索引在主键中避免了另一次索引查找。

3.5.4.6. 覆盖索引必须保存它包含列的数据。

3.5.4.7. 当发起一个索引覆盖的查询,用EXPLAIN会在extra列显示Using Index

3.5.5. 为排序使用索引扫描

3.5.5.1. MySQL有两种产生排序结果的方式,使用文件排序(Filesort)和扫描有序的索引。若是EXPLAIN的输入type列的值是index。那说明MySQL会扫描索引。

3.5.5.2. 只有当索引的顺序和order by字句中的顺序彻底一致,而且全部列排序的方向(升序或降序)同样才能够。若是查询链接多个表,只有在order by 字句的全部列引用的是第一个表才能够。

3.5.5.3. 假设有以下表:

01 CREATE TABLE rental (
02  
03 …………
04  
05 PRIMARY KEY (rental_id),
06  
07 UNIQUE KEY rental_date (rental_date, inventory_id, customer_id),
08  
09 KEY idx_fx_inventory_id (inventory_id),
10  
11 KEY idx_fx_customer_id (customer_id),
12  
13 KEY idx_fx_staff_id (staff_id),
14  
15 …………
16  
17 )

3.5.5.3.1. 下面的这几个语句是使用到索引的。

1 WHERE rental_date='2010-05-02' ORDER BY inventory_id desc;
2  
3 WHERE rental_date > '2010-05-02' ORDER BY rental_date, inventory_id; 在where字句是范围的时候须要用最左前缀索引进行排序。

3.5.5.3.2. 而下面的都是不能使用索引进行排序的。

1 ... WHERE rental_date = '2005-05-25' ORDER BY inventory_id, staff_id;
2  
3 ... WHERE rental_date = '2005-05-25' ORDER BY customer_id;
4  
5 ... WHERE rental_date > '2005-05-25' ORDER BY inventory_id, customer_id; 缘由是第一句里用到staff_id不在索引的列中,第二句是没能造成索引的最左前缀,第三句是因为在索引的第一列有范围条件那MySQL就不会使用余下的索引。还有多个where等于条件也不会使用索引排序。

3.5.6. 压缩(前缀压缩)索引

3.5.6.1. 这个只在MyISAM存储引擎才有效

3.5.6.2. 压缩完成会块的占用空间会较小,可是某些操做会变慢。排序若是是顺序没有问题,可是倒序会很是的慢。

3.5.6.3. CPU的负载会比日常多几倍。

3.5.6.4. 压缩后索引大小也许只有原来的1/10

3.5.6.5. 能够在使用CREATE TABLE命令的时候用PACK_KEYS来控制索引压缩的方式。

3.5.7. 多余和重复索引

3.5.7.1. 之前我一直以为多建索引只是多占用系统的磁盘大小,而看了这里感受本身错了,仍是须要注意不能随便建索引。

3.5.7.2. MySQL自己不会提醒你建立了重复的索引。这样MySQL不得不单独维护每个索引,而且查询优化器在优化查询的时候会逐个考虑它们,这样就会严重影响性能。

3.5.7.3. 看下面这个例子: CREATE TABLE test (ID INT NOT NULL PRIMARY KEY, UNIQUE(ID) INDEX(ID)); 我本身之前也有过这样的写法,其实这样对于ID这一列建立了3个相同的索引。事实上MySQL利用索引实现了UNIQUE约束和PRIMARY KEY约束。因此一般不须要这样作,除非你要在同一列上有不一样的索引知足不一样类型的查询(好比KEY和FULLTEXT KEY)

3.5.7.4. 多余索引。有几个例子,好比创建了(A,B)2列这样的索引,那就没有必要建(A)索引了,可是仍是有必要创建(B)索引。一样创建了(A,B)若是有须要仍是能够创建(B,A)索引的。

3.5.7.5. 同时在大部分状况下,为了不它,应该扩展索已有索引,而不是添加新索引。

3.5.8. 索引和锁定(Indexes and Locking)

3.5.8.1. 索引可让查询锁定更少的行,由于在InnoDB只有在事务提交后才会给行解锁。

3.5.8.2. InnoDB进行行锁定仍是有一些开销的,锁定超过须要的行会增长锁竞争和减小并发。

3.5.8.3. 下面是一个例子

1 Set autocommit=0
2  
3 SELECT actor_id FROM table_name WHERE actor_id < 5 AND actor_id <> 1 FOR UPDATE;

该查询实际以独占的方式锁定了1到4行,InnoDB锁定第一行的缘由是“从索引的开头开始,而且提取全部行直到 actor_id < 5不成立”。若是没有索引,MySQL无论是否须要行,都会进行全表扫描而且锁定每一行(MySQL5.1中 READ COMMITTED事务隔离级别也有这个问题)。

InnoDB能在第二索引上放置共享(读)锁,可是独占(写)锁要求访问主键。这消除了使用覆盖索引的可能性,而且能致使SELECT FOR UPDATE比LOCK IN SHARE MODE或非锁定要慢不少。

3.5.9. 索引策略总结

3.5.9.1. 为任何耗时很长的查询添加索引。

3.5.9.2. 在任何可能的地方,都要试着扩展索引,而不是新增索引。若是不知道查询的分布,就要尽量地使索引变得更有选择性。

3.6. 索引实例研究

3.6.1. 这个主要是一个婚恋网站的维护,比较简单。

3.7. 索引和表维护

3.7.1. 维护的目的是为了减小碎片

3.7.2. 查找并修复表损坏

3.7.2.1. 这个通常MyISAM引擎会因为服务器崩溃致使表损坏,可使用myisamchk来进行修复,修复的方法有不少。至于InnoDB我本身是历来没有碰到过表损坏的问题出现。

3.7.3. 更新索引统计

3.7.3.1. MySQL查询优化器在决定如何使用索引的时候会调用两个API,以了解索引如何分布,一个是调用records_in_range(),它接受范围结束点而且返回范围内记录的数量。第二个info(),它返回不一样类型的数据。

3.7.3.2. 查询优化器的开销指标是查询会访问多少数据。若是统计永远没有产生,或者过期了,优化器就会作出很差的决定。能够运行ANALYZE TABLE来解决这个问题。

3.7.3.3. InnoDB是在第一次打开表的时候利用随机索引进行估计。InnoDB上的ANALYZE TABLE命令就使用了随机索引,一样,ANALYZE TABLE在InnoDB不是阻塞性的,而且开销也不大,所以能够在不大影响服务器的状况下在线更新统计。

3.7.3.4. 咱们可使用SHOW INDEX FROM table_name命令来检查索引的基数性。须要特别注意Cardinality列,它显示了存储引擎估计的索引中惟一值的数量。在MySQL5.0以及以上版本能够经过INFORMATION_SCHEMA.STATISTICS表来获得这些数据。

3.7.4. 减小索引和数据碎片

3.7.4.1. B-TREE索引会有它的造成机制,故会造成碎片并下降了性能。

3.7.4.2. 表的数据存储也能变的碎片化。有两种碎片分别为

3.7.4.2.1. 行碎片(row fragmentation):当行存储在多个地方的多个片断中就会产生这种碎片。

3.7.4.2.2. 内部行碎片(intra-row fragmentation):当逻辑上顺序的页面或行在磁盘上没有被顺序的存储时就会产生这种碎片。它影响了全表扫描和汇集索引这样的操做。

3.7.4.3. 为了消除数据碎片能够运行OPTIMIZE TABLE来转储或者从新加载数据。对于不支持OPTIMIZE TABLE的存储引擎能够用ALTER TABLE从新创建表。

3.8. 范式化和非范式化(Normalization and Denormalization)

3.8.1. 这个属于数据库设计的范畴,第一范式,第二范式,第三范式,BCNF

3.8.2. 范式化架构的利弊:这个基本上就是BCNF的好处,当写入负载大的时候,使用范式化架构师比较好的。它的优缺点以下:

3.8.2.1. 范式化更新比非范式化更新快

3.8.2.2. 当数据被不多的范式化后,就不多或者很是少的重复数据,所以改动的数据会变少

3.8.2.3. 范式化表一般较小,容易被装载到内存中而且性能更好

3.8.2.4. 因为缺乏冗余数据,在取得数据的时候会较少采用DISTINCT或者GROUP BY,这样有时候要查数据会链接多个表。这样就很是难创建索引策略。

3.8.3. 非范式化架构的利弊:非范式化架构因为全部数据都在一个表里面,避免了链接,因此性能不错。因为不须要链接表,对于大多数查询,甚至不使用索引,这会比链接快的多,由于它避免了随机I/0。因此非范式化架构用在查询很是多的状况是很是好的。

3.8.4. 结合范式化和非范式化:在真实环境中不多会所有范式化和非范式化,一般都是结合者2中方案的,也许是采用部分范式化,缓存表以及其它技巧。

3.8.4.1. 非范式化最多见的技巧是复制,缓存,把一个表中部分列选到另一个表中,在MySQL5.0以及以上版本,可使用触发器来更新缓存的值。

3.8.5. 缓存和汇总表

3.8.5.1. 有时需构造彻底不一样的汇总或者缓存表,为获取数据进行特别的调优。

3.8.5.2. 缓存表的含义就是可以容易的得到数据,若是这样更慢的话就会经过scheme来获取数据。缓存表对于优化搜索和获取数据的查询是有用的。

3.8.5.3. 汇总表的意思就是咱们能够经过GROUP BY来汇总查询数据。

3.8.5.4. 当使用缓存和汇总表的时候,你不得不决定是否要进行实施数据维护或周期性重建。不过能够经过使用影子表来解决这个问题。

1 DROP TABLE IF EXISTS my_summary_new, my_summary_old;
2  
3 CREATE TABLE my_summary_new LIKE my_summary;
4  
5 RENAME TABLE my_summary TO my_summary_old, my_summary_new TO my_summary;

3.9. 加速ALTER TABLE

3.9.1. 这个是有技巧的,不是全部的ALTER TABLE操做都会致使重建表。

下面这个例子是想把这一列默认值变为5,可是第一句它实际上会执行1000次读取和1000次插入,这样就会很是慢。而第二句只修改默认值,列的默认值实际保存在表的.frm文件中,因此能够不不接触表而更改它。任何MODIFY COLUMN操做都会致使表重建。

1 ALTER TABLE table_name MODIFY COLUMN column_name TINYINT(3) NOT NULL DEFAULT 5;
2  
3 ALTER TABLE table_name ALTER COLUMN column_name SET DEFAULT 5;

3.9.2. 若是愿意承担必定风险,可让MySQL作其它类型的修改时也不重建表。

3.9.2.1. 如下操做能够不重建表:移除(但不是添加)列的AUTO_INCREMENT属性

3.9.2.2. 添加,移除或更改ENUM和SET常量。

3.9.2.3. 官方说这是不受支持的技巧,使用后果自负。

2010-05-05

——————————————————————————————————————————

架构优化和索引 
  1.数据类型 
  浮点:FLOAT和DOUBLE使用的是平台的浮点数,分别占用4字节和8字节。DECIMAL是表示精确的小数。 
  字符串:VARCHAR(255)表示最大长度255字节,对于uft-8编码,将不知道能保留多少字符,中文是3字节,字母1字节,这一点要注意。另外设高上限,由于内存的分配mysql是固定长度的,这样可能会致使内存的浪费。另外会占用额外的1或2字节来记录长度。该类型的问题是修改字段会改变行的大小,可能引发分页。带来额外的工做。但这个问题不是很大,由于预写式日志的存在。CHAR优点比较小了,对于固定长度的,却是能够用一下,问题是会丢失字符串后面的空格。 
  时间:DATETIME实际上是封装为YYYYMMDDHHMMSS的整数,8字节,至关与bigint,精确到秒 
  TIMESTAMP:从1970年1月1日到如今的秒数,占用4字节 
  BIT和SET,都是位集,要使用的时候研究一下跟使用INT有什么区别。 
  IP地址用无符号整数保存,mysql提供了INET_ATION()和INET_NTOA()实现转化,我这个项目恰好能用上。 
  2.索引 
  大部分存储引擎都是用B-Tree(一个节点不少分支,深度同样,为低速IO和按序查找而优化的)来实现。能够匹配全名,匹配最左前缀(找到第一层小于前缀的风格点和第一个大于前缀的风格点,递归)匹配范围值(找到第一层最大小于下届的和最小大于上界的,递归),like也能够当作一个范围值。好比 like ("123%" > "123" and <"124"). 这三种状况能够变成两种。能够组合好比,Key(a,b,c),索引中也是按顺序存储的, a='A'and b like 'B%',至关于abc like "AB%"能够索引 a='A' and b>"B1" and B<"B2" 至关于abc >"AB1" abc<"AB2" 
like也能够当作一个范围值。对于多列索引,顺序很重要,
若是前一列使用了范围条件,后一列的索引就不能使用了,由于mysql不知道这个范围里包括了多少值,没法组合出索引,因此这个时候In就特别有用,它能够是mysql知道如何组合条件使用索引。 
如何select * form employee where gender>=0 and gender <=1 and birthday="1987-08-19",该表上有索引key(gender,birthday),这种写法会扫描全表, 
而select * form employee where gender in (0,1) and birthday="1987-08-19"就会使用索引。这个其实优化器还可能优化,而若是gender是字符串,就无从优化了。 
  Hash索引不能处理范围检索。能够用索引一个保存hash值的列来优化索引的性能。 
  全文索引:经过分词,创建一个相似B-Tree的结构。 
  3.索引优化: 
  (1).有索引的列要单独出如今表达式的一边,这是最简单的,第二,表达式也最好不要有其余的变量,好比CURRENT_DATE,这个会影响缓存。(这个也许新的版本能优化) 
  (2).前缀索引,把字符串的前面一部分用作索引,Text字段会很须要,要关注选择性和平均性两个指标,语法alert table table_name add key (column_name(N)). 

from:http://zizipo.iteye.com/blog/1012975

http://zizipo.iteye.com/blog/1011641

相关文章
相关标签/搜索