什么是成本,即SQL进行查询的花费的时间成本,包含IO成本和CPU成本。
IO成本:即将数据页从硬盘中读取到内存中的读取时间成本。通常1页就是1.0的成本。
CPU成本:即是读取和检测是否满足条件的时间成本。0.2是每行的CPU成本。
我们对其进行分析的具体步骤如下:
计算IO成本:
Data_Length计算页的方法:Data_Length / (页的大小 = 16 * 1024 = 16KB)
计算CPU成本:
所以我们可以将其两个成本相加就是全表扫描的总成本。
如果我们选择的索引执行的条件是区间。
where key1 > 10 and key1 < 1000 # 在计算单个索引的成本时对于其他条件直接为true。
就会进入以下步骤
所以IO成本为1 + n * 1,CPU成本为n*0.2 + 0.01 + n * 0.2。
where key1 in (a,b,c,...,z)
当我们选择的索引的条件是上述的单点区间的情况时
我们查询n个单点区间。
最后找出成本最小的,选择对应方法执行SQL。
我们将这样从索引中找到最小左边界和最大右边界的过程计算索引的数量称为index dive。
当然我们找到一个大区间进行一次index dive,但是in(a,b,c...d)这样每一个参数都是一个单点区间,就要进行多次index dive。in里面的参数多起来,特别是in (sql) 嵌套子查询,就会使参数爆炸了,单点区间是导致超出index dive上线的主要原因。
MySQL有一个index dive的上限,默认值为200。
mysql> show variables like '%dive%';
+---------------------------+-------+
| Variable_name | Value |
+---------------------------+-------+
| eq_range_index_dive_limit | 200 |
+---------------------------+-------+
1 row in set, 1 warning (0.00 sec)
像上面我们利用索引计算范围的那种计算成本的方式,仅适用于区间范围数量小的情况下,当大于index dive的上限,就不能使用index dive了,就得使用索引的数据进行估算。
如何估算?
show index from 表名;
我们首先获得MySQL数据字典中统计的该表的Rows即行数,这个值是不准确的,是估计值。(后面解释)
然后通过上面语句获得的Cardinality列对应的索引的参数,即该索引列的基数,即索引列的值不重复的列的数量。
将Rows / Cardinality 就可以得到每个索引值重复行数的平均值。
我们根据每个值重复的数量,乘以单点区间的数量,就充当每个单点区间匹配的记录数。
对于驱动表的查询后的得到记录条数就叫做驱动表的扇出。
对于驱动表来说计算其最后记录的条数,当能用到索引直接使用索引计算其条数,对于用不到索引的情况呢,就只能进行猜,就是对其进行评估(启发式规则),最后得到驱动表的扇出。
然后我们要计算连接的成本,就需要确定连接的方式。
所以流程如下:
ps:内和外连接都是一样的,区别内连接需要确定哪个作为驱动表成本更低。
我们会知道如果两表连接时,驱动表的每一个结果行是作为一个常数传入被驱动表进行查询的。所以如果在连接条件上有索引的话,就可以加快连接,否则就要进行全表扫描。
当然了被驱动表的搜索条件能有索引那更好了。也能加快其计算出最后结果。
我在之前的总结文章中,有一个错误,就是我提出一个能不能将被驱动表在自身搜索条件筛选后应该缓存起来这个观点,其实是不对的,如果没有被驱动表自身搜索条件进行是没有意义的。而且因为驱动表的结果行也是作为一个参数的搜索条件连接的,然后一条一条的进行设置参数搜索被驱动表符合的结果行。
我们知道的从磁盘从IO到内存的成本常数是1.0
mysql> select * from mysql.server_cost;
+------------------------------+------------+---------------------+---------+---------------+
| cost_name | cost_value | last_update | comment | default_value |
+------------------------------+------------+---------------------+---------+---------------+
| disk_temptable_create_cost | NULL | 2020-12-17 14:54:07 | NULL | 20 |
| disk_temptable_row_cost | NULL | 2020-12-17 14:54:07 | NULL | 0.5 |
| key_compare_cost | NULL | 2020-12-17 14:54:07 | NULL | 0.05 |
| memory_temptable_create_cost | NULL | 2020-12-17 14:54:07 | NULL | 1 |
| memory_temptable_row_cost | NULL | 2020-12-17 14:54:07 | NULL | 0.1 |
| row_evaluate_cost | NULL | 2020-12-17 14:54:07 | NULL | 0.1 |
+------------------------------+------------+---------------------+---------+---------------+
6 rows in set (0.00 sec)
如果更新直接使用update语句即可
然后让系统刷新以下这个值 flush optimizer_costs;
mysql> select * from mysql.engine_cost;
+-------------+-------------+------------------------+------------+---------------------+---------+---------------+
| engine_name | device_type | cost_name | cost_value | last_update | comment | default_value |
+-------------+-------------+------------------------+------------+---------------------+---------+---------------+
| default | 0 | io_block_read_cost | NULL | 2020-12-17 14:54:07 | NULL | 1 |
| default | 0 | memory_block_read_cost | NULL | 2020-12-17 14:54:07 | NULL | 0.25 |
+-------------+-------------+------------------------+------------+---------------------+---------+---------------+
2 rows in set (0.00 sec)
我们在上面所过全表扫描计算成本时我们需要拿出表的Rows即行数这个参数,这一些关于表的,索引的行数等等被叫做统计数据。
MySQL有两种统计数据存储方式
两种模式,内存需要每次启动MySQL进行数据统计,然后关闭统计数据就消失了。默认还是磁盘的永久存储。
统计数据可以分为两个,一个是表的统计数据,一个是索引的统计数据。
mysql> show tables from mysql like '%innodb%';
+----------------------------+
| Tables_in_mysql (%innodb%) |
+----------------------------+
| innodb_index_stats | // 索引的统计数据
| innodb_table_stats | // 表的统计数据
+----------------------------+
2 rows in set (0.13 sec)
mysql> select * from mysql.innodb_table_stats;
+---------------+-----------------------------------------+---------------------+--------+----------------------+--------------------------+
| database_name | table_name | last_update | n_rows | clustered_index_size | sum_of_other_index_sizes |
+---------------+-----------------------------------------+---------------------+--------+----------------------+--------------------------+
| mall | cms_help | 2022-04-14 15:26:26 | 0 | 1 | 0 |
先取出几个叶子页面,然后计算这几个叶子节点行数的平均值。
然后乘以全部叶子的页面,就是全部的叶子节点数。这就是为什么不准确。
统计页面数,分为两个段,一个叶子段,一个非叶子段,从索引根节点找到两个段,然后从段的结构找出占用的页面数,流程如下。
sum_of_other_index_sizes 统计类似

