高性能mysql 4,5,6章优化总结

针对数据库的优化,我们不能单纯的说从哪一个方面,需要结合数据表的建立,数据类型的选择,索引的设计和sql语句来考虑,我就针对怎么建表,怎么选择数据类型,如何应用B-tree索引,hash索引和覆盖索引的特点来建立高效的索引策略,然后我具体对 count()查询,最大最小值查询,关联查询,子查询,GROUP BY ,limit 分页,Union查询做一些具体的说明,最后我说一下怎样使用切分查询和分解关联查询来重构我们的查询方式,

一、数据表的设计

首先我们要根据范式化反范式化各自的优缺点,选择一个最佳的设计。

(反范式化设计,一个典型列子就是缓存表,它的特点就是在不同的表中存储相同的列,它很好的避免了关联,但是增加了许多冗余的数据,插入和更新可能会慢一些,而范式化设计的数据表通常比较小很少有冗余的数据插入更新也比反范式化设计的表要快,但是通常需要做关联操作。)

二、数据类型的选择:

我主要说3点

1、选择简单的,不超过范围的最小类型,避免使用 NULL(可为NULL的列使得索引,索引统计和值比较都更复杂,还会使用更多的存储空间,在mysql里也需要特殊处理)

Example:

(1)日期时间类型最好使用 timestamp 而不用datetime存储,datetime使用了8个字节的存储空间,而timestamp 只使用4个字节的存储空间,可以使用 FROM_UNIXTIME() UNIX_TIMESTAMP()进行相互转化。

(2)IP地址是一个 32 位无符号整数,应该 unsigned int 来存储,不应该使用 varchar(15) 来存储,可以使用 INET_NTOA() INET_ATON() 两个函数来相互转化。

2、注意char 和 varchar 的区别,(varchar适合存储最大长度比平均长度大很多和列的更新少,不容易产生碎片的数据,而char适合存储短的,所有值接近同一个长度的数据,对于经常变更的数据也适合用char存储)

3、对于相似或相关的值尽量使用同种数据类型存储,特别是在关联条件中使用的列

三、高效的索引策略

MyISAM,InnoDB和memory存储引擎都支持B-tree索引,memory支持hash索引,InnoDB支持聚簇索引,那么我们就可以根据它们所支持的索引类型和数据的存储结构来设计高效的索引策略。

1、首先我说说 hash 索引,因为hash查找非常快,hash索引是基于哈希表实现,存储引擎会对所有的索引列计算一个哈希码,哈希索引将所有的哈希码存储在索引中,同时在哈希表中保存每个指向数据行的指针。我们就可以利用它的这个特点来创建一个自定义hash索引提高查询效率。

举个列子,假如现在有一个address表,里面存储大量的 url,而且需要根据url进行搜素查找,我需要执行一条这样的查询:

SELECT id FROM address WHERE url=’http://www.mysql.com’;

那么我直接在url上面建立一个索引好不好呢,答案是肯定能提高查询效率,但是由于这个字段太长,直接用它做索引不是最佳选择,我们应该删除url上的索引,再数据表中增加一个字段crc_url,使用CRC32做hash,然后执行下面的查询:

SELECT id FROM address WHERE url=’http://www.mysql.com’ AND url_crc = CRC32(“http://www.mysql.com”);

这样做性能会非常高,因为mysql优化器会使用这个选择性更高而且体积更小的基于url_crc列的索引来完成查找,只需要根据哈希值做快速的整数比较就可找到索引条目,然后返回对应的列,哈希值我们可以用触发器来维护,

CREATE TRIGGER  address_hash_crc_ins BEFORE INSERT ON address FOR EACH ROW BEGIN SET NEW.crc_url = crc32(NEW.url);

CREATE TRIGGER  address_hash_crc_upd BEFORE UPDATE ON address FOR EACH ROW BEGIN SET NEW.crc_url = crc32(NEW.url);

当然InnoDB有一个特殊的功能叫“自适应哈希索引,当InnoDB注意到某些索引使用的非常频繁是就会在内存中基于B-tree索引在创建一个哈希索引,让B-tree索引页具有hash索引的一些优点”。

当然虽然哈hash索引查找速度非常快。但是也有它的局限性由于它不是按照索引值顺序存储的,所以它无法用于排序操作,也不支持部分索引列匹配查找,因为hash索引始终是使用索引列的全部内容来计算hash值的,哈希索引只支持等值比较查询。

