【GreatSQL优化器-16】INDEX_SKIP_SCAN

Index Skip Scan 介绍 这种扫描方式就叫做 Index Skip Scan,之所以如此命名,是因为: - “Index”:因为 Index 具备数据有序存储的特征 - “Skip” :二分的方式来跳着查找,所以是 skip - “Scan”:这个技术背后的本质目的还是扫描出需要的所有数据,所以还是 Scan 除了求 distinct 值,Index Skip Scan 还可以用于多种场景: 阅读详情

【GreatSQL优化器-16】INDEX_SKIP_SCAN

一、INDEX_SKIP_SCAN介绍

GreatSQL 优化器的索引跳跃扫描(Index Skip Scan) 是一种优化查询的技术,尤其在联合索引中用于减少扫描的无效行数。它通过"跳跃"式的扫描方式,避免了对索引中无用部分的扫描,从而提升查询效率。这种技术适合特定场景,并有一定的优缺点。

索引跳跃扫描利用的是联合索引中非首列(非最左前缀)的索引列,来提高查询效率。例如,如果你有一个复合索引 (A, B),在传统的 B-Tree 索引中,只有当查询条件包含 A 列时,索引才会生效。但在跳跃扫描中,即使 A 没有出现在查询条件中,仍然可以通过扫描 B 列来有效查询。跳跃扫描会逐步扫描 A 列的每一个可能值,然后在每个 A 值下查找 B 列中符合条件的记录。这样避免了扫描大量无关记录,提升了查询性能。

下面用一个简单的例子来说明INDEX_SKIP_SCAN怎么应用在优化器。

CREATE TABLE t1(c1 INT, c2 INT, c3 INT, c4 INT);
CREATE UNIQUE INDEX i1_t1 ON t1(c1,c2,c3);
INSERT INTO t1 VALUES (1,1,1,1), (1,1,2,2), (1,3,3,3), (1,4,4,4), (1,5,5,5),
                      (2,1,1,1), (2,2,2,2), (2,3,3,3), (2,4,4,4), (2,5,5,5);
INSERT INTO t1 SELECT c1, c2, c3+5, c4+10  FROM t1;
INSERT INTO t1 SELECT c1, c2, c3+10, c4+20 FROM t1;
INSERT INTO t1 SELECT c1, c2, c3+20, c4+40 FROM t1;
INSERT INTO t1 SELECT c1, c2, c3+40, c4+80 FROM t1;
ANALYZE TABLE t1;

-- 下面的结果用到了skip scan
greatsql> EXPLAIN SELECT c1, c2 FROM t1 WHERE c2 > 40;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 10      | NULL |   53 |   100.00 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+

/*运行过程:
1、先找到第一列的不同值,得到结果为1和2
select distinct c1 from t1; 
2、根据第一列的分组结果在分组内执行范围扫描
select c1, c2 from t1 where c1 = 1 and c2 > 40;
select c1, c2 from t1 where c1 = 2 and c2 > 40;*/
二、get_best_skip_scan代码解释

skip scan 是根据条件生成 mm tree 以后,根据 mm tree 结果和索引进行判定能不能用 skip scan,其中联合索引中范围条件列之前的列为prefix列,即用来作为分组的依据的列,如果有多个除了第一列之外的范围列,那么选取遇到的第一个列之前的列为前缀进行分组。

int test_quick_select() {
  // 先判断skip_scan是否满足条件,不满足的话执行索引合并。
  AccessPath *skip_scan_path = get_best_skip_scan();
  // 不满足skip_scan执行索引合并,相关知识看上一节介绍
}