统计项有如下:
在MySQL中,跟null的任何表达式都为null。
null值对于二级索引的不重复值来说有很大影响。对于index dive 来说就需要用到不重复值来作为评估成本的参数。
复习:当in(...)里面的参数太多,就不会执行index dive而是直接估计,查询不重复值然后除以总的记录数,就可以得到每个单点区间的大概值数。
mysql> show variables like 'innodb_stats_method';
+---------------------+-------------+
| Variable_name | Value |
+---------------------+-------------+
| innodb_stats_method | nulls_equal |
+---------------------+-------------+
1 row in set, 1 warning (0.08 sec)
对于null值来说,默认是认为所有的null都是相等的。
nulls_unequal : 所有null都不为相等的。
nulls_ignored : 直接把null忽略掉。
我正在用Ruby编写一个简单的程序来检查域列表是否被占用。基本上它循环遍历列表,并使用以下函数进行检查。require'rubygems'require'whois'defcheck_domain(domain)c=Whois::Client.newc.query("google.com").available?end程序不断出错(即使我在google.com中进行硬编码),并打印以下消息。鉴于该程序非常简单,我已经没有什么想法了-有什么建议吗?/Library/Ruby/Gems/1.8/gems/whois-2.0.2/lib/whois/server/adapters/base.
我知道我可以指定某些字段来使用pluck查询数据库。ids=Item.where('due_at但是我想知道,是否有一种方法可以指定我想避免从数据库查询的某些字段。某种反拔?posts=Post.where(published:true).do_not_lookup(:enormous_field) 最佳答案 Model#attribute_names应该返回列/属性数组。您可以排除其中一些并传递给pluck或select方法。像这样:posts=Post.where(published:true).select(Post.attr
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
目录第1题连续问题分析:解法:第2题分组问题分析:解法:第3题间隔连续问题分析:解法:第4题打折日期交叉问题分析:解法:第5题同时在线问题分析:解法:第1题连续问题如下数据为蚂蚁森林中用户领取的减少碳排放量iddtlowcarbon10012021-12-1212310022021-12-124510012021-12-134310012021-12-134510012021-12-132310022021-12-144510012021-12-1423010022021-12-154510012021-12-1523.......找出连续3天及以上减少碳排放量在100以上的用户分析:遇到这类
我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时
我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin
我在Rails上使用带有ruby的solr。一切正常,我只需要知道是否有任何现有代码来清理用户输入,比如以?开头的查询。或* 最佳答案 我不知道执行此操作的任何代码,但理论上可以通过查看parsingcodeinLucene来完成并搜索thrownewParseException(只有16个匹配!)。在实践中,我认为您最好只捕获代码中的任何solr异常并显示“无效查询”消息或类似信息。编辑:这里有几个“sanitizer”:http://pivotallabs.com/users/zach/blog/articles/937-s
我正在为锦标赛开发一个Rails应用程序。我在这个查询中使用了三个模型:classPlayertruehas_and_belongs_to_many:tournamentsclassTournament:destroyclassPlayerMatch"Player",:foreign_key=>"player_one"belongs_to:player_two,:class_name=>"Player",:foreign_key=>"player_two"在tournaments_controller的显示操作中,我调用以下查询:Tournament.where(:id=>params
我想用sunspot重现以下原始solr查询q=exact_term_text:fooORterm_textv:foo*ORalternate_text:bar*但我无法通过标准的太阳黑子界面理解这是否可能以及如何实现,因为看起来:fulltext方法似乎不接受多个文本/搜索字段参数我不知道将什么参数作为第一个参数传递给fulltext,就好像我通过了"foo"或"bar"结果不匹配如果我传递一个空参数,我得到一个q=*:*范围过滤器(例如with(:term).starting_with('foo*')(顾名思义)作为过滤器查询应用,因此不参与评分。似乎可以手动编写字符串(或者可能使
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi