当前位置:首页 >休闲 >带你读 MySQL 源码:Where 条件怎么过滤记录? 只要其中一个返回值等于True

带你读 MySQL 源码:Where 条件怎么过滤记录? 只要其中一个返回值等于True

2024-06-09 15:03:31 [百科] 来源:避面尹邢网

带你读 MySQL 源码:Where 条件怎么过滤记录?

作者:操盛春 数据库 MySQL 从存储引擎读取一条记录之后,条件对 Or 连接的过滤 N 个 Where 条件(N >= 2)调用 Item->val_bool(),只要其中一个返回值等于True,记录记录就匹配 Or 连接的条件 N 个 Where 条件。

我们来聊聊 MySQL 是过滤怎么判断一条记录是否匹配 where 条件的。

带你读 MySQL 源码:Where 条件怎么过滤记录? 只要其中一个返回值等于True

本文内容基于 MySQL 8.0.32 源码。记录

带你读 MySQL 源码:Where 条件怎么过滤记录? 只要其中一个返回值等于True

正文

准备工作

创建测试表:

带你读 MySQL 源码:Where 条件怎么过滤记录? 只要其中一个返回值等于True

CREATE TABLE `t1` (  `id` int unsigned NOT NULL AUTO_INCREMENT,条件  `str1` varchar(255) DEFAULT '',  `i1` int DEFAULT '0',  `i2` int DEFAULT '0',  PRIMARY KEY (`id`) USING BTREE) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

插入测试数据:

INSERT INTO t1(str1, i1, i2) VALUES('s1', NULL, NULL),('s2', 20, NULL),('s3', 30, 31),('s4', 40, 41),('s5', 50, 51),('s6', 60, 61),('s7', 70, 71),('s8', 80, 81);

示例 SQL:

select * from t1where i2 > 20 and (i1 = 50 or i1 = 80)

整体介绍

在源码中,where 条件会形成树状结构,过滤示例 SQL 的记录 where 条件结构如下:

注意:这里的树状结构不是数据结构中的树。

图片

我们可以从图中得到以下信息:

  • Item_cond_and 代表 where 条件中的条件 and,连接 Item_func_gt 和 Item_cond_or。过滤
  • Item_func_gt 代表 i2 > 20,记录其中 Item_field 包含 Field_long,条件代表 i2 字段,过滤Item_int 代表整数 20。记录
  • Item_cond_or 代表 where 条件中的 or,连接两个 Item_func_eq。
  • 第 1 个 Item_func_eq 代表 i1 = 50,其中 Item_field 包含 Field_long,代表 i1 字段,Item_int 代表整数 50。
  • 第 2 个 Item_func_eq 代表 i1 = 80,其中 Item_field 包含 Field_long,代表 i1 字段,Item_int 代表整数 80。

接下来,我们结合堆栈来看看 where 条件的实现流程:

| > mysql_execute_command(THD*, bool) sql/sql_parse.cc:4688| + > Sql_cmd_dml::execute(THD*) sql/sql_select.cc:578| + - > Sql_cmd_dml::execute_inner(THD*) sql/sql_select.cc:778| + - x > Query_expression::execute(THD*) sql/sql_union.cc:1823| + - x = > Query_expression::ExecuteIteratorQuery(THD*) sql/sql_union.cc:1770| + - x = | > FilterIterator::Read() sql/iterators/composite_iterators.cc:79| + - x = | + > Item_cond_and::val_int() sql/item_cmpfunc.cc:5973| + - x = | + - > // 第 1 个 Item::val_bool()| + - x = | + - > // 代表 i2 > 20| + - x = | + - > Item::val_bool() sql/item.cc:218| + - x = | + - x > Item_func_gt::val_int() sql/item_cmpfunc.cc:2686| + - x = | + - x = > Arg_comparator::compare() sql/item_cmpfunc.h:210| + - x = | + - x = | > Arg_comparator::compare_int_signed() sql/item_cmpfunc.cc:1826| + - x = | + - x = | + > Item_field::val_int() sql/item.cc:3013| + - x = | + - x = | + - > Field_long::val_int() const sql/field.cc:3763 // i2| + - x = | + - x = | + > Item_int::val_int() sql/item.h:4934 // 20| + - x = | + - > // 第 2 个 Item::val_bool()| + - x = | + - > // 代表 i1 = 50 or i1 = 80| + - x = | + - > Item::val_bool() sql/item.cc:218| + - x = | + - x > Item_cond_or::val_int() sql/item_cmpfunc.cc:6017| + - x = | + - x = > // 第 3 个 Item::val_bool()| + - x = | + - x = > // 代表 i1 = 50| + - x = | + - x = > Item::val_bool() sql/item.cc:218| + - x = | + - x = | > Item_func_eq::val_int() sql/item_cmpfunc.cc:2493| + - x = | + - x = | + > Arg_comparator::compare() sql/item_cmpfunc.h:210| + - x = | + - x = | + - > Arg_comparator::compare_int_signed() sql/item_cmpfunc.cc:1826| + - x = | + - x = | + - x > Item_field::val_int() sql/item.cc:3013| + - x = | + - x = | + - x = > Field_long::val_int() const sql/field.cc:3763 // i1| + - x = | + - x = | + - x > Item_int::val_int() sql/item.h:4934 // 50| + - x = | + - x = > // 第 4 个 Item::val_bool()| + - x = | + - x = > // 代表 i1 = 80| + - x = | + - x = > Item::val_bool() sql/item.cc:218| + - x = | + - x = | > Item_func_eq::val_int() sql/item_cmpfunc.cc:2493| + - x = | + - x = | + > Arg_comparator::compare() sql/item_cmpfunc.h:210| + - x = | + - x = | + - > Arg_comparator::compare_int_signed() sql/item_cmpfunc.cc:1826| + - x = | + - x = | + - x > Item_field::val_int() sql/item.cc:3013| + - x = | + - x = | + - x = > Field_long::val_int() const sql/field.cc:3763 // i1| + - x = | + - x = | + - x > Item_int::val_int() sql/item.h:4934 // 80

