【GreatSQL优化器-15】index merge

肺癌术后随访离散选择实验(DCE)设计与SAS实现 随着肺癌早期诊断和治疗技术的进步,肺癌患者的生存率显著提高,术后随访管理成为提升患者生活质量的关键环节。然而,如何在有限的医疗资源下设计出既符合患者需求又具有成本效益的随访方案,是临床决策面临的重要挑战。 离散选择实验(Discrete Choice Experiment, DCE)是一种定量研究方法,通过模拟真实世界的选择情境,测量患者对不同随访方案属性的偏好程度。本研究旨在通过DCE方法,系统评估肺癌术后患者对随访服务的偏好,为制定以患者为中心的随访策略提供循证依据。 阅读详情

【GreatSQL优化器-15】index merge

一、index merge介绍

GreatSQL的优化器的Index Merge Optimization是查询优化器在处理复杂查询时使用的一种高级技术。当查询的 WHERE 子句中有多个独立的条件,且每个条件都可以使用不同的索引时,优化器会尝试将这些索引合并起来,以提高查询效率。这种优化策略允许数据库在一个查询中同时使用多个索引,从而避免全表扫描或减少需要扫描的数据量。

在某些情况下,单独使用任何一个索引都无法高效地获取到完整的结果集。而通过合并多个索引的扫描结果,我们可以更精确地定位到满足所有条件的记录,从而提高查询效率。当优化器生成mm tree的时候会保存不同索引的tree信息,生成mm tree之后会基于OR或者AND条件进行索引并集合并或者交集合并,从而实现index merge。

下面用一个简单的例子来说明索引合并是什么。

greatsql> CREATE TABLE t1 (c1 INT PRIMARY KEY, c2 INT,date1 DATETIME);
greatsql> INSERT INTO t1 VALUES (1,10,'2021-03-25 16:44:00.123456'),(2,1,'2022-03-26 16:44:00.123456'),(3,4,'2023-03-27 16:44:00.123456'),(5,5,'2024-03-25 16:44:00.123456'),(7,null,'2020-03-25 16:44:00.123456'),(8,10,'2020-10-25 16:44:00.123456'),(11,16,'2023-03-25 16:44:00.123456');
greatsql> CREATE TABLE t2 (cc1 INT, cc2 INT,cc3 int);
greatsql> INSERT INTO t2 VALUES (1,3,1),(2,1,4),(3,2,10),(4,3,4),(5,15,10),(1,10,3),(4,4,1),(6,4,9),(11,110,1);
greatsql> CREATE INDEX idx1 ON t1(c2);
greatsql> CREATE INDEX idx2 ON t1(c2,date1);
greatsql> CREATE INDEX idx2_1 ON t2(cc1);
greatsql> CREATE INDEX idx2_2 ON t2(cc2);
greatsql> CREATE INDEX idx2_3 ON t2(cc3);

greatsql>  explain SELECT * FROM t2 where cc2=3 and cc1=1 and cc3=1;
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys        | key           | key_len | ref  | rows | filtered | Extra                                       |
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
|  1 | SIMPLE      | t2    | NULL       | index_merge | idx2_1,idx2_2,idx2_3 | idx2_1,idx2_2 | 5,5     | NULL |    1 |    33.33 | Using intersect(idx2_1,idx2_2); Using where |
这里用到了索引交集合并
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
二、test_quick_select代码解释

首先了解一下ROR的定义,ROR 的含义是 Rowid-Ordered Retrieval,表示单个索引返回的结果集是按照主键有序排列的。索引交集和并集是对ROR的索引进行操作,而如果有非ROR索引的话就要执行排序并集操作。

不是所有涉及不同mm tree的查询最后都会走索引合并,还是要取决于cost大小,有时候全表扫描反而cost更小。例子见下面第三节。

int test_quick_select() {
  // 先判断skip_scan是否满足条件,不满足的话执行索引合并。skip_scan下一期介绍
  AccessPath *skip_scan_path = get_best_skip_scan();
  // 不满足skip_scan执行索引合并
  if (tree && (best_path == nullptr || !get_forced_by_hint(best_path))) {
    // 获取is_ror_scan值与table->quick_rows[keynr]区间范围行数,is_ror_scan为true才能执行索引合并,取值见表二
    get_key_scans_params();
    // 执行索引交集合并,计算cost并跟全表扫描对比,选取cost低的path
    AccessPath *rori_path = get_best_ror_intersect(
            thd, &param, table, index_merge_intersect_allowed, tree,
            &needed_fields, best_cost,
            /*force_index_merge_result=*/true, /*reuse_handler=*/true);
    // tree->merges在tree_or()的时候赋值,赋值条件见表二
    if (!tree->merges.is_empty()) {
      // 按照不同索引组执行索引并集合并,计算cost并跟全表扫描对比,选取cost低的path
      for (SEL_IMERGE &imerge : tree->merges) {
          new_conj_path = get_best_disjunct_quick(
              thd, &param, table, index_merge_union_allowed,
              index_merge_sort_union_allowed, index_merge_intersect_allowed,
              skip_records_in_range, &needed_fields, &imerge, best_cost,
              needed_reg);
      }
    }
  }
}

// 执行索引交集合并
AccessPath *get_best_ror_intersect() {
  // 遍历mm tree的索引数组,给tree->ror_scans数组和cpk_scan赋值,cpk_scan专门存放主键索引信息
  for (idx = 0, cur_ror_scan = tree->ror_scans; idx < param->keys; idx++) {
    tree->ror_scans = make_ror_scan();
  }
  // 对tree->ror_scans数组根据覆盖列个数和范围包含的行数进行排序,范围包含的行数越少排序越前。这个步骤排除了主键索引,因为主键在函数最后单独处理
  // 排序函数见下面is_better_intersect_match函数
  find_intersect_order(tree->ror_scans, tree->ror_scans_end, param,
                       needed_fields);
  // 按照上面的索引排序顺序进行交集操作,可以减少cost的索引进行交集操作,不能减少的排除
  while (cur_ror_scan != tree->ror_scans_end && !intersect->is_covering) {
    // 计算除了主键以外的所有索引的过滤系数、行数、cost并且累加到intersect变量
    ror_intersect_add(intersect);
  }
  // 计算主键列的过滤系数、行数、cost并与之前别的索引结果累加到intersect变量
  if (cpk_scan) ror_intersect_add(intersect);
  // 最后生成AccessPath
  AccessPath *path = new (param->return_mem_root) AccessPath;
}