AccessPath *get_best_skip_scan() {
  // 先判断sql是否支持skip_scan,见表一
  // 接着开始执行skip_scan,遍历所有索引
  for (uint cur_param_idx = 0; cur_param_idx < param->keys; ++cur_param_idx) {
    // covering_keys代表可以使用的联合索引,sql涉及的所有列必须都在联合索引上才会有值
    if (!table->covering_keys.is_set(cur_index)) {
      cause = "query_references_nonkey_column";
      goto next_index;
    }
    // 从mm tree抽出当前联合索引相关的SEL_ROOT,如果没有那么就不能执行skip scan
    cur_index_range_tree = get_index_range_tree(cur_index, tree, param);
    // 遍历当前索引涉及的所有列
    for (cur_part = cur_index_info->key_part; cur_part != end_part;
         cur_part++, part++) {
      // 从mm tree获取当前列相关的SEL_ROOT,就是查询条件涉及的列
      get_sel_root_for_keypart();
      1、如果找到联合索引前缀列相关条件并且不是等号条件,那么就判断为"prefix_not_const_equality"并且跳到下一个索引
      2、如果找到联合索引前缀列以外的列相关条件,以遇到的第一个条件作为skip scan的条件。
    }
    // 针对联合索引的前缀列估计范围内包含的行数,用在条件包含前缀列有等号条件的情况
    quick_prefix_records = check_quick_select();
    // 计算skip_scan的行数和cost
    cost_skip_scan();
    // 最后生成AccessPath,包含IndexSkipScanParameters
  }
}
// 获取skip scan的行数和cost结果
void cost_skip_scan() {
  table_records = table->file->stats.records;
  // 如果可以根据前缀列估算出行数,那么就用估算的值,如果不能就用全表的行数。
  if (quick_prefix_records == HA_POS_ERROR)
    skip_scan_records = table_records;
  else
    skip_scan_records = quick_prefix_records;
  // 首先根据联合索引前缀列数值获取每个唯一数组包含的相同数的数量
  /* Compute the number of keys in a group. */
  if (index_info->has_records_per_key(distinct_key_parts - 1)) {
    keys_per_group = index_info->records_per_key(distinct_key_parts - 1);
  } else
    keys_per_group = guess_rec_per_key(table, index_info, distinct_key_parts);

  num_groups = (uint)(skip_scan_records / keys_per_group) + 1;
  num_groups = std::max(num_groups, 1U);

  // 接着根据可以用于skip scan的列,获取这个列的不同唯一数值包含的相同数的数量
    if (index_info->has_records_per_key(distinct_key_parts))
      keys_per_range = index_info->records_per_key(distinct_key_parts);
    else
      keys_per_range =
          guess_rec_per_key(table, index_info, distinct_key_parts + 1);
    // 
    double max_distinct_values = max(
        1.0, static_cast<double>(uint(keys_per_group) / uint(keys_per_range)));
   // 根据条件估算过滤系数
   float filtering_effect = where_cond->get_filtering_effect();
   // 计算得出的总行数
   *records = max(ha_rows(1), ha_rows(skip_scan_records * filtering_effect));
   // 最后估算IO cost和CPU cost
   Cost_estimate cost_skip_scan =
      table->file->index_scan_cost(key, num_groups, *records);
}

最后具体实现过程见代码IndexSkipScanIterator::Read(),这里不再展开赘述。

表一:不能使用 skip_scan 的场合

场合说明
sql语句带有ORDER DESC倒序排序不支持skip_scan
sql语句带有group by
没有生成mm tree条件必须带有列相关的范围
sql语句带有distinct
选择项包含聚合函数和DISTINCT
查询语句的Item包含不在联合索引的列
联合索引前缀列涉及的条件不为等号条件比如where c1<10 and c2>10
联合索引非前缀列涉及的条件生成的mm tree元素超过1个比如where c2>10 or c2<1

表二:索引判断状态迁移

索引状态说明
EQUALITY_KEYPART当前列是联合索引的前缀列,并且条件是等于条件
SKIPPED_KEYPART当前列是联合索引的非条件列,即当前列没有相关条件
RANGE_KEYPART当前列是联合索引的最终条件列,并且条件是单个范围,不是多个范围。
TRAILING_KEYPART状态结束

表三:skip index 使用场景

场景说明
联合索引查询当查询条件不包括索引的最左前缀列,而仅包括后面的列时,可以使用跳跃扫描。
低基数列查询对于列值种类少、重复率高的列,跳跃扫描可以减少扫描无效记录的时间。
避免额外索引当现有的联合索引足够支持查询,而不想为特定列额外创建索引时,跳跃扫描是一种权衡。

表四:skip index 优点

优点说明
提高查询效率对于联合索引,如果查询条件只涉及非最左前缀列,跳跃扫描能够提高查询效率,减少全表扫描的次数。
减少 I/O 操作通过避免扫描无效的索引行,跳跃扫描减少了对数据页的访问,从而节省了 I/O 操作。
降低索引空间要求在某些场景下,可以减少为查询额外建立索引的需求,因为即使只使用了非首列,跳跃扫描也能利用现有的复合索引。

表五:skip index 缺点

缺点说明
不适合高基数列跳跃扫描对低基数列(值不多但重复率高的列)有较好的效果。但如果参与跳跃扫描的列基数高,可能需要大量跳跃,反而影响效率。
无法替代覆盖索引对于那些经常查询的列,跳跃扫描并不能代替为每个列创建单独的索引。对于常用列,覆盖索引的效果会更好。
不适用于所有查询类型跳跃扫描仅在某些查询模式下有效,特别是当查询条件中不包含索引的最左前缀列时。如果最左列经常被查询,跳跃扫描无法发挥作用。

表六:相关关键词

关键词值
OPTIMIZER_SKIP_SCANON/OFF
SKIP_SCAN参数为表名,必须满足使用条件才生效,使用见下面例子
NO_SKIP_SCAN参数为表名和索引名,必须满足使用条件才生效,使用见下面例子
三、实际例子说明

接下来看几个例子来说明上面的代码:

greatsql> EXPLAIN SELECT c1, c2 FROM t1 WHERE c2 > 40;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 10      | NULL |   53 |   100.00 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
                  "skip_scan_range": {
                    "potential_skip_scan_indexes": [
                      {
                        "index": "i1_t1",
                        "tree_travel_cost": 0.4,
                        "num_groups": 3, -- 计算方式见下面
                        "rows": 53, -- 计算方式见下面
                        "cost": 11.483
                      }
                    ]
                  },
                  "best_skip_scan_summary": {
                    "type": "skip_scan",
                    "index": "i1_t1",
                    "key_parts_used_for_access": [
                      "c1",
                      "c2"
                    ],
                    "range": [
                      "40 < c2"
                    ],
                    "chosen": true
                  },

/* num_groups和rows计算过程:
skip_scan_records = table->file->stats.records = 160
keys_per_group = 条件涉及的第一个列的每个key包含的值 = 80
keys_per_range = 条件涉及列的每个key包含的值 = 17.7777786
num_groups = (skip_scan_records / keys_per_group) + 1 = 160 / 80 + 1 = 3   --->num_groups结果
max_distinct_values = keys_per_group / keys_per_range = 80 / 17.7777786 = 4
filtering_effect = max(1/4, COND_FILTER_INEQUALITY) = 0.333
records = skip_scan_records * filtering_effect = 160 * 0.333 = 53  --->rows结果 */

下面例子有的没用 skip scan 有的用了,区别仅在于包含的条件,根据条件最后生成的 mm tree 不同导致结果不同。

-- 查询语句的Item包含不在联合索引的列c4,因此被判定为"query_references_nonkey_column",不使用skip scan。
greatsql> EXPLAIN SELECT c1, c2,c3,c4 FROM t1 WHERE c3 >= 40 and c1<5;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | i1_t1         | NULL | NULL    | NULL |  160 |    33.33 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

-- 这个例子mm tree的c1<5因为不是等号条件因此被判断为"prefix_not_const_equality",因此不执行skip scan。
greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40 and c1<5;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | index | i1_t1         | i1_t1 | 15      | NULL |  160 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+

-- 这个例子mm tree只有一个40 <= c3,没有c1相关的tree,因此不需要判断索引前缀。
greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40 and 0<c1<3;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 15      | NULL |   53 |   100.00 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
/* ※0<c1<3不是标准条件,不支持生成mm tree,详情见https://bugs.mysql.com/bug.php?id=116515&thanks=4。这里可以改成c1 between 0 and 3 */

/*这个例子mm tree有两个40 <= c3和0<c1<3,0<c1<3被判断为"prefix_not_const_equality",因此不能用skip scan方式。*/

greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40 and 0<c1 and c1<3;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | index | i1_t1         | i1_t1 | 15      | NULL |  160 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+


-- 这个例子mm tree有两个:c1 = 5和40 <= c3,c1条件满足等号条件的要求,可以考虑用skip scan。
greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40 and c1=5;
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
| id | select_type | table | partitions | type | possible_keys | key   | key_len | ref   | rows | filtered | Extra                    |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | ref  | i1_t1         | i1_t1 | 5       | const |    1 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+------+---------------+-------+---------+-------+------+----------+--------------------------+
                  "skip_scan_range": {
                    "potential_skip_scan_indexes": [
                      {
                        "index": "i1_t1",
                        "tree_travel_cost": 0.4,
                        "num_groups": 1, -- 这里分组为1,因为c1=5前缀列算出来的skip_scan_records=1,因为不足1所以最后取1
                        "rows": 1,
                        "cost": 1.2
                      }
                    ]
                  },
                  "best_skip_scan_summary": {
                    "type": "skip_scan",
                    "index": "i1_t1",
                    "key_parts_used_for_access": [
                      "c1",
                      "c2",
                      "c3"
                    ],
                    "prefix ranges": [ -- 这里的prefix指的是条件出现的联合索引的第一列
                      "c1 = 5"
                    ],
                    "range": [ -- 这里的范围指的是可以用skip scan的列
                      "40 <= c3"
                    ],
                    "chosen": true

/* 这个例子mm tree有两个:c2=2和40 <= c3,skip scan只选除了联合索引第一个列以外遇到的第一个条件,因此最后用的是c2=2作为skip scan的条件。*/
greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40 and c2=2;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 10      | NULL |   40 |    33.33 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
                  "skip_scan_range": {
                    "potential_skip_scan_indexes": [
                      {
                        "index": "i1_t1",
                        "tree_travel_cost": 0.4,
                        "num_groups": 3, -- 这里分组为3,因为用c1来分组的,c1只有1和2两个不同值,因此结果为2+1=3
                        "rows": 40,
                        "cost": 8.67494
                      }
                    ]
                  },
                  "best_skip_scan_summary": {
                    "type": "skip_scan",
                    "index": "i1_t1",
                    "key_parts_used_for_access": [
                      "c1",
                      "c2"
                    ],
                    "range": [ -- 这里的范围指的是可以用skip scan的列。由于条件没有涉及第一列c1,因此没有出现"prefix ranges"选项。
                      "c2 = 2"
                    ],
                    "chosen": true
                    
-- 这个例子mm tree有1个:40 <= c3,最后用的是c1和c2作为前缀。
greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 15      | NULL |   53 |   100.00 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
                  "skip_scan_range": {
                    "potential_skip_scan_indexes": [
                      {
                        "index": "i1_t1",
                        "tree_travel_cost": 0.4,
                        "num_groups": 10, -- 这里公式=160 / 17.777 + 1 = 9+1 = 10,因为没有前缀条件可以过滤条件
                        "rows": 53,
                        "cost": 16.2332
                      }
                    ]
                  },
                  "best_skip_scan_summary": {
                    "type": "skip_scan",
                    "index": "i1_t1",
                    "key_parts_used_for_access": [
                      "c1",
                      "c2",
                      "c3"
                    ],
                    "range": [
                      "40 <= c3"
                    ],
                    "chosen": true
                  },
                  
-- 下面的例子因为出现了联合索引的所有列c1和c2和c3,因此不符合skip scan要求,不能用skip scan。
greatsql> EXPLAIN SELECT c1, c2,c3 FROM t1 WHERE c3 >= 40 and c1=1 and c2=2;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 15      | NULL |    1 |   100.00 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+

下面看一下用到 SKIP_SCAN 提示关键词的情况。下面这个例子没加关键字的时候因为 cost 没选择index skip scan,加上关键字之后就会强制走index skip scan

greatsql> EXPLAIN SELECT  /*+ SKIP_SCAN(t1) */ c1, c2,c3 FROM t1 WHERE c3 >= 40 and c1=5;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                                  |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | i1_t1         | i1_t1 | 15      | NULL |    1 |   100.00 | Using where; Using index for skip scan |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+----------------------------------------+

下面看一下用到 NO_SKIP_SCAN 提示关键词的情况。

greatsql> EXPLAIN SELECT /*+ NO_SKIP_SCAN(t1 i1_t1) */ c1, c2,c3 FROM t1 WHERE c3 >= 40;
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys | key   | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | index | NULL          | i1_t1 | 15      | NULL |  160 |    33.33 | Using where; Using index |
+----+-------------+-------+------------+-------+---------------+-------+---------+------+------+----------+--------------------------+
四、总结

从上面 skip index 的代码我们了解了跳跃扫描的使用条件和使用场景,以及使用规则,这个功能让一些以前用不到联合索引的查询也可以用到索引,提高了效率,但也有一定的局限和缺点,要看具体表的情况来决定。

基于Windows环境的OGG远程抽取异常(经典模式)OGG-02830 Formatting error MOS提供的解决方法,根据MOS的提示是由于使用的OGG版本太新了,经典模式下已经弃用了这种远程抽取方式。问题:Windows环境下启用远程抽取ADG备库的数据,在启动抽取进程的时候出现如下错误提示。 阅读详情

相关推荐

springboot源码 红色J_通达信副图指标源码之,出手就赢

今天跟大家分享一个通达信副图指标源码,本指标没有未来函数,因为本指标没有卖点提示,所以本指标需配合主图指标使用效果会更好,简单易懂,很容易明白,所以就不过多介绍使用方法了,以下是指标的贴图。风险提示!股市有风险买卖需谨慎,本指标只做参考建议,不做买卖交易结果,自己要对自己交易结果的盈亏负责,以下是指标的源码。VAR2:=REF(LOW,1);VAR3:=SMA(ABS(LOW-VAR2),3,1)...

weixin_39859909的博客 2847

mysql索引跳跃式扫描_索引跳跃式扫描(INDEX SKIP SCAN)

索引跳跃式扫描(INDEX SKIP SCAN)索引跳跃式扫描(INDEX SKIP SCAN)适用于所有类型的复合B树索引(包括唯一性索引和非唯一性索引),它使那些在where条件中没有对目标索引的前导列指定查询条件但同时又对该 索引的非前导列指定了查询条件的目标SQL依然可以用上该索引,这就像是在扫描该索引时跳过了它的前导列,直接从该索引的非前导列开始扫描一样(实际的执行过程并非如此),这也是...

weixin_42303003的博客 469

深度学习暑期科研项目(两个月发EI论文)

对本科生而言,越早接触系统的科研训练开始上手科研项目,就越能在未来的升学求职中占据很大的优势。暑假是提升个人简历、丰富科研经历的最佳时期!哈尔滨工业大学博士师兄也会在8月1日开始带大家上手科研项目,并撰写发表EI会议论文。

Magnolia_He的博客 1443

一次关于INDEX SKIP SCAN的SQL优化

客户发来一个比较长的SQL,说是跑了几小时还没出结果;这是制造业的一个统计型SQL,发来SQL文本一看SQL比较长要翻好几屏,一时半会也梳理不清;按照DBA的思路先尝试查看执行计划,看是否能通过索引或执行计划的改变在不修改SQL(也就不需要了解SQL业务逻辑)情况下优化;从执行计划来看cost 13676,具体的执行步骤在INDEX SKIP SCAN部分消耗比较大; 关于索引跳跃扫描inde...

还不算晕的专栏 4379

【机器学习实战中阶】音乐流派分类-自动化分类不同音乐风格

数据下载地址:https://www.kaggle.com/datasets/andradaolteanu/gtzan-dataset-music-genre-classification/data背景音乐。专家们长期以来一直在尝试理解声音以及是什么使一首歌曲与另一首不同。如何可视化声音。是什么使一个音调与另一个不同。这个数据集希望能提供实现这些目标的机会。内容原始流派 - 包含10种流派,每种流派有100个音频文件,每个文件长度为30秒(著名的GTZAN数据集,声音的MNIST)

2145

内网穿透-frp二层隧道搭建

环境: weblogic: 192.168.0.133 10.10.20.12 win7 : 10.10.20.7 10.10.10.7go语言、跨平台的 可以分为frpc和frps frpc—>请求----->frps frps:放到黑客公网上 1.需要出网 2.配置: frpc:放到肉鸡上,请求frps 2.端口转发 2.1frpc传到肉鸡(weblogic) 利用工具,传入内存马到weblogic,然后使用冰蝎进行连接 然后在文件管理中上传frpc.exe和frpc.ini文件 fr

yyytaotao的博客 2103

什么是INDEX SKIP SCAN

在查看SQL的执行计划的时候,有时候会看到INDEX SKIP SCAN,其实这个索引扫描路径会发生在多个列建立的复合索引上,如果SQL中谓词条件只包含索引中的部分列,并且这些列不是建立索引时的第一列时,就可 能发生INDEX SKIP SCAN。这里SKIP的意思是因为查询条件没有第一列或前面几列,被忽略了。 引用Oracle文档的例子,比如如下的表: employees (sex, e

mandy_rxr的专栏 1708

Java开发者必看:Apache PDFBox实现PDF数字签名完整指南(附源码)

本文为Java开发者提供了使用Apache PDFBox实现PDF数字签名的完整指南。详细介绍了从环境配置、核心原理到完整代码实现的全过程,涵盖证书管理、签名位置计算、性能优化及实际应用案例,并附有可直接复用的源码,帮助开发者高效集成PDF数字签名功能。

o1p2q3r的博客 340

oracle数据库SQL语句走INDEX SKIP SCAN性能问题

oracle,性能测试

shairvin的博客 1176

飞牛fnNAS虚拟机开启OVS导致失联的解决

摘要:用户在使用飞牛NAS时发现开启OVS网络功能后导致NAS失联。通过多种尝试(包括开关OVS、更换系统版本、手工配置、更换网卡、处理器检查等)后发现,OVS启用状态下DHCP无法获取IP地址,手动设置IP可暂时恢复网络。但重启后问题依旧,初步判断为OVS功能存在bug。目前临时解决方案是在需要时手动开关OVS,或考虑其他虚拟化方案。用户计划后续探索替代方案,并等待官方修复该问题。

dgnankai的专栏 4334

YOLOv8改进,YOLOv8添加DCNv3可变性卷积,无需编译

DCNv3在DCNv2版本上进行了改进,上一期我们在YOLOv8中添加了DCNv2,这一期我们将添加DCNv3,DCNv3代码详解可以参考链接:代码详解——可变形卷积(DCNv3)

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 5021

Index Skip Scan

Index Skip Scan

lltfjsy的博客 1087

华为OD机试双机位C卷-水库溃坝填补(Py/Java/C/C++/Js/Go)

华为OD机试(机考)双机位C卷 【水库溃坝填补】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。水库溃坝填补。

qq_45776114的博客 2109

深入理解Oracle索引(1):INDEX SKIP SCAN 和 INDEX RANGE SCAN

㈠ Index SKIP SCAN当表有一个复合索引,而在查询中有除了索引中第一列的其他列作为条件,并且优化器模式为CBO,这时候查询计划就有可能使用到SS Skip scan会探测出索引前导列的唯一值个数,每个唯一值都会作为常规扫描的入口,在此基础上做一次查找,最后合并这些查询 例如:表employees (sex, employee_id, address) ,有一个组合索引(s...

数据库 947

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

索引跳跃式扫描(INDEX SKIP SCAN)

执行计划中存在索引跳扫步骤 存在索引跳扫说明查询条件和索引定义不严格匹配 对于组合索引来说,在oracle8i之前,如果where字句中没有组合索引的前导列(即建立索引时排在第一位的字段)的话,这个组合索引在SQL语句中是不起任何作用的,在oracle9i中引入了跳跃式扫描,也就是如果在where字句中没有前导列的话也可以使用组合索引后面的列进行索引扫描,条件就是前导列的distinct的值不能...

凉茶微凉 2124

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

INDEX SKIP SCAN

1.交待一下背景:交易明细表,18亿条数据,表的容量大小是:数据量有480G,联合主键索引是112G。联合主键索引是这样建立的: 点击(此处)折叠或打开 ...

cll3830995的博客 572

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

MySQL中8.0为啥引入索引跳跃扫描(Index Skip Scan)

是一种优化查询的技术,尤其在中用于减少扫描的无效行数。它通过"跳跃"式的扫描方式,避免了对索引中无用部分的扫描,从而提升查询效率。这种技术适合特定场景,并有一定的优缺点。索引跳跃扫描利用的是联合索引中非首列(非最左前缀)的索引列,来提高查询效率。例如,如果你有一个复合索引(A, B),在传统的 B-Tree 索引中,只有当查询条件包含A列时,索引才会生效。但在跳跃扫描中,即使A没有出现在查询条件中,仍然可以通过扫描 B 列来有效查询。跳跃扫描会逐步扫描A列的每一个可能值,然后在每个A值下查找B。

hikktn的博客 2697

数据库性能之旅(三)index skip scan

Index Skip Scan是一种优化技术,用于解决复合索引前导列缺失时的查询效率问题。当查询条件不包含复合索引的前导列但后续列选择性高时,数据库会将查询逻辑拆分为多个子查询(如前导列不同值),通过合并结果避免全表扫描。Oracle、MySQL 8.0、PostgreSQL 18、达梦和OceanBase等数据库均支持该功能,实现方式各有特点:Oracle明确显示INDEX SKIP SCAN操作,PostgreSQL通过增强索引扫描实现,MySQL 8.0直接引入该特性。该技术适用于前导列基数低、后续列

tiojnhiinnb的博客 851

oracle skipscan,深入懂得Oracle索引(1):INDEX SKIP SCAN 和 INDEX RANGE SCAN

深入理解Oracle索引(1):INDEX SKIP SCAN 和 INDEX RANGE SCAN㈠ Index SKIP SCAN当表有一个复合索引,而在查询中有除了索引中第一列的其他列作为条件,并且优化器模式为CBO,这时候查询计划就有可能使用到SSSkip scan会探测出索引前导列的唯一值个数,每个唯一值都会作为常规扫描的入口,在此基础上做一次查找,最后合并这些查询例如:表employe...

weixin_42515045的博客 1338

mysql索引跳跃扫描_MySQL 8.0 之索引跳跃扫描(Index Skip Scan)

前言MySQL 8.0.13开始支持 index skip scan 也即索引跳跃扫描。该优化方式支持那些SQL在不符合组合索引最左前缀的原则的情况,优化器依然能组使用组合索引。talk is cheap ,show me the code实践使用官方文档的例子,构造数据mysql> CREATE TABLE t1 (f1 INT NOT NULL, f2 INT NOT NULL, PRI...

weixin_30249465的博客 485

INDEX SKIP SCAN和INDEX FULL SCAN

INDEX SKIP SCAN:跳跃式索引能够通过使用非前缀列提高索引的查询性能.通常情况下,扫描索引块比扫描表数据块快. 跳跃式索引能够使复合索引逻辑上拆分分一个个小的子索引.在跳跃式扫描中,复合索引的首列不会被限定在查询中,因为它是跳跃式的. 数据库确定了一定数量的逻辑子索引通过在首列的去重的值中.当数据库中复合索引的前导列有很少的去重的值和索引中有非前导列有很多重复的值时跳跃式扫描就会有

elvis的Oracle Lab 4131

关于INDEX SKIP SCAN

INDEX SKIP SCAN是9i之后提供的新功能,实现了即使WHERE条件中不存在索引的前导列也可以使用索引。但是INDEX SKIP SCAN是需要下面几个条件的: 1,版本9i及以后 2,CBO(表和索引都经过分析) 3,索引的前导列重复值很少(选择性低) 4,WHERE条件中不存在索引前导列 举例: 1,    建立测试环境 Sql>select * from v$vers

oratea的专栏 1087

index range scan,index fast full scan,index skip scan发生的条件

index range scan(索引范围扫描):1.对于unique index来说,如果where 条件后面出现了 ,between ...and...的时候,那么就可能执行index range scan,如果where条件后面是=,那么就会执行index unique scan。2.对于none unique index来说 如果where 条件后面出现了=,>,3.对于组合索

落落的专栏 专注SQL调优 性能调优 1万+

kudu index skip scan优化

Index skip scan可以在缺失主键第一列的情况下,自动补全主键第一列,将全表扫描转换成多次主键扫描。但是显而易见如果主键第一列的取值非常多,反而会导致性能下降。目前kudu中该优化默认开启,并且没有判断主键第一列取值数以决定是否关闭该优化的能力。另外目前只支持=谓词,尚且不支持范围谓词,以及IN谓词。查询条件落在tstamp列上,缺失了主键第一列,所以无法走到主键扫描。但是kudu可以可以根据主键索引得到第一列host的具体值,补全第一列后再进行查询。依次遍历所有host值,得到所有结果。...

badboynt的博客 562

mysql索引跳跃式扫描_跳跃式索引(Skip Scan Index)的浅析[转]

在Oracle9i中,有一个新的特性:跳跃式索引(Skip Scan Index)。当表有一个复合索引,而在查询中有除了索引中第一列的其他列作为条件,并且优化器模式为CBO,这时候查询计划就有可能使用到SS。此外,还可以通过使用提示index_ss(CBO下)来强制使用SS。举例:SQL> create table test1 (a number, b char(10), c varchar...

weixin_36326243的博客 1270

索引跳跃式扫描

索引跳跃式扫描(INDEX SKIP SCAN)适用于所有类型的复合B树索引(包括唯一性索引和非唯一性索引),它使那些在where条件中没有对目标索引的前导列指定查询条件但同时又对该 索引的非前导列指定了查询条件的目标SQL依然可以用上该索引,这就像是在扫描该索引时跳过了它的前导列,直接从该索引的非前导列开始扫描一样(实际的执行过程并非如此),这也是索引跳跃式扫描中"跳跃"(SKIP)一词的含义。 为什么在where条件中没有对目标索引的前导列指定查询条件但Oracle依然可以用上该索引呢?这是因为Orac

weixin_43626356的博客 1945
上一篇: GreatSQL 8.0.32-27 GA (2025-3-10)
下一篇: 优化GreatSQL日志文件空间占用
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值