mysql 范围索引 els_MySQL内核技术之“索引查询”

本文深入探讨了MySQL内部如何使用快速范围选择结构进行索引查询。从join初始化到read_record过程,详细解释了如何设置范围查询的上下边界,并通过read_range_first和read_range_next函数执行读取操作,确保数据在指定范围内。文章还提到了判断是否达到范围上限的逻辑,用于控制查询的结束条件。
摘要由CSDN通过智能技术生成

MySQL的索引查询内部使用的是quick range结构。先让我们看看调用流程:

join->optimize()-->

make_join_plan()-->

estimate_rowcount()-->

get_quick_record_count()-->

test_quick_select()-->

...

join_init_read_record()(这个函数设置要使用的读取操作为rr_quick())-->

read_record(实际上是QUICK_RANGE_SELECT::get_next())-->

ha_innobase::multi_range_read_next-->

DsMrr_impl::dsmrr_next-->

handler::multi_range_read_next-->

如果是第一次:mrr_funcs.next(mrr_iter, &mrr_cur_range)实际上是调用:quick_range_seq_next

如果不是:read_range_next

quick_range_seq_next最重要的作用是设置范围查询的上下边界:

uint quick_range_seq_next(range_seq_t rseq, KEY_MULTI_RANGE *range)

{

QUICK_RANGE_SEQ_CTX *ctx= (QUICK_RANGE_SEQ_CTX*)rseq;

if (ctx->cur == ctx->last)

return 1; /* no more ranges */

QUICK_RANGE *cur= *(ctx->cur);

key_range *start_key= &range->start_key;

key_range *end_key= &range->end_key;

start_key->key= cur->min_key;

start_key->length= cur->min_length;

start_key->keypart_map= cur->min_keypart_map;

start_key->flag= ((cur->flag & NEAR_MIN) ? HA_READ_AFTER_KEY :

(cur->flag & EQ_RANGE) ?

HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT);

end_key->key= cur->max_key;

end_key->length= cur->max_length;

end_key->keypart_map= cur->max_keypart_map;

/*

We use HA_READ_AFTER_KEY here because if we are reading on a key

prefix. We want to find all keys with this prefix.

*/

end_key->flag= (cur->flag & NEAR_MAX ? HA_READ_BEFORE_KEY :

HA_READ_AFTER_KEY);

range->range_flag= cur->flag;

ctx->cur++;

return 0;

}

上面函数中,设置上下边界的的代码:

start_key->key= cur->min_key; 为5

end_key->key= cur->max_key; 为7

start_key->flag=HA_READ_KEY_OR_NEXT;

end_key->flag=HA_READ_BEFORE_KEY;

执行完quick_range_seq_next,开始真正执行读取操作:

read_range_first

/** @brief

Read first row between two ranges.

Store ranges for future calls to read_range_next.

@param start_key Start key. Is 0 if no min range

@param end_key End key. Is 0 if no max range

@param eq_range_arg Set to 1 if start_key == end_key

@param sorted Set to 1 if result should be sorted per key

@note

Record is read into table->record[0]

@retval

0 Found row

@retval

HA_ERR_END_OF_FILE No rows in range

@retval

\# Error code

*/

int handler::read_range_first(const key_range *start_key,

const key_range *end_key,

bool eq_range_arg,

bool sorted /* ignored */)

{

int result;

DBUG_ENTER("handler::read_range_first");

eq_range= eq_range_arg;

set_end_range(end_key, RANGE_SCAN_ASC);

range_key_part= table->key_info[active_index].key_part;

if (!start_key) // Read first record

result= ha_index_first(table->record[0]);

else

result= ha_index_read_map(table->record[0],

start_key->key,

start_key->keypart_map,

start_key->flag);

if (result)

DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND)

? HA_ERR_END_OF_FILE

: result);

if (compare_key(end_range) <= 0)

{

DBUG_RETURN(0);

}

else