// 对索引进行排序,可以看到覆盖的列越少,包含的行数越少,排序越靠前
static bool is_better_intersect_match(const ROR_SCAN_INFO *scan1,
                                      const ROR_SCAN_INFO *scan2) {
  if (scan1 == scan2) return false;

  if (scan1->num_covered_fields_remaining > scan2->num_covered_fields_remaining)
    return false;

  if (scan1->num_covered_fields_remaining < scan2->num_covered_fields_remaining)
    return true;

  return (scan1->records > scan2->records);
}

// 执行索引并集合并
static AccessPath *get_best_disjunct_quick() {
  // 按照索引遍历所有tree
  for (auto tree_it = imerge->trees.begin(); tree_it != imerge->trees.end();
         ++tree_it, cur_child++) {
    // 获取is_ror_scan值与table->quick_rows[keynr]区间范围行数,is_ror_scan为true才能执行索引合并,取值见表二
    get_key_scans_params();
  }
  // 如果所有索引都是ROR的,那么直接返回结果
  if (all_scans_rors && (index_merge_union_allowed || force_index_merge))
    return get_ror_union_path();
  // 如果不是所有索引都是ROR的,那么需要执行Sort-Union
  // 首先计算磁盘扫描的cost
  get_sweep_read_cost();
  // 如果扫描磁盘cost太大,那么继续执行Sort-Union
  // 索引去重cost估计
  dup_removal_cost = Unique::get_use_cost(
        (uint)non_cpk_scan_records, table->file->ref_length,
        // 这个系统变量见表七
        thd->variables.sortbuff_size, cost_model);
  // 执行索引Sort-Union
  get_ror_union_path();
}

static AccessPath *get_ror_union_path() {
  // 遍历所有tree元素,对每个元素执行Intersection Merge
  for (auto tree_it = imerge->trees.begin(); tree_it != imerge->trees.end();
         tree_it++, cur_child++, cur_roru_plan++) {
    get_best_ror_intersect();
  }
  // 计算磁盘扫描cost
  get_sweep_read_cost();
  // 生成AccessPath,这个AccessPath带有child即Intersection Merge的索引子集
  AccessPath *path = new (param->return_mem_root) AccessPath;
  path->rowid_union().children = children;
}

表一:索引合并类型

索引合并类型说明对应代码举例
交集合并(Intersection Merge)当查询需要满足多个条件(使用 AND 连接),并且每个条件都可以使用不同的索引时,分别扫描这些索引,然后取结果的交集。原则是通过mm tree能找到唯一的那条数据的时候才执行交集get_best_ror_intersect()ROR条件 and ROR条件
并集合并(Union Merge)查询可能只需要满足多个条件中的任意一个(使用 OR 连接)。分别扫描这些索引,然后取结果的并集。被合并的子集也可以是索引交集比如union(intersect(),intersect()),例子见下面get_best_disjunct_quick()ROR条件 or ROR条件
排序并集合并(Sort-Union Merge)需要对结果进行排序,并且排序的字段也有索引时。分别扫描索引,然后合并并排序结果。这个cost在三者中最大因为要排序get_best_disjunct_quick() 排序执行过程:通过IndexMergeIterator::Init()的unique->unique_add进行排序非ROR条件 OR 非ROR条件,比如有非唯一索引列范围条件,where key1 < 3 or key2 > 1020

表二:is_ror_scan取值情况

条件is_ror_scan值赋值的函数说明
赋初始值!(file->index_flags(keynr, 0, true) & HA_KEY_SCAN_NOT_ROR)check_quick_select()初值
索引是倒序的falsecheck_quick_select()
索引类型不等于SE_SPECIFIC和BTREE或者索引包含虚拟列falsecheck_quick_select()
索引是主键列truecheck_quick_select()
列的第二个以上条件有右节点falsesel_arg_range_seq_next()见下面例子
tree叶节点范围最大值和最小值不相等falsesel_arg_range_seq_next()见下面例子
不是等号条件或者不为is_key_scan_ror()falsesel_arg_range_seq_next()等于条件的is_ror_scan=true is_key_scan_ror()比较索引列长度和插入数据的长度,不一致的话就返回false。比如创建索引字符集和插入数据的字符集不一致就会导致该列无法用index merge。

注:is_ror_scan原则就是通过条件可以确定唯一的位置,这就是ROR有序的含义

表三:tree->merges数组赋值条件

条件(以下必须全部满足)举例
不同索引的OR条件where d2<5 or d1=10,这两个条件涉及2个不同索引
两个SEL_TREE不能被合并where (d2=5 and d1=5) or (d2=4),这两个OR条件里面带有相同的索引d2,因此不能加入merges数组

表四:不同索引合并方法行数计算方法

索引合并类型公式
交集合并(Intersection Merge)总行数 * (第1个索引范围对应的行数 / 总行数) * (第2个索引范围对应的行数 / 总行数) …
并集合并(Union Merge)第1个索引范围对应的行数+第2个索引范围对应的行数 …
排序并集合并(Sort-Union Merge)第1个索引范围对应的行数+第2个索引范围对应的行数 …

表五:跟索引合并相关的OPTIMIZER_SWITCH

OPTIMIZER_SWITCH默认说明
OPTIMIZER_SWITCH_INDEX_MERGEON可以在多个索引上进行查询,并将结果合并返回。
OPTIMIZER_SWITCH_INDEX_MERGE_UNIONOFF会在使用到的多个索引上同时进行扫描,并取这些扫描结果的并集作为最终结果集。
OPTIMIZER_SWITCH_INDEX_MERGE_SORT_UNIONON比单纯的Union索引合并多了一步对二级索引记录的主键id排序的过程。
OPTIMIZER_SWITCH_INDEX_MERGE_INTERSECTON会在使用到的多个索引上同时进行扫描,并取这些扫描结果的交集作为最终结果集。

表六:索引扫描类型

类型说明trace显示
INDEX_RANGE_SCAN范围扫描range_scan
INDEX_MERGE排序并集合并模式index_merge
ROWID_INTERSECTION交集合并模式index_roworder_intersect
ROWID_UNION并集合并模式index_roworder_union
INDEX_SKIP_SCAN使用skip scan方式进行范围扫描,当要查询的列都在索引中时,即使where中的条件不是索引的第一部分,也可以使用索引。skip_scan
GROUP_INDEX_SKIP_SCAN用于group by的索引跳跃index_group

表七:涉及的系统变量

系统变量说明
thd->variables.sortbuff_size用在索引Sort-Union操作的时候对索引进行排序去重估计cost
OPTIMIZER_SWITCH_FAVOR_RANGE_SCAN索引合并执行在get_key_scans_params()的时候,如果这个设置为true,那么会对rows对应算出来的cost乘以系数0.1,让cost结果小十倍,这样结果就不会走索引合并而走RANGE_SCAN,见下面例子
三、实际例子说明

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

交集合并

greatsql> EXPLAIN SELECT * FROM t1 where c2=10 and c1<10;
+----+-------------+-------+------------+-------------+-------------------+--------------+---------+------+------+----------+--------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys     | key          | key_len | ref  | rows | filtered | Extra                                      |
+----+-------------+-------+------------+-------------+-------------------+--------------+---------+------+------+----------+--------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | index_merge | PRIMARY,idx1,idx2 | idx1,PRIMARY | 9,4     | NULL |    1 |    58.33 | Using intersect(idx1,PRIMARY); Using where |
+----+-------------+-------+------------+-------------+-------------------+--------------+---------+------+------+----------+--------------------------------------------+
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "PRIMARY",
                        "ranges": [
                          "c1 < 10"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true, 这里为true说明这个索引是ROR的
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 6,
                        "cost": 0.861465,
                        "chosen": true
                      },
                      {
                        "index": "idx1",
                        "ranges": [
                          "c2 = 10 AND c1 < 10"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true, 这里为true说明这个索引是ROR的
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 2,
                        "cost": 0.96,
                        "chosen": false,
                        "cause": "cost"
                      },
                      {
                        "index": "idx2",
                        "ranges": [
                          "c2 = 10"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": false, 这里为false说明这个索引不可以被合并
                        "using_mrr": false,
                        "index_only": true,
                        "in_memory": 0,
                        "rows": 2,
                        "cost": 1.21183,
                        "chosen": false,
                        "cause": "cost"
                      }
                    ],
                    "analyzing_roworder_intersect": {
                      "intersecting_indexes": [ 上面"rowid_ordered"数量加起来为2,因此可以执行索引交集
                        {
                          "index": "idx1",
                          "index_scan_cost": 0.250274, 满足c2 = 10行数2条,这个是对应2条的cost
                          "cumulated_index_scan_cost": 0.250274,
                          "disk_sweep_cost": 0.4375, 从硬盘读取数据的cost
                          "cumulated_total_cost": 0.687774, 这个值=index_scan_cost+disk_sweep_cost
                          "usable": true,
                          "matching_rows_now": 2,
                          "isect_covering_with_this_index": false,
                          "chosen": true
                        }
                      ],
                      "clustered_pk": {
                        "index_scan_cost": 0.1,
                        "cumulated_index_scan_cost": 0.350274, 这个值=index_scan_cost+上一个cumulated_index_scan_cost
                        "disk_sweep_cost": 0.25,
                        "clustered_pk_scan_added_to_intersect": true,
                        "cumulated_cost": 0.600274 这个值=disk_sweep_cost+cumulated_index_scan_cost,这里取两个索引交集以后的cost小于单独用idx1索引的cost 0.687774,说明索引交集确实提高了效率
                      },
                      "rows": 1.71429, 这个值=索引行数 * 过滤系数 =7 * 2/7 * 6/7,其中2是满足idx1条件c2=10的行数,6是满足主键列c1<10的行数
                      "cost": 0.600274,
                      "covering": false,
                      "chosen": true
                    }
                  },
                  "chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "index_roworder_intersect",
                      "rows": 1.71429,
                      "cost": 0.600274,
                      "covering": false,
                      "clustered_pk_scan": true,
                      "intersect_of": [
                        {
                          "type": "range_scan",
                          "index": "idx1",
                          "rows": 2,
                          "ranges": [
                            "c2 = 10 AND c1 < 10"
                          ]
                        }
                      ]
                    },
                    "rows_for_plan": 1.71429,
                    "cost_for_plan": 0.600274,
                    "chosen": true
                  }
                }
              }
          {
            "considered_execution_plans": [
              {
                "plan_prefix": [
                ],
                "table": "`t1`",
                "best_access_path": {
                  "considered_access_paths": [
                    {
                      "access_type": "ref",
                      "index": "idx1",
                      "chosen": false,
                      "cause": "range_uses_more_keyparts"
                    },
                    {
                      "access_type": "ref", 这个是因为keyuse_array数组有值,所以根据索引单独计算
                      "index": "idx2",
                      "rows": 2,
                      "cost": 1.20183,
                      "chosen": true
                    },
                    {
                      "rows_to_scan": 1, 这个值等于上面索引交集算出来的"rows_for_plan": 1.71429
                      "filtering_effect": [
                      ],
                      "final_filtering_effect": 1,
                      "access_type": "range",
                      "range_details": {
                        "used_index": "intersect(idx1,PRIMARY)"
                      },
                      "resulting_rows": 1, 这个值等于"rows_to_scan"
                      "cost": 0.700274,
                      "chosen": true
                    }
                  ]
                },
                "condition_filtering_pct": 100,
                "rows_for_plan": 1,
                "cost_for_plan": 0.700274, 最后结果:在上面ref和range两种方法中选择了cost低的range方法即索引交集方法
                "chosen": true
              }
            ]
          },

并集合并

greatsql> CREATE TABLE t4 (d1 INT, d2 int, d3 varchar(100));
greatsql> INSERT INTO t4 VALUES (1,2,'aa1'),(2,1,'bb1'),(2,3,'cc1'),(3,3,'cc1'),(4,2,'ff1'),(4,4,'ert'),(4,2,'f5fg'),(null,2,'ee'),(5,30,'cc1'),(5,4,'fcc1'),(4,10,'cc1'),(6,4,'ccd1'),(null,1,'fee'),(1,2,'aa1'),(2,1,'bb1'),(2,3,'cc1'),(3,3,'cc1'),(4,2,'ff1'),(4,4,'ert'),(4,2,'f5fg'),(null,2,'ee'),(5,30,'cc1'),(5,4,'fcc1'),(4,10,'cc1'),(6,4,'ccd1'),(null,1,'fee'),(1,2,'aa1'),(2,1,'bb1'),(2,3,'cc1'),(3,3,'cc1'),(4,2,'ff1'),(4,4,'ert'),(4,2,'f5fg'),(null,2,'ee'),(5,30,'cc1'),(5,4,'fcc1'),(4,10,'cc1'),(6,4,'ccd1'),(null,1,'fee');
greatsql> CREATE INDEX idx4_1 ON t4(d1);
greatsql> CREATE INDEX idx4_2 ON t4(d2);

greatsql> EXPLAIN SELECT * FROM t4 where d2=4 or d1=10;
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
| id | select_type | table | partitions | type        | possible_keys | key           | key_len | ref  | rows | filtered | Extra                                   |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
|  1 | SIMPLE      | t4    | NULL       | index_merge | idx4_2,idx4_1 | idx4_2,idx4_1 | 5,5     | NULL |   10 |   100.00 | Using union(idx4_2,idx4_1); Using where |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+-----------------------------------------+
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                    ],
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  },
                  "analyzing_index_merge_union": [
                    {
                      "indexes_to_merge": [
                        {
                          "range_scan_alternatives": [
                            {
                              "index": "idx4_2",
                              "ranges": [
                                "d2 = 4"
                              ],
                              "index_dives_for_eq_ranges": true,
                              "rowid_ordered": true, 这里为true说明这个索引是ROR的
                              "using_mrr": false,
                              "index_only": true,
                              "in_memory": 0,
                              "rows": 9,
                              "cost": 1.92074,
                              "chosen": true
                            }
                          ],
                          "index_to_merge": "idx4_2",
                          "cumulated_cost": 1.92074
                        },
                        {
                          "range_scan_alternatives": [
                            {
                              "index": "idx4_1",
                              "ranges": [
                                "d1 = 10"
                              ],
                              "index_dives_for_eq_ranges": true,
                              "rowid_ordered": true, 这里为true说明这个索引是ROR的
                              "using_mrr": false,
                              "index_only": true,
                              "in_memory": 0,
                              "rows": 1,
                              "cost": 1.11,
                              "chosen": true
                            }
                          ],
                          "index_to_merge": "idx4_1",
                          "cumulated_cost": 3.03074 这个值=idx4_1索引对应的cost+idx4_2索引对应的cost=1.11+1.92074
                        }
                      ],
                      "cost_of_reading_ranges": 3.03074,
                      "use_roworder_union": true,
                      "cause": "always_cheaper_than_not_roworder_retrieval",
                      "analyzing_roworder_scans": [
                        {
                          "type": "range_scan",
                          "index": "idx4_2",
                          "rows": 9,
                          "ranges": [
                            "d2 = 4"
                          ],
                          "analyzing_roworder_intersect": {
                            "usable": false,
                            "cause": "too_few_roworder_scans"
                          }
                        },
                        {
                          "type": "range_scan",
                          "index": "idx4_1",
                          "rows": 1,
                          "ranges": [
                            "d1 = 10"
                          ],
                          "analyzing_roworder_intersect": {
                            "usable": false,
                            "cause": "too_few_roworder_scans"
                          }
                        }
                      ],
                      "index_roworder_union_cost": 4.47442,
                      "members": 2, 涉及2个索引
                      "chosen": true
                    }
                  ],
                  "chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "index_roworder_union",
                      "union_of": [
                        {
                          "type": "range_scan",
                          "index": "idx4_2",
                          "rows": 9,
                          "ranges": [
                            "d2 = 4"
                          ]
                        },
                        {
                          "type": "range_scan",
                          "index": "idx4_1",
                          "rows": 1,
                          "ranges": [
                            "d1 = 10"
                          ]
                        }
                      ]
                    },
                    "rows_for_plan": 10,
                    "cost_for_plan": 4.47442,
                    "chosen": true
                  }
                }
              }
            ]
          },

排序合并

greatsql> EXPLAIN SELECT * FROM t4 WHERE d1 < 2 OR d2 > 20;
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+----------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys | key           | key_len | ref  | rows | filtered | Extra                                        |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+----------------------------------------------+
|  1 | SIMPLE      | t4    | NULL       | index_merge | idx4_2,idx4_1 | idx4_1,idx4_2 | 5,5     | NULL |    6 |   100.00 | Using sort_union(idx4_1,idx4_2); Using where |
+----+-------------+-------+------------+-------------+---------------+---------------+---------+------+------+----------+----------------------------------------------+
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                    ],
                    "analyzing_roworder_intersect": {
                      "usable": false,
                      "cause": "too_few_roworder_scans"
                    }
                  },
                  "analyzing_index_merge_union": [
                    {
                      "indexes_to_merge": [
                        {
                          "range_scan_alternatives": [
                            {
                              "index": "idx4_1",
                              "ranges": [
                                "NULL < d1 < 2"
                              ],
                              "index_dives_for_eq_ranges": true,
                              "rowid_ordered": false, 这里说明这个不是ROR索引,因为条件是一个范围不是等于
                              "using_mrr": false,
                              "index_only": true,
                              "in_memory": 1,
                              "rows": 3,
                              "cost": 0.560671,
                              "chosen": true
                            }
                          ],
                          "index_to_merge": "idx4_1",
                          "cumulated_cost": 0.560671
                        },
                        {
                          "range_scan_alternatives": [
                            {
                              "index": "idx4_2",
                              "ranges": [
                                "20 < d2"
                              ],
                              "index_dives_for_eq_ranges": true,
                              "rowid_ordered": false, 这里说明这个不是ROR索引,因为条件是一个范围不是等于
                              "using_mrr": false,
                              "index_only": true,
                              "in_memory": 1,
                              "rows": 3,
                              "cost": 0.560671,
                              "chosen": true
                            }
                          ],
                          "index_to_merge": "idx4_2",
                          "cumulated_cost": 1.12134 这个值=两个索引累加=0.560671+0.560671
                        }
                      ],
                      "cost_of_reading_ranges": 1.12134,
                      "cost_sort_rowid_and_read_disk": 0.822021, 磁盘扫描的cost
                      "cost_duplicate_removal": 1.2282, 索引排序去重cost
                      "total_cost": 3.17157
                    }
                  ],
                  "chosen_range_access_summary": {
                    "range_access_plan": {
                      "type": "index_merge",
                      "index_merge_of": [
                        {
                          "type": "range_scan",
                          "index": "idx4_1",
                          "rows": 3,
                          "ranges": [
                            "NULL < d1 < 2"
                          ]
                        },
                        {
                          "type": "range_scan",
                          "index": "idx4_2",
                          "rows": 3,
                          "ranges": [
                            "20 < d2"
                          ]
                        }
                      ]
                    },
                    "rows_for_plan": 6,
                    "cost_for_plan": 3.17157,
                    "chosen": true
                  }
                }
              }
            ]
          },