FilterIterator::Read() 从存储引擎读取一条记录,Item_cond_and::val_int() 判断该记录是否匹配 where 条件。

从堆栈中可以看到,Item_cond_and::val_int() 的下一层有两个 Item::val_bool():

  • 第 1 个 Item::val_bool() 代表 i2 > 20,经过多级调用 Arg_comparator::compare_int_signed() 判断记录的 i2 字段值是否大于 20。
  • 第 2 个 Item::val_bool() 代表 i1 = 50 or i1 = 80。
  • 第 2 个 Item::val_bool() 是复合条件,它的下层还嵌套了第 3、4 个 Item::val_bool():
  • 第 3 个 Item::val_bool() 代表 i1 = 50,经过多级调用 Arg_comparator::compare_int_signed() 判断记录的 i1 字段值是否等于 50。
  • 第 4 个 Item::val_bool() 代表 i1 = 80,经过多级调用 Arg_comparator::compare_int_signed() 方法判断记录的 i1 字段值是否等于 80。

第 3、4 个 Item::val_bool() 中只要有一个返回 true,第 2 个 Item::val_bool() 就会返回 true,表示记录匹配 i1 = 50 or i1 = 80。

第 1、2 个 Item::val_bool() 必须都返回 true,Item_cond_and::val_int() 才会返回 1,表示记录匹配示例 SQL 的 where 条件。

源码分析

ExecuteIteratorQuery()

// sql/sql_union.ccbool Query_expression::ExecuteIteratorQuery(THD *thd) {   ...  {     ...    for (;;) {       // 从存储引擎读取一条记录      int error = m_root_iterator->Read();      DBUG_EXECUTE_IF("bug13822652_1", thd->killed = THD::KILL_QUERY;);      // 读取出错,直接返回      if (error > 0 || thd->is_error())  // Fatal error        return true;      // error < 0      // 表示已经读完了所有符合条件的记录      // 查询结束      else if (error < 0)        break;      // SQL 被客户端干掉了      else if (thd->killed)  // Aborted by user      {         thd->send_kill_message();        return true;      }      ...      // 发送数据给客户端      if (query_result->send_data(thd, *fields)) {         return true;      }      ...    }  }  ...}

这个方法是 select 语句的入口,属于重量级方法,在源码分析的第 1 篇文章《带你读 MySQL 源码:limit, offset》中也介绍过,但是,本文示例 SQL 的执行计划和之前不一样,这里有必要再介绍下。

m_root_iterator->Read() 从存储引擎读取一条记录,对于示例 SQL 来说,m_root_iterator 是 FilterIterator 迭代器对象,实际执行的方法是 FilterIterator::Read()。

FilterIterator::Read()

int FilterIterator::Read() {   for (;;) {     int err = m_source->Read();    if (err != 0) return err;    bool matched = m_condition->val_int();    if (thd()->killed) {       thd()->send_kill_message();      return 1;    }    /* check for errors evaluating the condition */    if (thd()->is_error()) return 1;    if (!matched) {       m_source->UnlockRow();      continue;    }    // Successful row.    return 0;  }}

上面是 FilterIterator::Read() 方法的全部代码,代码量比较少,主要逻辑如下:

m_source->Read() 方法从存储引擎读取一条记录,因为示例 SQL 中 t1 表的访问方式为全表扫描,所以 m_source 是 TableScanIterator 迭代器对象。

通过 explain 可以确认示例 SQL 中 t1 表的访问方式为全表扫描(type = ALL):

explain select * from t1where i2 > 20 and (i1 = 50 or i1 = 80)\G

(责任编辑:探索)

    推荐文章
    热点阅读