{

/*

The last read row does not fall in the range. So request

storage engine to release row lock if possible.

*/

unlock_row();

DBUG_RETURN(HA_ERR_END_OF_FILE);

}

}

这里会设置结束边界:set_end_range(end_key, RANGE_SCAN_ASC); end_key为7。接下来读取第一条记录:

if (!start_key) // Read first record

result= ha_index_first(table->record[0]);

else

result= ha_index_read_map(table->record[0],

start_key->key,

start_key->keypart_map,

start_key->flag);

接下来sub_select会执行read_range_next来读取下一条数据。无论在read_range_next还是read_range_first中,最后面都有下面这个判断:

if (compare_key(end_range) <= 0)

{

DBUG_RETURN(0);

}

else

{

/*

The last read row does not fall in the range. So request

storage engine to release row lock if possible.

*/

unlock_row();

DBUG_RETURN(HA_ERR_END_OF_FILE);

}

}

这个就是用来判断读取的记录是不是到达了上界,如果是则返回HA_ERR_END_OF_FILE结束。

请注意,我只能解释SQL语句的执行计划,而无法直接执行SQL或访问您的数据库。 为了解释这个SQL语句的执行计划,您可以在查询前加上"EXPLAIN"关键字,这样可以返回查询计划的详细信息。执行计划中包含了查询优化器的选择,索引使用情况,以及表访问的顺序等信息,可以帮助您优化查询性能。以下是您提供的SQL语句的执行计划: ``` +----+-------------+-------+------------+--------+------------------------------------------------------------------------------------------------------------------------+-------------------------+---------+------------------------------------+------+----------+----------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+------------------------------------------------------------------------------------------------------------------------+-------------------------+---------+------------------------------------+------+----------+----------------------------------------------+ | 1 | SIMPLE | a1 | NULL | range | ACT_HI_TASKINST_NAME_UINDEX,ACT_IDX_HI_TASKINST_PROCINST_ID,ACT_HI_TASKINST_END_TIME_,ACT_HI_TASKINST_START_TIME_ | ACT_HI_TASKINST_END_TIME_ | 8 | NULL | 5515 | 100.00 | Using where; Using temporary; Using filesort | | 1 | SIMPLE | a2 | NULL | eq_ref | PRIMARY | PRIMARY | 32 | activiti.a1.PROC_INST_ID_ | 1 | 100.00 | NULL | | 1 | SIMPLE | a3 | NULL | ref | els_dict_item_item_value_dict_id_index | els_dict_item_item_value | 1020 | const,activiti.a2.BIZ_TYPE,const | 1 | 100.00 | NULL | | 1 | SIMPLE | a4 | NULL | eq_ref | PRIMARY | PRIMARY | 8 | activiti.a1.ASSIGNEE_ | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+------------------------------------------------------------------------------------------------------------------------+-------------------------+---------+------------------------------------+------+----------+----------------------------------------------+ ``` 根据执行计划,该查询使用了四张表: - ACT_HI_TASKINST - A1_FLOW_INSTANCE - els_dict_item - els_subaccount_info 其中,ACT_HI_TASKINST表和A1_FLOW_INSTANCE表使用了INNER JOIN,els_dict_item表使用了LEFT JOIN,els_subaccount_info表使用了INNER JOIN。 该查询使用了多个索引,包括: - ACT_HI_TASKINST_END_TIME_索引范围查找) - ACT_IDX_HI_TASKINST_PROCINST_ID索引(关联查询) - els_dict_item_item_value_dict_id_index索引(关联查询) - PRIMARY索引(关联查询查询的过滤条件包括END_TIME_ IS NOT NULL和NAME_ NOT LIKE '%发起人%',以及可选的START_TIME_和END_TIME_过滤条件。由于使用了聚合函数SUM和COUNT,因此需要使用GROUP BY对结果进行分组。 需要注意的是,该查询使用了"Using temporary; Using filesort"的Extra信息,这意味着MySQL需要在临时表中存储和排序查询结果。如果查询性能不够理想,可以考虑优化查询条件、索引和GROUP BY语句等。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值