上面的例子改一个条件范围,结果变成不选择排序合并而选择全表扫描的情况。下面trace对比上面发现,少了"chosen_range_access_summary",对比源码发现是imerge_cost > read_cost,因此被放弃。这里主要原因是索引排序cost太高所以放弃了。

greatsql> EXPLAIN SELECT * FROM t4 WHERE d1 < 4 OR d2 > 20;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | t4    | NULL       | ALL  | idx4_2,idx4_1 | NULL | NULL    | NULL |   39 |    53.84 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
          {
            "rows_estimation": [
              {
                "table": "`t4`",
                "range_analysis": {
                  "table_scan": {
                    "rows": 39,
                    "cost": 6.25 这个是全表扫描的cost
                  },

                  "analyzing_index_merge_union": [
                    {
                      "indexes_to_merge": [
                        {
                          "range_scan_alternatives": [
                            {
                              "index": "idx4_1",
                              "ranges": [
                                "NULL < d1 < 4"
                              ],
                              "index_dives_for_eq_ranges": true,
                              "rowid_ordered": false,
                              "using_mrr": false,
                              "index_only": true,
                              "in_memory": 1,
                              "rows": 12,
                              "cost": 1.46369,
                              "chosen": true
                            }
                          ],
                          "index_to_merge": "idx4_1",
                          "cumulated_cost": 1.46369
                        },
                        {
                          "range_scan_alternatives": [
                            {
                              "index": "idx4_2",
                              "ranges": [
                                "20 < d2"
                              ],
                              "index_dives_for_eq_ranges": true,
                              "rowid_ordered": false,
                              "using_mrr": false,
                              "index_only": true,
                              "in_memory": 1,
                              "rows": 3,
                              "cost": 0.560671,
                              "chosen": true
                            }
                          ],
                          "index_to_merge": "idx4_2",
                          "cumulated_cost": 2.02436
                        }
                      ],
                      "cost_of_reading_ranges": 2.02436,
                      "cost_sort_rowid_and_read_disk": 0.986637,
                      "cost_duplicate_removal": 4.42426,
                      "total_cost": 7.43526 索引合并cost大于全表扫描cost的6.25,因此不选择用索引合并
                    }
                  ]
                }
              }
            ]
          },

无法使用index merge交集合并的情况

列的第二个以上条件有右节点
greatsql> EXPLAIN SELECT * FROM t4 WHERE d2>=2 AND (d2>=6 or d2=4);
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type  | possible_keys | key    | key_len | ref  | rows | filtered | Extra                 |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+
|  1 | SIMPLE      | t4    | NULL       | range | idx4_2        | idx4_2 | 5       | NULL |   15 |   100.00 | Using index condition |
+----+-------------+-------+------------+-------+---------------+--------+---------+------+------+----------+-----------------------+

tree叶节点范围最大值和最小值不相等
greatsql> EXPLAIN SELECT * FROM t1 WHERE c1>=2 AND c2>=2 AND c2<=11;
+----+-------------+-------+------------+-------+-------------------+------+---------+------+------+----------+--------------------------+
| id | select_type | table | partitions | type  | possible_keys     | key  | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------+------------+-------+-------------------+------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | t1    | NULL       | range | PRIMARY,idx1,idx2 | idx2 | 5       | NULL |    4 |    85.71 | Using where; Using index |
+----+-------------+-------+------------+-------+-------------------+------+---------+------+------+----------+--------------------------+

多个索引与索引交集的选择

greatsql> CREATE TABLE t5 (d1 INT, d2 int, d3 int, d4 int, d5 int);
greatsql> INSERT INTO t5 VALUES (1,2,1,2,4),(2,1,1,2,1),(2,3,6,9,10),(3,3,2,1,5),(4,2,9,22,4),(4,4,2,1,3),(4,2,7,3,5);
greatsql> create index idx4_1 on t5(d1);
greatsql> CREATE INDEX idx4_2 ON t5(d2);
greatsql> CREATE INDEX idx4_3 ON t5(d3);
greatsql> CREATE INDEX idx4_4 ON t5(d4);
greatsql> CREATE INDEX idx4_5 ON t5(d5);

