数据库 mysql 优化器原理
MySQL查询优化器有几个目标,但是其中最主要的目标是尽可能地使用索引,并且使用最严格的索引来消除尽可能多的数据行。
你的最终目标是提交SELECT语句查找数据行,而不是排除数据行。优化器试图排除数据行的原因在于它排除数据行的速度越快,那么找到与条件匹配的数据行也就越快。
如何 更好的 利用索引:
1:尽量比较数据类型相同的数据列。当你在比较操作中使用索引数据列的时候,请使用数据类型相同的列。相同的数据类型比不同类型的性能要高一些。
例如,INT与BIGINT是不同的。CHAR(10)被认为是CHAR(10)或VARCHAR(10),但是与CHAR(12)或VARCHAR(12)不同。如果你所比较的数据列的类型不同,那么可以使用ALTER TABLE来修改其中一个,使它们的类型相匹配。
mysql优化,包括表数据类型选择,sql语句优化,系统配置与维护优化
1、 表数据类型选择
(1)越小的数据类型通常更好:越小的数据类型通常在磁盘、内存和CPU缓存中都需要更少的空间,处理起来更快。
(2)简单的数据类型更好:整型数据比起字符,处理开销更小,因为字符串的比较更复杂。在MySQL中,应该用内置的日期和时间数据类型,而不是用字符串来存储时间;以及用整型数据类型存储IP地址。
(3)尽量避免NULL:应该指定列为NOT NULL,除非你想存储NULL。在MySQL中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值。
(3)字符串如何选择char和varchar?一般phper能想到就是char是固定大小,varchar能动态储存数据。这里整理一下这两者的区别:
属性 |
Char |
Varchar |
值域大小 |
最长字符数是255(不是字节),不管什么编码,超过此值则自动截取255个字符保存并没有报错。 |
65535个字节,开始两位存储长度,超过255个字符,用2位储存长度,否则1位,具体字符长度根据编码来确定,如utf8 则字符最长是21845个 |
如何处理字符串末尾空格 |
去掉末尾空格,取值出来比较的时候自动加上进行比较 |
Version<=4.1,字符串末尾空格被删掉,version>5.0则保留 |
储存空间 |
固定空间,比喻char(10)不管字符串是否有10个字符都分配10个字符的空间 |
Varchar内节约空间,但更新可能发生变化,若varchar(10),开始若储存5个字符,当update成7个时有myisam可能把行拆开,innodb可能分页,这样开销就增大 |
适用场合 |
适用于存储很短或固定或长度相似字符,如MD5加密的密码char(33)、昵称char(8)等 |
当最大长度远大于平均长度并且发生更新的时候。 |
注意当一些英文或数据的时候,最好用每个字符用字节少的类型,如latin1
(4)整型、整形优先原则
Tinyint、smallint、mediumint、int、bigint,分别需要8、16、24、32、64。
值域范围:-2^(n-1)~ 2^(n-1)-1
很多程序员在设计数据表的时候很习惯的用int,压根不考虑这个问题
笔者建议:能用tinyint的绝不用smallint
误区:int(1) 和int(11)是一样的,唯一区别是mysql客户端显示的时候显示多少位。
int(2) unsigned => 存入2 即为 2
int(2) unsigned zerofil => 存入2 即为 02
整形优先原则:能用整形的不用其他类型替换,如ip可以转换成整形保存,如商品价格‘50.00元’则保存成50
(5)精确度与空间的转换。在存储相同数值范围的数据时,浮点数类型通常都会比DECIMAL类型使用更少的空间。FLOAT字段使用4字节存储
数据。DOUBLE类型需要8 个字节并拥有更高的精确度和更大的数值范围,DECIMAL类型的数据将会转换成DOUBLE类型。
2、 sql语句优化
- mysql> create table test (
- id smallint(10) not null auto_increment primary key,
- username char(8) not null,
- password char(4) not null,
- `level` tinyint (1) default 0,
- last_login char(15) not null,
- index(username,password,last_login))engine=innodb;
这是test表,其中id是主键,多列索引(username,password,last_login),里面有10000多条数据.
(1) 最左前缀原则
定义:最左前缀原则指的的是在sql where 字句中一些条件或表达式中出现的列的顺序要保持和多索引的一致或以多列索引顺序出现,只要出现非顺序出现、断层都无法利用到多列索引。
举例说明:上面给出一个多列索引(username,password,last_login),当三列在where中出现的顺序如(username,password,last_login)、(username,password)、(username)才能用到索引,如下面几个顺序(password,last_login)、(passwrod)、(last_login)—这三者不从username开始,(username,last_login)—断层,少了password,都无法利用到索引。
因为B+tree多列索引保存的顺序是按照索引创建的顺序,检索索引时按照此顺序检索
测试:以下测试不精确,这里只是说明如何才能正确按照最左前缀原则使用索引。还有的是以下的测试用的时间0.00sec看不出什么时间区别,因为数据量只有20003条,加上没有在实体机上运行,很多未可预知的影响因素都没考虑进去。当在大数据量,高并发的时候,最左前缀原则对与提高性能方面是不可否认的。
Ps:最左前缀原则中where字句有or出现还是会遍历全表
(1.1)能正确的利用索引
l Where子句表达式顺序是(username)
- mysql> explain select * from one where username=‘abgvwfnt’;
- +—-+————-+——-+——+—————+———-+———+——-+——+————-+
- | id | select_type | table | type | possible_keys | key | key_len | ref |rows | Extra |
- +—-+————-+——-+——+—————+———-+———+——-+——+————-+
- | 1 | SIMPLE | one | ref | username | username | 24 | const |5 | Using where |
- +—-+————-+——-+——+—————+———-+———+——-+——+————-+
- 1 row in set (0.00 sec)
l Where子句表达式顺序是(username,password)
- mysql> explain select * from one where username=‘abgvwfnt’ and password=’123456′;
- +—-+————-+——-+——+—————+———-+———+————-+——+————-+
- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
- +—-+————-+——-+——+—————+———-+———+————-+——+————-+
- | 1 | SIMPLE | one | ref | username | username | 43 | const,const | 1 | Using where |
- +—-+————-+——-+——+—————+———-+———+————-+——+————-+
- 1 row in set (0.00 sec)
l Where子句表达式顺序是(username,password, last_login)
- mysql> explain select * from one where username=‘abgvwfnt’ and password=’123456′andlast_login=’1338251170′;
- +—-+————-+——-+——+—————+———-+———+——————-+——+————-+
- | id | select_type | table | type | possible_keys | key | key_len | ref| rows | Extra |
- +—-+————-+——-+——+—————+———-+———+——————-+——+————-+
- | 1 | SIMPLE | one | ref | username | username | 83 | const,const,const | 1 | Using where|
- +—-+————-+——-+——+—————+———-+———+——————-+——+————-+
- 1 row in set (0.00 sec)
上面可以看出type=ref 是多列索引,key_len分别是24、43、83,这说明用到的索引分别是(username), (username,password), (username,password, last_login );row分别是5、1、1检索的数据行都很少,因为这三个查询都按照索引前缀原则,可以利用到索引。
(2) Order by 优化
(3) 隔离列:如id+1=2 => id = 1
(4) OR、IN、UNION ALL,可以尝试用UNION ALL
(4.1)or会遍历表就算有索引
(4.2)对于in,这个是有争议的,网上很多优化方案中都提到尽量少用in,这不全面,其实在in里面如果是常量的话,可一大胆的用in
(4.3)UNION All 直接返回并集,可以避免去重的开销。之所说“尝试”用UNION All 替代 OR来优化sql语句,因为这不是一直能优化的了,这里只是作为一个方法去尝试
(5) 索引选择性
索引选择性是不重复的索引值也叫基数(cardinality)表中数据行数的比值,索引选择性=基数/数据行,基数可以通过“show index from 表名”查看。
高索引选择性的好处就是mysql查找匹配的时候可以过滤更多的行,唯一索引的选择性最佳,值为1。
那么对于非唯一索引或者说要被创建索引的列的数据内容很长,那就要选择索引前缀。这里就简单说明一下:
- mysql> select count(distinct(username))/count(*) from one;
- +————————————+
- | count(distinct(username))/count(*) |
- +————————————+
- | 0.2047 |
- +————————————+
- 1 row in set (0.09 sec)
count(distinct(username))/count(*)就是索引选择性的值,这里0.2太小了。
(6) 重复或多余索引
很多phper开始都以为建索引相对多点性能就好点,压根没考虑到有些索引是重复的,比如建一个(username),(username,password), (username,password,last_login),很明显第一个索引是重复的,因为后两者都能满足其功能。
要有个意识就是,在满足功能需求的情况下建最少索引。对于INNODB引擎的索引来说,每次修改数据都要把主键索引,辅助索引中相应索引值修改,这可能会出现大量数据迁移,分页,以及碎片的出现。
3、系统配置与维护优化
(1) 重要的一些变量
l key_buffer_size索引块缓存区大小, 针对MyISAM存储引擎,该值越大,性能越好.但是超过操作系统能承受的最大值,反而会使mysql变得不稳定. —-这是很重要的参数
l sort_buffer_size 这是索引在排序缓冲区大小,若排序数据大小超过该值,则创建临时文件,注意和myisam_sort_buffer_size的区别—-这是很重要的参数
l read_rnd_buffer_size当排序后按排序后的顺序读取行时,则通过该缓冲区读取行,避免搜索硬盘。将该变量设置为较大的值可以大大改进ORDER BY的性能。但是,这是为每个客户端分配的缓冲区,因此你不应将全局变量设置为较大的值。相反,只为需要运行大查询的客户端更改会话变量
l join_buffer_size用于表间关联(join)的缓存大小
l tmp_table_size缓存表的大小
l table_cache允许 MySQL 打开的表的最大个数,并且这些都cache在内存中
l delay_key_write针对MyISAM存储引擎,延迟更新索引.意思是说,update记录时,先将数据up到磁盘,但不up索引,将索引存在内存里,当表关闭时,将内存索引,写到磁盘
更多参数查看http://www.phpben.com/?post=70
(2) optimize、Analyze、check、repair维护操作
l optimize 数据在插入,更新,删除的时候难免一些数据迁移,分页,之后就出现一些碎片,久而久之碎片积累起来影响性能,这就需要DBA定期的优化数据库减少碎片,这就通过optimize命令。
如对MyisAM表操作:optimize table 表名
对于InnoDB表是不支持optimize操作,否则提示“Table does not support optimize, doing recreate + analyze instead”,当然也可以通过命令:alter table one type=innodb; 来替代。
l Analyze用来分析和存储表的关键字的分布,使得系统获得准确的统计信息,影响 SQL 的执行计划的生成。对于数据基本没有发生变化的表,是不需要经常进行表分析的。但是如果表的数据量变化很明显,用户感觉实际的执行计划和预期的执行计划不同的时候,执行一次表分析可能有助于产生预期的执行计划。
Analyze table 表名
l Check检查表或者视图是否存在错误,对 MyISAM 和 InnoDB 存储引擎的表有作用。对于 MyISAM 存储引擎的表进行表检查,也会同时更新关键字统计数据
l Repair optimize需要有足够的硬盘空间,否则可能会破坏表,导致不能操作,那就要用上repair,注意INNODB不支持repair操作
以上的操作出现的都是如下这是check
- +———-+——-+————–+————-+
- | Table | Op | Msg_type| Msg_text |
- +———-+——-+————–+————-+
- | test.one | check | status | OK |
- +———-+——-+————–+————-+
其中op是option 可以是repair check optimize,msg_type 表示信息类型,msg_text 表示信息类型,这里就说明表的状态正常。如在innodb表使用repair就出现note | The storage engine for the table doesn’t support repair
注意:以上操作最好在数据库访问量最低的时候操作,因为涉及到很多表锁定,扫描,数据迁移等操作,否则可能导致一些功能无法正常使用甚至数据库崩溃。
(3)表结构的更新与维护
l 改表结构。当要在数据量千万级的数据表中使用alter更改表结构的时候,这是一个棘手问题。一种方法是在低并发低访问量的时候用平常的alter更改表。另外一种就是建另一个与要修改的表,这个表除了要修改的结构属性外其他的和原表一模一样,这样就能得到一个相应的.frm文件,然后用flush with read lock 锁定读,然后覆盖用新建的.frm文件覆盖原表的.frm,最后unlock table 释放表。
l 建立新的索引。一般方法这里不说。
1、 创建没索引的a表,导入数据形成.MYD文件。
2、 创建包括索引b表,形成.FRM和.MYI文件
3、 锁定读写
4、 把b表的.FRM和.MYI文件改成a表名字
5、 解锁
6、 用repair创建索引。
这个方法对于大表也是很有效的。这也是为什么很多dba坚持说“先导数据库在建索引,这样效率更快”
l 定期检查mysql服务器
定期使用show status、show processlist等命令检查数据库。这里就不细说,这说起来也篇幅是比较大的,笔者对这个也不是很了解
第四部分:图说mysql查询执行流程
1、 查询缓存,判断sql语句是否完全匹配,再判断是否有权限,两个判断为假则到解析器解析语句,为真则提取数据结果返回给用户。
2、 解析器解析。解析器先词法分析,语法分析,检查错误比如引号有没闭合等,然后生成解析树。
3、 预处理。预处理解决解析器无法决解的语义,如检查表和列是否存在,别名是否有错,生成新的解析树。
4、 优化器做大量的优化操作。
5、 生成执行计划。
6、 查询执行引擎,负责调度引擎获取相应数据
7、 返回结果。
转载地址:http://ourmysql.com/archives/1171
http://blog.chinaunix.net/uid-25311424-id-3957863.html
数据库 mysql 优化器原理的更多相关文章
- 数据库MySQL 之 索引原理与慢查询优化
数据库MySQL 之 索引原理与慢查询优化 浏览目录 索引介绍方法类型 聚合索引辅助索引 测试索引 正确使用索引 组合索引 注意事项 查询计划 慢查询日志 大数据量分页优化 一.索引介绍方法类型 1. ...
- 机智的MySQL优化器 --- is null
[介绍] 工作的越久越到的的问题越多,就越是觉得一些“老话”历久弥新:由于最近的学习计划是深入的学习一遍MySQL优化器:学习过程中的一些成果 也会发布到这里,一来是为了整理自己已经知道的和新学到的, ...
- 如何干涉MySQL优化器使用hash join
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 前言 实验 总结 前言 数据库的优化器相当于人类的大 ...
- 0104探究MySQL优化器对索引和JOIN顺序的选择
转自http://www.jb51.net/article/67007.htm,感谢博主 本文通过一个案例来看看MySQL优化器如何选择索引和JOIN顺序.表结构和数据准备参考本文最后部分" ...
- MySQL优化器 --- index_merge
[背景] 对于关系数据库中的一张表,通常来说数据页面的总大小要比较某一个索引占用的页面要大的多(上面说的索引是不包涵主键索引的); 更进一步我们可以推导出,如果我们通过读索引就能解决问题,那么它相比读 ...
- 《Mysql - 优化器是如何选择索引的?》
一:概念 - 在 索引建立之后,一条语句可能会命中多个索引,这时,索引的选择,就会交由 优化器 来选择合适的索引. - 优化器选择索引的目的,是找到一个最优的执行方案,并用最小的代价去执行语句. 二: ...
- MySQL优化器cost计算
记录MySQL 5.5上,优化器进行cost计算的方法. 第一篇: 单表的cost计算 数据结构: 1. table_share: 包含了表的元数据,其中索引部分: key_info:一个key的结构 ...
- MySQL优化器功能开关optimizer_switch
MySQL 8.0新增特性 use_invisible_indexes:是否使用不可见索引,MySQL 8.0新增可以创建invisible索引,这一开关控制优化器是否使用invisible索引,on ...
- MySQL优化器不使用索引的情况
优化器选择不适用索引的情况 有时候,有乎其并没有选择索引而去查找数据,而是通过扫描聚集索引,也就是直接进行全表的扫描来得到数据.这种情况多发生于范围查找.JOIN链接操作等情况.例如 ; 通过SHOW ...
随机推荐
- PHP读书笔记(5)-结构语句
PHP结构语句 顺序结构 顺序结构就像一条直线,按着顺序一直往下执行.我们编写的代码默认都是按照顺序结构执行的. 条件结构之if…else… 条件结构就像一个岔路口,可以向左走,也可以向右走.比如上洗 ...
- 两个数据库表同步的可视化WEB同步程序
因业务升级,现有一个数据库中的表需要与先前项目中的表进行数据同步,停用先前的表,这两个表只能按其中相同的一个字段同步,认真研究了一下,用WEB程序进行了处理,可视化显示处理进度,同步操作结果.使用到的 ...
- python(5)- sys.stdout()实现进度条
1. 使用\r , 让其始终在行首输出,实现进度条 import sys, time ''' 使用\r 来实现进度条的效果,\r 是光标移到行首但不换行. 假设文件大小为60,一下下载1, 下载到60 ...
- CF Tanya and Postcard
Tanya and Postcard time limit per test 2 seconds memory limit per test 256 megabytes input standard ...
- hdu 4005 边连通度与缩点
思路:先将图进行缩点,建成一颗树,那么如果这是一条单路径树(即最大点度不超过2),就不在能删的一条边,使得不连通.因为将其头尾相连,形成一个圈,那么删任意一条边,图都是连通的. 上面的是无解的情况,如 ...
- loadrunner做webservice接口之简单调用
今天听大神讲了webservice做接口,我按照他大概讲的意思自己模拟实战了下,可能还有很多不对,一般使用webservice做接口,会使用到soapui,但是用了loadrunner以后发现lr很快 ...
- c#语法笔记
书写代码需要注意的地方: 1.代码中出现的所有标点都是英文半角 shift键快速切换中文半角和英文半角 shift+空格 切换全角/半角 2.在c#代码中,每行代码的结束,我们都以分号结束,注意:这个 ...
- UML建模——概述
轻松玩建模 统一建模语言UML快速入门 http://soft.yesky.com/lesson/281/2472281.shtml UML是一种定义良好.易于表达.功能强大且普遍适用的建模语言.它溶 ...
- 设置win7任务栏显示标题,而不显示缩略图
win7系统的任务栏可以显示桌面缩略图,这是非常好的一个功能,但是有时候我们希望只显示标题,如下所示 怎样设置呢?只要在桌面上的计算机图标上面“右键”,选择“属性”,在弹出的窗口选择“高级系统设置”, ...
- [Entity Framework] MySQL @ Entity Framework 6
原文 [Entity Framework] MySQL @ Entity Framework 6 要让MySQL能够用EF6,我花了一点时间,在此记录一下 安装元件 在设定档加入Provider 安装 ...