`
风雪涟漪
  • 浏览: 497276 次
  • 性别: Icon_minigender_1
  • 来自: 大连->北京
博客专栏
952ab666-b589-3ca9-8be6-3772bb8d36d4
搜索引擎基础(Search...
浏览量:8773
Ae468720-c1b2-3218-bad0-65e2f3d5477e
SEO策略
浏览量:17697
社区版块
存档分类
最新评论

Schema的优化和索引 - 学习一个索引示例

阅读更多

用例子来理解索引的概念是再简单不过的方法了。因此我们研究一个索引示例。

 

假使我们要做一个在线约会的网站。用户的资料就会有很多列,比如country, state/region, city, sex, age, eye color等等。这个站点必须支持这些属性的组合来查询用户资料。也必须支持让用户通过用户最近的在线时间,其他会员的打分等等来排序和限制结果。对于这么个复杂的需求我们怎样来设计索引?

 

奇怪的是,第一个要决定的就是我们是否必须使用索引排序或者文件排序是否是可接受的。基于索引的排序限定了索引和语句创建的方式。比如,我们不能使用索引的情况是WHERE条件的age在18-25之间,不管语句是否使用了一个索引去排序。

如果MySQL使用了索引的范围查询,它也就不能使用另一个索引来排序了。假如这是个比较常见的查询,我们就得知有很多查询都要用到文件排序。

 

支持多种类的过滤

现在我们需要知道列具有唯一值的数量以及那些经常出现在WHERE条件后的列。在具有许多唯一值的列创建索引,这样做选择性很高。因为MySQL会高效的过滤掉不需要的值。

 

country不确定是否具有选择性,但是它可能会在大部分查询语句中出现。SEX列不具有选择性,但是它基本会在每个查询语句中出现。根据以上的想法,我们可以使用前缀为(sex,country)来创建一系列不同组合的索引。

 

按照传统的思路来说,在选择性低的字段加索引没有什么效果。但是我们为什么把一个选择性低的列放在每个索引的开始部分呢?思路错误了?

 

我们有两个理由要这么使用。第一个理由就是每个查询都会用到sex。我们甚至可以设计让用户仅能通过一个sex来进行搜索。重要的是,添加这一列也不会有什么弊端,因为我们还有一些技巧在里面。

 

技巧就是:即使一个查询没有对sex进行一些约束,我们也能通过AND sex IN('m', 'f')保证索引的使用。这么做不会过滤任意的行。因此功能上来说和不包含sex查询的语句一样。然而,我们需要包含这一列,因为这样做会使MySQL使用一个更大前缀的索引。这个技巧比较适用于这个需求,但是如果这个值有太多的不同,这样做就不会工作的很好,因为IN()列表太大了些。

 

这个例子揭示出一个一般准则:全面的考虑整张表。当你设计索引,不要仅仅思考你现有语句所需的索引类型,还要考虑优化语句。如果你需要一个索引,但是你想到一些语句可能会受到不好的影响,问问自己是否能改变这些语句。你应该把语句和索引一起优化来找到折中的一个方案;你没必要设计一个完美的索引schema.

 

接下来我们要考虑的是其他WHERE条件的组合并且考虑没有索引的情况下哪些组合会变慢。(sex,country,age)上索引是个显而易见的选择,并且我们也需要在(sex,country,region,age)和(sex,country,region,city,age)上创建索引。

 

这样会有很多索引的。如果我们想重用索引并且不生成过多的索引组合。我们可以使用IN()的技巧并且废弃(sex,country,age)和(sex,country,region,age)索引。如果它们并没有在搜索表单中,可以指定所有国家的列表或者国家的所有区域来确保符合和索引前缀相等的约束。

 

这些索引可以满足大部分常用查询的需要,但是我们怎样设计其他一些不常用的搜索条件呢。比如has_pictures, eye_color, hair_color和education?如果这些列选择性低并且不经常使用,我们可以不给它们创建索引并且让MySQL扫描一些额外的行。我们也可以这样做,我们可以把它们添加到age之前并且用到我们之前用到的IN()的方法。

 

现在你可能会发现,我们创建索引的时候总是把age列放到最后。这个列有啥不同么,为什么要放到最后?我们要尽可能使MySQL是用索引上所有的列,因为只能使用最左端的前缀,直到发现第一个条件为范围查询为止。我们已经提到的其他列在WHERE中都可以用相等的条件,但是age的条件是个范围(比如18-25之间)。

 

我们也可以转为IN()列表。如age IN(18, 19, 20, 21, 22, 23, 24, 25),但是这种方法并不适合这个类型的查询。我们举例要说明的一般准则就是把范围条件的列放到索引的最后,因此优化器就会尽可能使用索引。

 

我们已经说过了你可以添加更多的列到索引中并且使用IN()列表覆盖那些并不是WHERE条件一部分的那些列,但是你可能做的过了头就会导致问题的发生。在IN中使用了过多的值,是这些list的组合变得巨大,优化器就要评估这种情况了,并且,这样也会降低查询的速度。考虑下如下的语句:

 

 

WHERE eye_color IN('brown','blue','hazel')

AND hair_color IN('black','red','blonde','brown')

AND sex IN('M','F')

 

优化器会把这些转换为3*4*2组合,并且WHERE条件必须要对每一个进行校验。24个组合并不是很极端的数字。但是如果这个数字变为上千的话就要小心了。旧的MySQL版本在处理大量IN组合的时候会出现很多问题:查询优化器会用很多事前和消耗很多内存。如果组合变得过大,新的MySQL版本会停止评估这些组合 ,但是仍会限制MySQL使用索引。

 

避免使用多个范围查询

假设我们有个last_online列并且我们的需求是上个星期哪些用户在线:

 

WHERE eye_color IN('brown','blue','hazel')

AND hair_color IN('black','red','blonde','brown')

AND sex IN('M','F')

AND last_online > DATE_SUB('2008-01-17', INTERVAL 7 DAY)

AND age BETWEEN 18 AND 25

 

这个查询有个问题:它有两个范围条件。MySQL会使用这两个条件的其中一个,但不能同时使用。如果last_online约束单独出现,或者如果last_online的选择性多余age,我们可能希望添加另一系列的索引,要把last_online放在最后。但是,如果我们不能把age转为IN()列表,并且必须要在这两个条件同时出现来提高查询速度,怎么办?这种情况下,没有什么直接的方法,但是我们可以把一个范围条件转化为一个等值比较。为了这样做,我们添加了个active列,我们要周期性的维护它。当用户登录的时候我们会把这个列设为1,并且如果用户连续7天没有登录,会把它设为0。

 

这个方法可以让MySQL使用如(active, sex, country, age).这样的索引。这个列可能不是很精确,但是这种类型的查询要求的精确度低。如果我们需要精确地结果,我们可以在WHERE条件中加入last_online,当时不要索引它。这个方法有些像我们上几节说过的模拟HASH索引。这个条件不会使用任何的索引,但是因为它不太可能抛弃那些加上索引并没有太多好处的行。也就是说,没有索引也不太影响查询。

 

目前为止,你可能看到了一个模式:如果既想看活动的,也要看不活动的用户,我们可以添加IN()列表。我们已经添加了太多的列表了,但是可选择的是可以创建独立的索引来满足不同条件的组合。我们已经用了如下索引:(active,sex,country,age), (active,country,age), (sex,country,age), (country,age).虽然这些索引对于特有的查询都是最优的,但是就有维护的消耗,占有的空间,也可能使这种索引策略变得很失败。

 

这是个优化方式影响优化索引的例子。如果未来的MySQL版本可以放宽索引的扫描,它应该做到在单独索引上使用多个范围查询,因此我们也不必使用IN()列表了。

 

对于排序的优化

这个示例中我们所关心最后一个优化就是排序。排序小量规模的数据使用文件排序是很快的,但是如果有百万级别的呢?比如,查询条件只有个SEX的情况?

 

对于这种低选择性的情况,我们可以添加特殊的索引。比如,一个索引(sex,rating)可以适用于下列查询:

SELECT <cols> FROM profiles WHERE sex='M' ORDER BY rating LIMIT 10;

 

这个查询有ORDER BY 和LIMIT,并且它如果没有索引会非常之慢。

 

即使加上了索引,如果用户接口是分页并且用户请求的页并不是靠近数据开始的位置,查询还是很慢。语句如下:

mysql> SELECT <cols> FROM profiles WHERE sex='M' ORDER BY rating LIMIT 100000, 10;

 

这样的语句还是有问题,和加不加索引没什么关系。因为太高的数据偏移导致了MySQL不得不扫描没用的行。反规范的,提前计算的,缓存可能是处理这种语句比较好的方法。一个更好点的策略是限制用户可以查看的页数。这不太会影响用户体验,因为不会有人关心10000页以后的查询结果。

 

另一个优化这种语句比较好的方法是使用一个覆盖索引去重新获取行的主键。你能连接这个返回的表获得所需要的列。这些可以最小化MySQL的工作量,这些工作就是MySQL必须要收集它所要抛弃的数据。下面的例子就是针对这个语句的优化

 

 

mysql> SELECT <cols> FROM profiles INNER JOIN (

-> SELECT <primary key cols> FROM profiles

-> WHERE x.sex='M' ORDER BY rating LIMIT 100000, 10

-> ) AS x USING(<primary key cols>);

 

 

 

分享到:
评论

相关推荐

    graphql-compose-algolia:GraphQL组成中间件和解析器,以使您的数据与Algolia索引保持同步

    插件,通过中间件和解析器丰富您现有的类型编辑器,以使您的数据与Algolia索引保持同步。 该库当前为WIP。 到目前为止,如下面的示例中所示,我仅与一起使用,因此该库可能有问题,或者在其他环境中完全损坏,直到...

    dropbox-search:在Dropbox中建立索引并搜索文件内容

    需要node.js和Solr,支持许多文档类型和关键字快捷方式,并在添加或编辑文件时更新索引。 捆绑了带有摘要和Ajax加载结果的简单Web前端。示例搜索食谱搜索文本(区分大小写) “蛋糕配方” 词组搜索果酱配方:文件...

    Delphi/Kylix Database Development 附书代码

    .CtrlGrid--一个VCL特有的例子,演示如何使用TDBCtrlGrid组件。 第7章 .Updates--演示数据集提供者的基本操作。 .Joins--演示如何正确地解析通过SQLJOIN获取的数据。 .DataFetch——演示如何限制从数据集返回的...

    SQL Server 2008管理员必备指南(超高清PDF)Part2

    10.4.8 在一个表中添加和删除列 10.4.9 编写表脚本 10.5 管理表的值 10.5.1 使用原始数据类型 10.5.2 使用固定长度、可变长度 和最大长度的字段 10.5.3 使用用户定义的数据类型 10.5.4 允许和不允许Null值 10.5.5 ...

    MySQL5.1参考手册官方简体中文版

    在同一个数据库中创建多个表的缺陷 7.5. 优化MySQL服务器 7.5.1. 系统因素和启动参数的调节 7.5.2. 调节服务器参数 7.5.3. 控制查询优化器的性能 7.5.4. 编译和链接怎样影响MySQL的速度 7.5.5. MySQL如何使用内存 ...

    Delphi/Kylix数据库开发附书源码

    CtrlGrid-一个VCL特有的例子,演示如何使用TDBCtrlGrid组件。 第7章 Updates-演示数据集提供者的基本操作。 Joins-演示如何正确地解析通过SQL JOIN获取的数据。 DataFetch-演示如何限制从数据集返回的BLOB...

    whyte-dwarf:自定义搜寻器和自动Solr索引更新器

    我很快将添加简单的静态Solr适配器类,用于基于Silo的索引(基于域的键)的Example Schema.xml和用于示例使用的搜索表单。 设置Solr由您决定!学分PHP爬网PHPCrawl由sminnee创建 Robots.class.php 机器人由安迪·...

    NoSQL云数据库mongoDB的C#示例(vs2005)

    MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可护展的高性能数据存储解决方案。  它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:  *面向集合存储,易存储...

    SQL Server 2008管理员必备指南(超高清PDF)Part1

    10.4.8 在一个表中添加和删除列 10.4.9 编写表脚本 10.5 管理表的值 10.5.1 使用原始数据类型 10.5.2 使用固定长度、可变长度 和最大长度的字段 10.5.3 使用用户定义的数据类型 10.5.4 允许和不允许Null值 10.5.5 ...

    SQL Server 2008管理员必备指南(超高清PDF)Part3

    10.4.8 在一个表中添加和删除列 10.4.9 编写表脚本 10.5 管理表的值 10.5.1 使用原始数据类型 10.5.2 使用固定长度、可变长度 和最大长度的字段 10.5.3 使用用户定义的数据类型 10.5.4 允许和不允许Null值 10.5.5 ...

    SQL.Server.2008管理员必备指南.part1.rar(1/4)

    (本书由4部分组成,需下载完4个文件之后,放在同一文件目录下才能正常解压) 基本信息  作者:(美国)(William R.... 10.4.8 在一个表中添加和删除列 293  10.4.9 编写表脚本 294  10.5 管理表的值 294  10.5.1...

    Hibernate中文详细学习文档

    1.2. 第一部分 - 第一个Hibernate应用程序 1.2.1. 第一个class 1.2.2. 映射文件 1.2.3. Hibernate配置 1.2.4. 用Ant构建 1.2.5. 启动和辅助类 1.2.6. 加载并存储对象 1.3. 第二部分 - 关联映射 1.3.1. ...

    MySQL 5.1官方简体中文参考手册

    在同一个数据库中创建多个表的缺陷 7.5. 优化MySQL服务器 7.5.1. 系统因素和启动参数的调节 7.5.2. 调节服务器参数 7.5.3. 控制查询优化器的性能 7.5.4. 编译和链接怎样影响MySQL的速度 7.5.5. MySQL如何使用内存 ...

    Microsoft SQL Server 2005技术内幕: T-SQ程序设计.pdf

    从1999年开始,他一直是Microsoft SQL Server MVP(最有价值专家),并在全世界已经开展过无数次T-SQL查询、T-SQL优化和编程方面的培训。Itzik是Microsoft SQL Server方面几本著作的作者。他在SQL ServerMagazine和...

    SQL.Server.2008管理员必备指南.part2.rar(2/4)

    (本书由4部分组成,需下载完4个文件之后,放在同一文件目录下才能正常解压) 基本信息  作者:(美国)(William R.... 10.4.8 在一个表中添加和删除列 293  10.4.9 编写表脚本 294  10.5 管理表的值 294  10.5.1...

    SQL.Server.2008管理员必备指南.part3.rar(3/4)

    (本书由4部分组成,需下载完4个文件之后,放在同一文件目录下才能正常解压) 基本信息  作者:(美国)(William R.... 10.4.8 在一个表中添加和删除列 293  10.4.9 编写表脚本 294  10.5 管理表的值 294  10.5.1...

    SQL.Server.2008管理员必备指南.part4.rar(4/4)

    (本书由4部分组成,需下载完4个文件之后,放在同一文件目录下才能正常解压) 基本信息  作者:(美国)(William R.... 10.4.8 在一个表中添加和删除列 293  10.4.9 编写表脚本 294  10.5 管理表的值 294  10.5.1...

    MySQL 5.1参考手册

    在同一个数据库中创建多个表的缺陷 7.5. 优化MySQL服务器 7.5.1. 系统因素和启动参数的调节 7.5.2. 调节服务器参数 7.5.3. 控制查询优化器的性能 7.5.4. 编译和链接怎样影响MySQL的速度 7.5.5. MySQL如何使用内存 ...

    solr基础知识介绍

    4.应用示例 7 4.1 创建索引 7 4.1 删除索引 7 4.1 索引查询 8 5.配置文件分析 9 5.1 schema.xml 9 5.1.1 文件分析 9 5.1.2 文档注释 13 5.2 solrconfig.xml 16 6.Solr缓存 18 6.1 filterCache 18 6.2 queryResult...

Global site tag (gtag.js) - Google Analytics