greatsql> EXPLAIN SELECT * FROM t5 where d2=2 and d4=2 and d3=1 ;
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys        | key           | key_len | ref  | rows | filtered | Extra                                       |
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
|  1 | SIMPLE      | t5    | NULL       | index_merge | idx4_2,idx4_3,idx4_4 | idx4_3,idx4_4 | 5,5     | NULL |    1 |    42.86 | Using intersect(idx4_3,idx4_4); Using where |
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
这个sql通过find_intersect_order函数排序结果为idx4_3、idx4_4、idx4_2,因为idx4_3和idx4_4的rows都是2,但是因为idx4_3在sql位置靠前因此排序靠前。而idx4_2包含的rows是3,因此位置在最后。
                  "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "idx4_2",
                        "ranges": [
                          "d2 = 2"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 3,
                        "cost": 1.31,
                        "chosen": true
                      },
                      {
                        "index": "idx4_3",
                        "ranges": [
                          "d3 = 1"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 2,
                        "cost": 0.96,
                        "chosen": true
                      },
                      {
                        "index": "idx4_4",
                        "ranges": [
                          "d4 = 2"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 2,
                        "cost": 0.96,
                        "chosen": false,
                        "cause": "cost"
                      }
                    ],
                   "analyzing_roworder_intersect": {
                      "intersecting_indexes": [
                        {
                          "index": "idx4_3",
                          "index_scan_cost": 0.250336,
                          "cumulated_index_scan_cost": 0.250336,
                          "disk_sweep_cost": 0.4375,
                          "cumulated_total_cost": 0.687836,
                          "usable": true,
                          "matching_rows_now": 2,
                          "isect_covering_with_this_index": false,
                          "chosen": true
                        },
                        {
                          "index": "idx4_4",
                          "index_scan_cost": 0.250336,
                          "cumulated_index_scan_cost": 0.500671,
                          "disk_sweep_cost": 0,
                          "cumulated_total_cost": 0.500671,
                          "usable": true,
                          "matching_rows_now": 0.571429,
                          "isect_covering_with_this_index": false,
                          "chosen": true 合并完idx4_3和idx4_4以后发现cost变小,因此可以选择
                        },
                        {
                          "index": "idx4_2",
                          "index_scan_cost": 0.250671,
                          "cumulated_index_scan_cost": 0.751342,
                          "disk_sweep_cost": 0,
                          "cumulated_total_cost": 0.751342,
                          "usable": true,
                          "matching_rows_now": 0.244898,
                          "isect_covering_with_this_index": false,
                          "chosen": false,
                          "cause": "does_not_reduce_cost" 这里合并到idx4_2就发现cost大于单独用前两个索引,因此不继续进行交集操作了
                        }
                      ],
                      "clustered_pk": {
                        "clustered_pk_added_to_intersect": false,
                        "cause": "no_clustered_pk_index"
                      },
                      "rows": 1,
                      "cost": 0.500671,
                      "covering": false,
                      "chosen": true
                    }

索引并集合并的子集是索引交集的情况

greatsql> EXPLAIN SELECT * FROM t5 WHERE (d2=4 AND d1=4 ) OR (d3=1 AND d4=2);
+----+-------------+-------+------------+-------------+-----------------------------+----------------------+---------+------+------+----------+-----------------------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys               | key                  | key_len | ref  | rows | filtered | Extra                                                     |
+----+-------------+-------+------------+-------------+-----------------------------+----------------------+---------+------+------+----------+-----------------------------------------------------------+
|  1 | SIMPLE      | t5    | NULL       | index_merge | idx4_1,idx4_2,idx4_3,idx4_4 | idx4_2,idx4_3,idx4_4 | 5,5,5   | NULL |    2 |   100.00 | Using union(idx4_2,intersect(idx4_3,idx4_4)); Using where |
   这里的子集没用idx4_1和idx4_2的交集,因为加上idx4_1以后的cost比单独用idx4_2还要大,因此idx4_1和idx4_2不使用索引交集
+----+-------------+-------+------------+-------------+-----------------------------+----------------------+---------+------+------+----------+-----------------------------------------------------------+

OPTIMIZER_SWITCH_FAVOR_RANGE_SCAN使用举例

greatsql> EXPLAIN SELECT * FROM t5 WHERE d2=2 AND d4=2 AND d3=1;
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
| id | select_type | table | partitions | type        | possible_keys        | key           | key_len | ref  | rows | filtered | Extra                                       |
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+
|  1 | SIMPLE      | t5    | NULL       | index_merge | idx4_2,idx4_3,idx4_4 | idx4_3,idx4_4 | 5,5     | NULL |    1 |    42.86 | Using intersect(idx4_3,idx4_4); Using where |
+----+-------------+-------+------------+-------------+----------------------+---------------+---------+------+------+----------+---------------------------------------------+

加上FAVOR_RANGE_SCAN以后看到结果变为走单个索引而不走索引合并了。
greatsql> EXPLAIN SELECT /*+ set_var(optimizer_switch='FAVOR_RANGE_SCAN=ON') */ * FROM t5 WHERE d2=2 AND d4=2 AND d3=1;
+----+-------------+-------+------------+------+----------------------+--------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys        | key    | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+----------------------+--------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | t5    | NULL       | ref  | idx4_2,idx4_3,idx4_4 | idx4_3 | 5       | const |    2 |    14.29 | Using where |
+----+-------------+-------+------------+------+----------------------+--------+---------+-------+------+----------+-------------+
下面的trace可以看到每个单独的索引的cost都被改小十倍,这样后面执行索引合并的时候对比这个cost就会偏大,导致最后走单个索引而不是索引合并。
                 "analyzing_range_alternatives": {
                    "range_scan_alternatives": [
                      {
                        "index": "idx4_2",
                        "ranges": [
                          "d2 = 2"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 3,
                        "cost": 1.31,
                        "revised_cost": 0.131, 这个地方修改了cost值,变为cost * 0.1 = 1.31 * 0.1
                        "chosen": true
                      },
                      {
                        "index": "idx4_3",
                        "ranges": [
                          "d3 = 1"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 2,
                        "cost": 0.96,
                        "revised_cost": 0.096, 这个地方修改了cost值,变为cost * 0.1 = 1.31 * 0.1
                        "chosen": true
                      },
                      {
                        "index": "idx4_4",
                        "ranges": [
                          "d4 = 2"
                        ],
                        "index_dives_for_eq_ranges": true,
                        "rowid_ordered": true,
                        "using_mrr": false,
                        "index_only": false,
                        "in_memory": 1,
                        "rows": 2,
                        "cost": 0.96,
                        "revised_cost": 0.096, 这个地方修改了cost值,变为cost * 0.1 = 1.31 * 0.1
                        "chosen": false,
                        "cause": "cost"
                      }
                    ],
                    "analyzing_roworder_intersect": {
                      "intersecting_indexes": [
                        {
                          "index": "idx4_3",
                          "index_scan_cost": 0.250336,
                          "cumulated_index_scan_cost": 0.250336,
                          "disk_sweep_cost": 0.4375,
                          "cumulated_total_cost": 0.687836,
                          "usable": true,
                          "matching_rows_now": 2,
                          "isect_covering_with_this_index": false,
                          "chosen": true
                        },
                        {
                          "index": "idx4_4",
                          "index_scan_cost": 0.250336,
                          "cumulated_index_scan_cost": 0.500671,
                          "disk_sweep_cost": 0,
                          "cumulated_total_cost": 0.500671,
                          "usable": true,
                          "matching_rows_now": 0.571429,
                          "isect_covering_with_this_index": false,
                          "chosen": true
                        },
                        {
                          "index": "idx4_2",
                          "index_scan_cost": 0.250671,
                          "cumulated_index_scan_cost": 0.751342,
                          "disk_sweep_cost": 0,
                          "cumulated_total_cost": 0.751342,
                          "usable": true,
                          "matching_rows_now": 0.244898,
                          "isect_covering_with_this_index": false,
                          "chosen": false,
                          "cause": "does_not_reduce_cost"
                        }
                      ],
                      "clustered_pk": {
                        "clustered_pk_added_to_intersect": false,
                        "cause": "no_clustered_pk_index"
                      },
                      "chosen": false, 索引合并cost大于idx4_3,因此没有选择索引合并
                      "cause": "cost"
                    }
                  },
                  "chosen_range_access_summary": { 最后结果选了cost最小的idx4_3,因为cost最小
                    "range_access_plan": {
                      "type": "range_scan",
                      "index": "idx4_3",
                      "rows": 2,
                      "ranges": [
                        "d3 = 1"
                      ]
                    },
                    "rows_for_plan": 2,
                    "cost_for_plan": 0.096,
                    "chosen": true
                  }
                }
              }
四、总结

从上面索引合并的应用例子我们认识了索引合并的使用场合和好处,也知道了索引合并的三种类型以及分别使用的条件,认识了ROR条件的判定和使用,还有相关系统变量,以上选择都是系统自动选择的,如果要改变结果只能添加OPTIMIZER_SWITCH强制改变索引的使用。

基于STC8G1K08A单片机的PCA捕获外部信号(100~1000HZ)程序实现 阅读详情

相关推荐

【UE5 C++开发指南】从零配置Visual Studio 2022开发环境

本文详细指导如何从零开始为UE5 C++开发配置Visual Studio 2022环境。文章强调了VS2022与UE5的深度兼容性,并逐步讲解了社区版下载、核心工作负载(如“使用C++的游戏开发”)与组件安装、界面优化、专属工具设置以及首个C++项目的创建与验证流程,帮助开发者高效搭建专业开发环境。

weixin_29016865的博客 393

MySQL 优化之 index_merge (索引合并)

MySQL 优化之 index_merge (索引合并)案例优化知识点功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流程图导出与导入导出导入 案例 sql如下: SELECT ID, COUPONS_CODE, BATCH_ID, IS_SOLD, IS

java~ 7251

R链式中介:如何一次性生成4个变量所有可能的链式中介结果?(含结果解读与粗略作图)#bruceR#ggplot2

一次性生成4个变量所能生成的24种链式中介并作图

weixin_64183017的博客 3236

MySQL索引合并Index Merge 一条SQL使用多个索引情况分析,以及索引合并失效情况

MySQL在5.0版本加入了索引合并优化(Index Merge),索引合并可以同时查询多个索引的范围扫描,并将其结果合并为一个。索引合并只可以合并来自单个表的索引扫描,而不支持跨多个表的扫描。合并可以生成其基础扫描的并集(unions)、交集(intersections)或交集的并集(unions-of-intersections)。

xzh_blog 4270

潜行游戏设计:核心机制与关卡设计解析

潜行游戏作为动作游戏的重要分支,通过视觉遮蔽、声音传播和AI行为树等复杂系统,为玩家提供独特的策略体验。视觉隐蔽系统采用视野锥和光照值的双重判定,声学传播模型模拟距离衰减和材质穿透,而敌人AI行为树则包含巡逻、警戒和团队协作等状态节点。这些技术不仅提升了游戏的真实感和挑战性,还在《合金装备》《刺客信条》等经典作品中得到广泛应用。潜行游戏的关卡设计强调多路径拓扑结构和环境叙事元素,通过沙漏模型和动态难度调节,平衡自由度和开发成本。未来,基于ML的适应性AI和物理模拟的隐蔽系统将成为创新方向。

weixin_42497762的博客 180

【WorkBuddy专栏50】代码开发技术体系深度分析——前端、后端、全栈、移动端、数据工程,WB和CODEBUDDY谁更擅长?

【WorkBuddy专栏50】代码开发技术体系深度分析——前端、后端、全栈、移动端、数据工程,WB和CODEBUDDY谁更擅长?

AI人人懂 237

MySQL中为什么要使用索引合并(Index Merge)?

在MySQL中,索引是提高查询性能的重要工具。索引合并(Index Merge)是一种优化技术,允许MySQL在执行查询时结合多个单列索引的结果,以提高查询效率。本文将详细解释为什么要使用索引合并,索引合并的工作原理,实际应用场景,以及如何在MySQL中查看和优化索引合并。

w13707470416的博客 579

【数据集介绍】MVTec 3D AD数据集介绍

MVTec AD(MVTec Anomaly Detection)数据集是用于异常检测的计算机视觉数据集。该数据集包含来自工业生产中不同材料和产品的图像,包括玻璃、塑料、纺织品等。每个类别都包含正常样本和异常样本。这些异常样本包括各种常见的生产缺陷,如划痕、裂纹、凹陷等。MVTec AD数据集旨在帮助研究人员和从业者开发和评估用于检测工业制造中物体缺陷的算法和系统。

原来也有北瓜 7751

mysql使用了index merge(索引合并)造成死锁分析总结

mysql数据库更新时造成死锁,经查看执行计划发现是使用到了index merge索引合并算法,那个这个索引合并是什么呢?为什么会用到索引合并呢?使用index merge为什么会造成死锁呢?怎么避免或解决死锁呢?接下来通过这篇文章带你一探究竟。

阿钟小哥的博客 5720

基于 Simulink 的双向 DC/AC 逆变器中 IGBT/MOSFET 寄生参数影响分析实战教程

摘要:本教程详细解析了Simulink中IGBT/MOSFET寄生参数对双向DC/AC逆变器的影响。通过对比理想模型与含寄生参数(如20nH内部电感、50nH回路电感)的仿真,揭示了电压尖峰、高频振荡等实际问题。建模步骤包括搭建基准电路、引入寄生参数、配置高精度求解器(步长10ns),并给出抑制措施:添加RC缓冲电路、调整驱动电阻。关键发现:寄生参数会显著增加开关损耗和EMI噪声,需通过参数扫描优化设计。教程还提供了代数环报错、仿真速度优化等实战技巧,帮助工程师从理想仿真过渡到高保真分析。

xiaoheshang_123的博客 68

基于BQ25895RTWR的锂电池充电管理硬件设计

BQ25895 是一款适用于单节锂离子电池和锂聚合物电池的高度集成型 5A 开关模式电池充电管理和系统电源路径管理器件。此类器件支持高输入电压快速充电。低阻抗电源路径对开关模式运行效率进行了优化、缩短了电池充电时间并延长了放电阶段的电池使用寿命。• 移动电源、移动 Wi-Fi 热点。• 便携式互联网器件。

corlin6688的博客 1087

MySQL优化器:index merge介绍

在MySQL官方手册上,关于index merge的介绍非常非常少。甚至还有不少误导的地方,这次把5.1版本关于此类优化处理的代码细看了一遍,以案例的方式介绍了各种实用index merge访问类型的SQL。后续的还会继续介绍index merge实现的主要数据结构,以及成本评估。 1. 什么是index merge MySQL优化器如果发现可以使用多个索引查找后的交集/并集定位数据,那么MySQL优化器就会尝试index merge这类访问方式。index merge主要分为两大类,多个索引交集访问(

GarfieldEr007的专栏 1976

十九、帧间预测编码_2、解码、显示顺序与图像管理、参考帧管理

一、基本概念 1.1H.264的解码顺序和显示顺序: 解码过程中,每一帧的数据按照相应的NAL Unit在码流中的顺序传入解码器进行解码 由于B帧的存在,视频帧在输出时会进行顺序重排 1.2frame_num与POC frame_num:表示解码顺序 POC:表示显示顺序(picture_order_count) 在H.264的码流中,表示解码顺序和显示顺序分别有相应的语法元素表示。这两个值都会在码流中保存,并在读取slice信息时解析(SliceHeader中) 1.3参考帧列表 某一帧图像在解

叮咚的博客 1330

Mysql——》index merge 索引合并

MySQL在一般情况下执行一个查询时,只会用到单个二级索引,但是在某些特殊情况下,可能会使用到多个二级索引。使用多个索引来完成一次查询的执行方法称之为index merge索引合并,具体的索引合并算法有下边三种。

小仙~ 1126

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Index_merge

SELECT uid, target_uid, reward_value, reward_name, lottery_status, create_time FROM user_lottery_reward_log WHERE act_id = 18 AND (uid = 22381848 OR (target_uid = 22381848 AND lottery_status != 3)) O...

RedHaohao的博客 2345

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

记一次实遇的 MySQL--index merge 死锁历程

本文仅描述了由于 index_merge 优化导致的死锁,讲述了死锁产生的原因以及解决办法,并顺便介绍了 MySQL 加锁机制。事实上死锁的原因还有很多,比如操作数据顺序不一致、长事务等,就不一一展开了。

Park33的博客 1059

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

【面试】什么是Mysql索引合优化index_merge?

在使用 explain 命令分析 SQL 执行情况的时候,type列会描述了表如何被连接,这个列的内容直接反映了 SQL 执行的效率。当里面的内容展示为时表示使用了索引合并优化,在这种情况下输出行中的key列包含具体使用的索引。MySQL 的索引合并优化是一种查询优化技术,它利用多个索引来加速查询的执行。当一个查询中包含多个条件,并且这些条件分别适用于不同的索引时,MySQL 可以将这些索引合并起来使用,减少了回表的次数,以加速查询的执行。

没有伞的孩子必须努力奔跑!—小林 1101

mysql索引合并index-merge

和联合索引相比,index merge optimization因为要扫描多个索引,并将多个索引的主键进行排序,取交集,取并集等操作,所以通常会比聚合索引慢。结论:or条件能很好的触发index_merge,但是and条件不一定会触发,目前没有发现扫描规律,具体需要通过mysql源码才能确定具体逻辑。同时,如果在同一张表上创建多个索引,表的insert,update等操作需要维护的索引比较多,因此有会降低DML语句的执行效率。但是index_merge的存在确实能提高sql的执行效率,可以减少回表的次数。

qq_36115368的博客 739

MySQL优化之Index Merge

1. 前言 先问大家一个问题,在不考虑多表联查这种复杂的查询场景下,一个简单的单表查询,MySQL可以同时利用几个索引? 当初我学习MySQL的时候,天真的以为只要把WHERE条件涉及到的列全部加上索引,就可以提升查询速度,这个想法其实大错特错。因为一般情况下,单表查询MySQL只能利用一个索引,比如下面这个查询,假设id是主键,a和b分别创建了索引,别天真的以为idx_a和idx_b都能发挥作用,其实不是的。 SELECT id,a,b FROM T WHERE a>100 AND b>200

程序员小潘 3329

数据库优化 index merge 的理解

今天上班的路上看到脉脉上的帖子(暴露我上班时间了),在讨论一个索引的问题, 自认为对索引还比较熟悉,专门抽时间验证了一下各位的说法.记录下来用来加深理解; 先说说对于这段sql大家的理解: 不用索引 只用一个索引 有人理解为联合索引,联合索引全表扫描 or 只用一个索引,and走index merge ​ 先说说我自己为做动手前的想法; 面...

zhangzeshuai的专栏 7189

MySQL 优化 index merge(索引合并)引起的死锁分析(强烈推荐)

这里可能很多人会有疑问了,uniq_trans_id 已经是一个唯一索引了,通过这个索引最终只能找到最多一条数据,那MySQL优化器为啥还要用两个索引取交集,再回表进行查询呢,这样不是多了一次 idx_status 索引查找的过程么。通过看执行计划,可以发现,update语句用到了索引合并,也就是这条语句既用到了 uniq_trans_id 索引,又用到了 idx_status 索引,Using intersect(uniq_trans_id,idx_status)的意思是通过两个索引获取交集。

m0_63437643的博客 368

MySQL学习笔记(七)--Index Merge

举例说明。

hongjian006的博客 989

mysql index merge_MySQL查询优化:Index Merge

一、为什么会有Index Merge?我们的 where 中可能有多个条件(或者join)涉及到多个字段,它们之间进行 AND 或者 OR,那么此时就有可能会使用到 Index Merge 技术。Index Merge 技术如果简单的说,其实就是对多个索引分别进行条件扫描,然后将它们各自的结果进行集合运算(Intersect/Union)。MySQL 5.0之前,一个表一次只能使用一个索引,无法同...

weixin_32892145的博客 674

MySQL 优化之 index merge(索引合并)

深入理解 index merge 是使用索引进行优化的重要基础之一。理解了 index merge 技术,我们才知道应该如何在表上建立索引。 1. 为什么会有index merge 我们的 where 中可能有多个条件(或者join)涉及到多个字段,它们之间进行 AND 或者 OR,那么此时就有可能会使用到 index merge 技术。index merge 技术如果简单的说,其实就是:对多...

weixin_34217711的博客 586

MySQL 优化 index merge(索引合并)引起的死锁分析

生产环境出现死锁流水,通过查看死锁日志,看到造成死锁的是两条一样的update语句(只有where条件中的值不同),如下: UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0; UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0; 一

晓呆同学的专栏 2658

mysql 查询优化实验报告_MySQL 查询优化器实验分析(四)

紧接着之前的《》中的内容,以下对测试过程进行详细分析。2、复合查询在进行复合查询时,为了体现外连接(left join、rightjoin)和一般联合查询的区别,对student表增加了几条记录,而这几条记录在std_cur和course中都没有对应的记录。2.1多表联合查询多表联合查询的逻辑处理过程如下所示:JOIN:prepare阶段setup_tables():对查询涉及的表,逐个查看是否存...

weixin_39614874的博客 643
上一篇: 【GreatSQL优化器-14】直方图应用
下一篇: GreatSQL修改配置文件参数无法生效
GreatSQL社区
博客等级 码龄5年 1210粉丝 390原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值