2、在说一下InnoDB支持的聚簇索引,聚簇索引的数据实际上是保存在索引的叶子页中,我先说说MyISAM和InnoDB的数据存储结构:

         

举个列子:

假如现在有一个这样的查询:

SELECT * FROM questions WHERE question_userid=1 AND question_title LIKE ‘%PHP试题%’;

当数据量很大的时候,返回的行有比较少时,查询速度回很慢,我们需要重写查询并巧妙的设计索引,我需要先添加一个多列索引 userid_title(question_userid,question_title),我将查询改成这样:

SELECT * FROM questions JOIN (SELECT question_id FROM questions WHERE question_userid=1 AND question_title LIKE ‘%PHP试题%’) AS t1 ON(t1.question_id = questions.question_id);

使用这种延迟关联的查询方式,查询的第一阶段mysql可以使用覆盖索引,然后根据这些question_id值在外层查询匹配获取需要的所有值。

四、创建高效的sql语句,

1、优化count

如果是MyISAM存储引擎,而且没有任何的where条件,我们直接使用count(*),mysql会利用存储引擎的特点直接获取这个值,还有一个方法就是利用反正特性,比如有这样一个查询:

SELECT COUNT(*) FROM questions WHERE id>5;

此时将语句改写成:

SELECT (SELECT COUNT(*) FROM questions) - COUNT(*) FROM questions WHERE id<5;

这样就可以大大减小需要扫描的行数,查询优化器会直接将子查询当做一个常数来处理,而且查询小于5的数据很少很多,如果只是需要一个粗略值的话可以直接使用EXPLAIN 中优化器估算出的一个行数就可以当做这个近似值。如果需要分别查询单选题题,判断题各有多少道,可以这样写:

SELECT COUNT(question_type=1 OR NULL) AS  ‘单选题’, COUNT(question_type=2 OR NULL) AS ‘多选题’ FROM qustions;

2、优化min()

SELECT MIN(question_id) FROM questions WHERE question_userid=1;

改写成:

SELECT question_id FROM questions USE INDEX(PRIMARY) WHERE question_userid=1 LIMIT 1;

这里使用了一个优化器提示 USE INDEX() 来告诉优化器使用主键索引来查询记录,由于数据存储的时候是按照主键值从小到大存储的,那么满足条件的第一条记录一定是最小值。

3、优化关联查询

如果 A 与 B 通过 c 列关联,关联顺序是 B,A,则只需要在A表的c列上建立索引就好,如果在B表上也建索引,那么这种冗余的索引只会带来额外的负担,还要注意它们的关联字段的数据类型尽量用通一种数据类型,如果查询语句中有 ORDER BY 或者 GROUP BY,那么最好只涉及到一个表中的列,这样mysql才有可能使用索引来优化这个过程。

4、优化GROUP BY

当我们需用分组做分组排序的时候,这个时候我们创建索引就应该尽量让它满足既能用于查找行又能用于排序操作,这样就不用创建临时表来做额外的排序操作了,那么优化GROUP BY 最好的方法就是使用松散索引扫描,如果不能满足松散索引扫描的话,也尽量使用紧凑索引扫描,避免使用临时表来排序。

(1)使用松散索引扫描(当mysql完全利用索引扫描来实现GROUP BY的时候,并不需要扫描所有满足条件的索引键就可以完成操作的出结果)

假设有一个表 t1 有4个列 (c1,c2,c3,c4), 给它建立了一个多列索引 (c2,c3,c4);

做类似这样查询 SELECT c2,MAX(c3) FROM t1 GROUP BY c2,c3;

例子:

SELECT question_userid,question_type FROM questions GROUP BY question_userid,question_type;

(2)使用紧凑索引扫描:

SELECT c2,MAX(c3) FROM t1 WHERE c2=const GROUP BY c3,c4;

SELECT question_userid,question_type FROM questions WHERE question_userid=1 GROUP BY question_type;

紧凑索引扫描和松散索引扫描的区别在于,紧凑索引扫描需要在扫描索引的时候,读取所有满足条件的索引键,然后在根据读取的数据来完成GROUP BY 操作,它需要访问WHERE条件中所限定的所有索引键之后才能得出结果。这里的GROUP BY并不是一个连续的索引,但是WHERE 条件中的question_userid 是个常数,弥补了缺失的索引,因此使用紧凑索引扫描。

