手机浏览 RSS 2.0 订阅 膘叔的简单人生 , 腾讯云RDS购买 | 超便宜的Vultr , 注册 | 登陆
浏览模式: 标准 | 列表Tag:索引

[分享]mysql数据库索引查询优化的分享

问题描述:


我们要访问的表是一个非常大的表,四千万条记录,id是主键,program_id上建了索引。
执行一条SQL:

select * from program_access_log where program_id between 1 and 4000

这条SQL非常慢。
我们原以为处理记录太多的原因,所以加了id限制,一次只读五十万条记录

select * from program_access_log where id between 1 and 500000 and program_id between 1 and 4000

但是这条SQL仍然很慢,速度比上面一条几乎没有提升。
Mysql处理50万条记录的表,条件字段还建了索引,这条语句应该是瞬间完成的。


问题分析:


这张表大约容量30G,数据库服务器内存16G,无法一次载入。就是这个造成了问题。
这条SQL有两个条件,ID一到五十万和Program_id一到四千,因为program_id范围小得多,mysql选择它做为主要索引。
先通过索引文件找出了所有program_id在1到4000范围里所有的id,这个过程非常快。
接下来要通过这些id找出表里的记录,由于这些id是离散的,所以mysql对这个表的访问不是顺序读取。
而这个表又非常大,无法一次装入内存,所以每访问一条记录mysql都要重新在磁盘上定位并把附近的记录都载入内存,大量的IO操作导致了速度的下降。

问题解决方案:
1. 以program_id为条件对表进行分区
2. 分表处理,每张表的大小不超过内存的大小
然而,服务器用的是mysql5.0,不支持分区,而且这个表是公共表,无法在不影响其它项目的条件下修改表的结构。
所以我们采取了第三种办法:

select * from program_access_log where id between 1 and 500000 and program_id between 1 and 15000000

现在program_id的范围远大于id的范围,id被当做主要索引进行查找,由于id是主键,所以查找的是连续50万条记录,速度和访问一个50万条记录的表基本一样

总结:
这是一个在千万笔记录表中由于使用了索引导致了数据查找变慢的问题,有一定的典型性和大家交流下!

————END————

索引这个东西很害人的。好的时候很好。。。用的不好。那真的是害己害人啊。

本来来自:PHP5研究室,责编:抽烟的蚊子,网址:http://www.phpv.net/html/1624.html

Tags: mysql, database, 索引, 优化, 技巧

MYSQL索引之小小分析

MYSQL中,如果表是MYISAM,除了主键索引外,基本上常用的就是默认的索引,而unique index应该是比较少用到的。外键索引在MYSQL4.0以下直接忽略。

查了一下资料,好象默认索引应该是称之为B*Tree索引

B*Tree索引
  1. B*Tree索引是最常见的索引结构,默认建立的索引就是这种类型的索引。B*Tree索引在检索高基数数据列(高基数数据列是指该列有很多不同的值)时提供了最好的性能。当取出的行数占总行数比例较小时B-Tree索引比全表检索提供了更有效的方法。但当检查的范围超过表的10%时就不能提高取回数据的性能。B-Tree索引是基于二叉树的,由分支块(branch block)和叶块(leaf block)组成。在树结构中,位于最底层底块被称为叶块,包含每个被索引列的值和行所对应的rowid。在叶节点的上面是分支块,用来导航结构,包含了索引列(关键字)范围和另一索引块的地址,如图26-1所示。  
  2.   
  3. 假设我们要找索引中值为80的行,从索引树的最上层入口开始,定位到大于等于50,然后往左找,找到第2个分支块,定位为75-100,最后再定位到叶块上,找到80所对应的rowid,然后根据rowid去读取数据块获取数据。如果查询条件是范围选择的,比如where column >20 and column <80,那么会先定位到第一个包含20的叶块,然后横向查找其他的叶块,直到找到包含80的块为止,不用每次都从入口进去再重新定位。  
平时在MYSQL的查询中,我们也是建议,筛选条件高的、返回结果集比较整齐的,尽量放在条件的第一个,依稀记得WHERE条件中,如果是AND那么是从前往后比较,如果是OR条件,那么是从后往前比较。(记不清了。。。)

刚才又看到文章,说在使用B*Tree索引的时候千万要注意:Btree索引不存储NULL值,而在order by desc中,NULL值总是最大的,sql语句通过索引无法判断表是否存在NULL,执行计划还是走全表扫描[详见:http://rdc.taobao.com/blog/dba/html/67_oracle_fenye.html](当然,这也只是一个参考)

如果是确实这样,那么如果该字段可能会需要用到排序的时候,请尽量不要使用default NULL,而且在处理where的时候,NULL是不会被显示的。除非是条件是is NULL。

刚才测试了一下,设定了某字段允许NULL,然后插入数据,并为该字段做了索引,结果在where fields < 100 的时候,使用了该索引(当然NULL值是不显示的),但我取where fields > 0的时候,扫描方式立刻变为了全表,而且没有使用fields索引(同样不显示含 NULL值的行),where fields > 1 的时候,同样扫描全表。直到我设为fields > 2的时候,又开始使用了索引,看来,MYSQL对于索引字段做查询条件的时候还是需要斟酌的,条件不能随便乱加。

其实在程序中,如果使用INT字段(数值型字段),请尽量不要使用NULL,否则确实有可能会出现值查不到的情况。

然而,在我现在的项目中却有这种情况,要求我们把该字段设为NULL,明天我要再检查一下代码,以防止有漏掉的数据。

不看不知道一看吓一跳呀

Tags: mysql, 索引, index

Records:712