
在开始演示之前,我们先介绍下两个概念。
概念一,数据的可选择性基数,也就是常说的cardinality值。
查询优化器在生成各种执行计划之前,得先从统计信息中取得相关数据,这样才能估算每步 *** 作所涉及到的记录数,而这个相关数据就是cardinality。简单来说,就是每个值在每个字段中的唯一值分布状态。
比如表t1有100行记录,其中一列为f1。f1中唯一值的个数可以是100个,也可以是1个,当然也可以是1到100之间的任何一个数字。这里唯一值越的多少,就是这个列的可选择基数。
那看到这里我们就明白了,为什么要在基数高的字段上建立索引,而基数低的的字段建立索引反而没有全表扫描来的快。当然这个只是一方面,至于更深入的探讨就不在我这篇探讨的范围了。
概念二,关于HINT的使用。
这里我来说下HINT是什么,在什么时候用。
HINT简单来说就是在某些特定的场景下人工协助MySQL优化器的工作,使她生成最优的执行计划。一般来说,优化器的执行计划都是最优化的,不过在某些特定场景下,执行计划可能不是最优化。
比如:表t1经过大量的频繁更新 *** 作,(UPDATE,DELETE,INSERT),cardinality已经很不准确了,这时候刚好执行了一条SQL,那么有可能这条SQL的执行计划就不是最优的。为什么说有可能呢?
来看下具体演示
譬如,以下两条SQL,
A:
select * from t1 where f1 = 20B:
select * from t1 where f1 = 30如果f1的值刚好频繁更新的值为30,并且没有达到MySQL自动更新cardinality值的临界值或者说用户设置了手动更新又或者用户减少了sample page等等,那么对这两条语句来说,可能不准确的就是B了。
这里顺带说下,MySQL提供了自动更新和手动更新表cardinality值的方法,因篇幅有限,需要的可以查阅手册。
那回到正题上,MySQL 8.0 带来了几个HINT,我今天就举个index_merge的例子。
示例表结构:
mysql>desc t1+------------+--------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+------------+--------------+------+-----+---------+----------------+| id | int(11) | NO | PRI | NULL | auto_increment || rank1 | int(11) | YES | MUL | NULL | || rank2 | int(11) | YES | MUL | NULL | || log_time | datetime | YES | MUL | NULL | || prefix_uid | varchar(100) | YES | | NULL | || desc1 | text | YES | | NULL | || rank3 | int(11) | YES | MUL | NULL | |+------------+--------------+------+-----+---------+----------------+7 rows in set (0.00 sec)表记录数:
mysql>select count(*) from t1+----------+| count(*) |+----------+| 32768 |+----------+1 row in set (0.01 sec)这里我们两条经典的SQL:
SQL C:
select * from t1 where rank1 = 1 or rank2 = 2 or rank3 = 2SQL D:
select * from t1 where rank1 =100 and rank2 =100 and rank3 =100表t1实际上在rank1,rank2,rank3三列上分别有一个二级索引。
那我们来看SQL C的查询计划。
显然,没有用到任何索引,扫描的行数为32034,cost为3243.65。
mysql>explain format=json select * from t1 where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "3243.65" }, "table": { "table_name": "t1", "access_type": "ALL", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "rows_examined_per_scan": 32034, "rows_produced_per_join": 115, "filtered": "0.36", "cost_info": { "read_cost": "3232.07", "eval_cost": "11.58", "prefix_cost": "3243.65", "data_read_per_join": "49K" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))" } }}1 row in set, 1 warning (0.00 sec)我们加上hint给相同的查询,再次看看查询计划。
这个时候用到了index_merge,union了三个列。扫描的行数为1103,cost为441.09,明显比之前的快了好几倍。
mysql>explain format=json select /*+ index_merge(t1) */ * from t1 where rank1 =1 or rank2 = 2 or rank3 = 2\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "441.09" }, "table": { "table_name": "t1", "access_type": "index_merge", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "key": "union(idx_rank1,idx_rank2,idx_rank3)", "key_length": "5,5,5", "rows_examined_per_scan": 1103, "rows_produced_per_join": 1103, "filtered": "100.00", "cost_info": { "read_cost": "330.79", "eval_cost": "110.30", "prefix_cost": "441.09", "data_read_per_join": "473K" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank1` = 1) or (`ytt`.`t1`.`rank2` = 2) or (`ytt`.`t1`.`rank3` = 2))" } }}1 row in set, 1 warning (0.00 sec)我们再看下SQL D的计划:
不加HINT,
mysql>explain format=json select * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "534.34" }, "table": { "table_name": "t1", "access_type": "ref", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "key": "idx_rank1", "used_key_parts": [ "rank1" ], "key_length": "5", "ref": [ "const" ], "rows_examined_per_scan": 555, "rows_produced_per_join": 0, "filtered": "0.07", "cost_info": { "read_cost": "478.84", "eval_cost": "0.04", "prefix_cost": "534.34", "data_read_per_join": "176" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100))" } }}1 row in set, 1 warning (0.00 sec)加了HINT,
mysql>explain format=json select /*+ index_merge(t1)*/ * from t1 where rank1 =100 and rank2 =100 and rank3 =100\G*************************** 1. row ***************************EXPLAIN: { "query_block": { "select_id": 1, "cost_info": { "query_cost": "5.23" }, "table": { "table_name": "t1", "access_type": "index_merge", "possible_keys": [ "idx_rank1", "idx_rank2", "idx_rank3" ], "key": "intersect(idx_rank1,idx_rank2,idx_rank3)", "key_length": "5,5,5", "rows_examined_per_scan": 1, "rows_produced_per_join": 1, "filtered": "100.00", "cost_info": { "read_cost": "5.13", "eval_cost": "0.10", "prefix_cost": "5.23", "data_read_per_join": "440" }, "used_columns": [ "id", "rank1", "rank2", "log_time", "prefix_uid", "desc1", "rank3" ], "attached_condition": "((`ytt`.`t1`.`rank3` = 100) and (`ytt`.`t1`.`rank2` = 100) and (`ytt`.`t1`.`rank1` = 100))" } }}1 row in set, 1 warning (0.00 sec)对比下以上两个,加了HINT的比不加HINT的cost小了100倍。
总结下,就是说表的cardinality值影响这张的查询计划,如果这个值没有正常更新的话,就需要手工加HINT了。相信MySQL未来的版本会带来更多的HINT。
右键单击要在日志传送配置中用作主数据库的数据库,然后单击“属性”。在“选择页”下,单击“事务日志传送”。在“辅助服务器实例和数据库”下,单击“添加”。单击“连接”,连接到要用作辅助服务器的 SQL Server 实例。在“辅助数据库”框中,从列表中选择一个数据库或键入要创建的数据库的名称。在“初始化辅助数据库”选项卡上,选择要用于初始化辅助数据库的选项。在“复制文件”选项卡的“复制文件的目标文件夹”框中,键入应将事务日志备份复制到的文件夹的路径。该文件夹通常位于辅助服务器上。请注意“复制作业”下的“计划”框中列出的复制计划。如果希望针对您的安装自定义计划,请单击“计划”,然后根据需要调整 SQL Server 代理计划。此计划应为大致的备份计划。在“还原”选项卡上的“还原备份时的数据库状态”下,选择“无恢复模式”或“备用模式”选项。如果选择了“备用模式”选项,请选择是否要在进行还原 *** 作时从辅助数据库断开用户连接。如果希望延迟辅助服务器上的还原进程,请在“延迟还原备份 *** 作至少”下选择延迟时间。在“在以下时间内没有执行还原时报警”下选择警报阈值。请注意,还原计划列在“计划”框中的“还原作业”下。如果希望针对您的安装自定义计划,请单击“计划”,然后根据需要调整 SQL Server 代理计划。此计划应为大致的备份计划。单击“确定”。在一个关系数据库中提高和优化查询方法。很多人都将数据库看成神奇的圣人,即能够解决人们提出的各种问题。任何关系数据库都有一套解决查询的规则,而各种关系数据库查询的过程稍有所区别,但是基本的 *** 作思想和过程是一致的。本文将为你介绍查询分析器解决查询的方法和过程。查询优化的目标在查看分析器查询的步骤之前,理解查询优化目标相当重要。显然,查询 *** 作的其中一个目标是尽可能地减少使用资源。从数据库的角度看,这就意味着尽可能地减少I/O *** 作的次数。在对I/O *** 作的判断上,查询分析器经常做出错误的结果。而I/O *** 作次数必须满足磁盘的读取容量。这样从磁盘I/O读取的角度看,必须做出合理的选择条件。索引基于表格的索引是关系数据库用于解决查询的重要技术,也是数据库同时预先将数据分类导入到多表格的方式。通过索引中的字段和实际数据存放的指针可以完成以上的过程。除了集簇索引(Clustered Index),每一索引的使用都以磁盘容量作为代价。集簇索引是真正意义上与磁盘读取和磁盘容量代价无关的方法,因为集簇索引是真正按照顺序将数据存储到表格。当使用一个索引,数据库引擎必须执行两个数据读取,这两个数据读取是数据库记录所必需的。第一个数据被读取到实际数据指针的索引。第二个数据被读入到指针指定的位置。此时必须通过数据库服务器来查询,所以考虑系统资源消耗是有必要的。这也是查询分析器不使用索引的主要原因。在后面的部分中,即Covering Indices,你将学会不使用这两种读取的方法——然而,在很多时候使用索引即意味着每一记录可以完成两次读取。统计页统计页(Statistics page)是SQL Server用于决定是否使用索引时必需的信息。每一索引都有一个信息表,以将表格所有数据的索引关键值分布告诉查询优化器。统计页可用于大致估计从一个查询返回的行数。查询分析器必须知道返回的行数,由此确定是否值得使用索引方式。如果查询优化器从索引统计页中得知将返回几行,它就会选择使用索引;如果从统计页中得知将返回大数量的行数,索引查询优化器将有可能使用一个表扫描来解决查询。字段顺序当使用到索引时,字段顺序(Field order)代表众多字段的顺序。当判断是否使用索引时,服务器必须从第一字段到最后字段扫描。任何与查询无关的字段都将该索引清除掉。当进行索引安排时,你应该将最经常使用到的查询排列在索引最顶端,不属于查询范围的字段可以使查询优化器忽略整个索引。 使用WHERE语句WHERE语句是确定索引的选择语句的重要组成部分。WHERE语句过滤了显示记录的数量,也是查询优化器查找索引值的最容易的方法。WHERE语句的使用方法有很多种,以下为通常使用到的几种形式:匹配(相等)WHERE语句最为常用的例子就是一个记录或多个记录的匹配。当你指定一个特定字段等于一个值时,查询优化器将获知它要查询的索引入口,并识别满足查询条件的记录。这就大大地过滤读取记录的数量,从而减少查询所需要的时间。并且,查询分析器将可找到包含与匹配 *** 作有关的字段索引的位置。大于或小于虽然匹配和相等是最为普通的选择方式,而WHERE语句中的查询范围要求也是经常见到的。在这种情况下,查询分析器获知大于或者小于指定值的索引范围。通常,查询分析器可从多个独立语句中确定被读取的索引百分含量,并决定是否值得使用索引技术。函数在WHERE语句中使用函数可以限制索引查询的范围。查询分析器的查询结果难于确定,尤其在执行非常量字段的时候。所以,使用WHERE语句的函数将尽可能减少查询次数。使用ORDER BY语句一旦查询分析器以WHERE语句来判断,它将以ORDER BY语句而开始查询。如果查询优化器找到正确顺序行的相应索引,并且这一索引与WHERE条件相符合,优化器将会直接使用到索引技术。为了方便使用索引,ORDER BY语句不应该包含不必要的字段。查询分析器不能识别一个字段的表面意思,而ORDER BY语句可实现按照字段来排序。由此,如果你的ORDER BY语句中包括字段,优化器将会找到包含所有这些字段的索引。在ORDER BY语句中列出每一字段将有效地阻止查询优化器使用索引。详细索引(Covering indices)以上我提到查询分析器使用索引也会带来负面,所以有时候我们将不使用索引技术,特别是对于已经确定顺序的索引。比如,如果你从一个用户记录中选择User ID,First Name,LastName以及EmailAddress,你可获得包含所有这些字段的一个索引,然后查询分析器可以直接使用索引并读取数据表。此时,使用一个双向对照表(cross reference table)将特别有用。你可以在一个方向上使用一个集簇索引,然后在相反方向建立一个带有字段的索引。这样SQL服务器的第一个方向上可以使用物理表查询,而在相反方向上使用到索引技术。由于长关键字的原因,详细索引需要额外的空间和更多的时间。然而,如果你有一个参考表,详细索引能够有助于查询分析器更好地工作。帮助查询优化器当你提交一个查询之后,查询分析器的执行都必须通过很多环节。这些环节将有助于快速地获得结果。然而,通过在查询中指定你所需要的内容和建立正确的索引,即帮助查询优化器的 *** 作,以上过程才能顺利完成。欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)