(3)使用临时表

如果这两种中条件都打不到的话,就只能使用临时表来进行排序了,如果数据量小可以直接在内存里进行,数据量大的话可能还需要借助磁盘来完成,如果我们自己清楚临时表的数据量大小,可以使用优化器提示SQL_SMALL_RESULTSQL_GIG_RESULT 告诉优化器在哪儿排序。如果我们只需要对结果分组而不需要排序,可以加一句 ORDER BY NULL 来避免GROUP BY 默认的按照分组字段进行排序。

5、优化 UNION

除非确实需要去除重复的行,否则一定要使用 UNION ALL , 因为如果没有ALL,mysql会给临时表加上DISTINCT做唯一性检查,这样做代价非常高,

Mysql在做UNION 查询时,无法将限制条件从外层“下推”到内层,例如:

(SELECT c1,c2 FROM t1 ORDER BY c2) UNION ALL (SELECT c3,c4 FROM t2 ORDER BY c4) LIMIT 20;

假设从t1表中查出了500条记录,从t2表中查出了800 条记录,那么mysql会将这1300 条记录放入临时表在取出20条。因为它无法将limit下推到内层,我们可以这样优化一下:

(SELECT c1,c2 FROM t1 ORDER BY c2 LIMIT 20) UNION ALL (SELECT c3,c4 FROM t2 ORDER BY c4 LIMIT 20) LIMIT 20;

这样临时表就只有40条记录了,再取出20条。

6、优化limit 分页

对于limit的优化,最常用的地方就是分页,假设我们有很多页,有例如 limit 1000,20 这样的查询,那么mysql需要查询1020条记录,然后将前面的1000条都扔掉,然后返回最后20条,这样做代价太高,我们可以这样来优化: 尽可能使用索引覆盖扫描,而不是查询所有的列,然后根据需要做一次关联在返回所需的列:

例如:

SELECT question_name,question_type FROM questions ORDER BY question_inserttime LIMIT 1000,20;

可以这样改:

SELECT question_name,question_type INNOR JOIN (SELECT question_id FROM questions ORDER BY question_inserttime LIMIT 1000,20) AS t1 USEING(question_id);

这里使用了延迟关联,先使用索引覆盖扫描快速找到对应的20个question_id,然后做一次关联操作返回所需的列。

7、子查询

对于子查询最好是使用关联查询来代替。

当然除了这些查询语句优化外,还可以重构查询方式,比如切分查询和分解关联查询;

五、重构查询方式

1、切分查询

所谓的切分查询,就是将一个大查询分而治之,将一个大查询切分成小查询,每个查询的功能完全一样,只完成一小部分,每次只完成一小部分查询结果。

例如我们有一个很大的message表,我们需要定期去删除里面的数据,需要执行这样一条sql:

DELETE FROM message WHERE create_time < DATE_SUB(NOW(),INTVAL 1 MONTH);

我们可以将它改成这样:

$row_effect = 0;
do
{
$sql = “DELETE FROM message WHERE create_time<DATE_SUB(NOW(),INTVAL 1 MONTH) LIMIT 10000”;
$row_effect = $db -> execte($sql);
}while $row_effect >0

2、分解关联查询

分解关联查询就是将多表关联查询切分成单个查询,这样做可以让缓存命中率更高,而且执行单个查询可以减少锁的竞争

该文章是我学习了高性能mysql一书后自己做的一个总结,如果有转载请注明出处:http://www.cnblogs.com/chrdai/p/6809369.html

时间: 2024-10-25 14:51:09

高性能mysql 4,5,6章优化总结的相关文章

mysql分解连接的总结(来自于高性能MySQL以及自己网站性能优化)

许多高性能的站点都用了"分解连接"技术,也就是把单个多表连接查询改成多个但表查询,然后在程序中合并数据,比如: select a.*,b.* from A a join B b on a.id = b.id 可以替换为: select a.* from A; select b.* from B; 然后再把数据通过程序合并. 可能有些人认为这太浪费了,把一个查询语句变成两条查询语句或者更多的查询语句了,如果哪位猿类这样想了,那你就应该继续往下看了. 将连接查询重构为多表查询,总体有以下性

《高性能MySQL》第三章MySQL服务器性能剖析学习笔记

MySQL性能优化介绍 什么是性能优化呢?其实我们往往从广义的定义是觉得一个MySQL系统的非功能性的优化都会看作是性能优化,比如我们会将数据库服务器的稳定性.每秒执行的SQL查询数目.系统的可扩展性.cpu利用率等等特性的优化都会看成是MySQL的性能优化. 我个人比较赞同本书的观点是MySQL性能优化应该就是指MySQL的查询响应时间的优化,MySQL性能优化就是将查询响应时间优化到一个客户或者用户体验能够接受的一个程度.

高性能MySQL笔记 第4章 Schema与数据类型优化

4.1 选择优化的数据类型   通用原则 更小的通常更好 前提是要确保没有低估需要存储的值范围:因为它占用更少的磁盘.内存.CPU缓存,并且处理时需要的CPU周期也更少. 简单就好 简单数据类型的操作需要更少的CPU周期. 尽量避免NULL 值可为NULL的列使得索引.索引统计和值比较都更复杂化.可为NULL的列会使用更多的存储空间. 整数类型 TINYINT SMALLINT MEDIUMINT INT BIGINT.分别使用8,16,24,32,64位存储空间.他们可以存储的值的范围从 -2

高性能mysql总结(一 数据类型优化)

选择优化的数据类型 mysql支持的数据类型非常多,选择正确的数据类型对于获取高性能至关重要.不管存储哪种类型的数据,下面几个简单的原则都有助于做粗活更好的选择. 最小的通常更好 一般情况下,应该尽量使用正确存储数据的最小数据类型.最小的数据类型通常更快,因为它们占用更少的磁盘.内存和cpu缓存,并且处理时需要的cpu周期也更少. 但是要确保没有低估需要存储的值的范围,因为在schema中的多个地方增加数据类型的范围是一个非常耗时和痛苦的操作.如果无法确定哪个数据类型是最好的,就选择你认为不会超

高性能MySQL笔记-第4章Optimizing Schema and Data Types

1.Good schema design is pretty universal, but of course MySQL has special implementation details to consider. In a nutshell, it’s a good idea to keep things as small and simple as you can. MySQL likes simplicity, and so will the people who have to wo

高性能MySQL笔记-第5章Indexing for High Performance-005聚集索引

一.聚集索引介绍 1.什么是聚集索引? InnoDB’s clustered indexes actually store a B-Tree index and the rows together in the same structure. 2.为什么一张表只能一个聚集索引? When a table has a clustered index, its rows are actually stored in the index’s leaf pages.The term “clustered

高性能MySQL笔记-第5章Indexing for High Performance-003索引的作用

一. 1. 1). Indexes reduce the amount of data the server has to examine.2). Indexes help the server avoid sorting and temporary tables.3). Indexes turn random I/O into sequential I/O. Lahdenmaki and Leach’s book also introduces a three-star system for

第六章 优化服务器设置--高性能MySQL 施瓦茨

MySql的默认配置不适用于使用大量资源,因为其通用性很高. 不要期望改变配置文件会带来巨大得性能提升.提升大小取决于工作负载,通常可以通过选择适当的配置参数得到两到三倍得性能提升.在这时候,性能提升就是增量的.为了更大得提升,通常要检查服务器架构,查询及应用程序的架构. 6.1配置基础知识 1 首先要知道MySQL从什么地方获取配置信息.(可以用启动脚本 --defaults-file=配置文件位置) 2 配置文件被分成了若干部分,每部分第一行都是 [程序名] mysql程序会读取和程序名同名

【MySQL】《高性能MySQL》学习笔记,第四章,Schema与数据类型优化

[MySQL]<高性能MySQL>学习笔记,第四章,Schema与数据类型优化 良好的逻辑设计和物理设计是高性能的基石,应该根据系统将要执行的查询语句来设计schema. 反范式的设计可以加快某些类型的查询,单同时可能使另一类型的查询变慢,比如添加计数表和汇总表是一种很好的优化查询的方式,但这些表的维护成本可能会很高. 1.选择优化的数据类型 更小的通常更好. ? 应该尽量使用可以正确存储数据的最小类型,更小的数据类型通常更快,因为他们占用更少的磁盘,内存和CPU缓存,并且处理时需要的CPU周