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

从存储引擎读取一条记录之后,对 Or 连接的 N 个 Where 条件(N >= 2)调用 Item->val_bool(),只要其中一个返回值等于True,记录就匹配 Or 连接的 N 个 Where 条件 。

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

文章插图
我们来聊聊 MySQL 是怎么判断一条记录是否匹配 where 条件的 。
本文内容基于 MySQL 8.0.32 源码 。
正文准备工作创建测试表:
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 条件结构如下:
注意:这里的树状结构不是数据结构中的树 。
带你读 MySQL 源码: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【带你读 MySQL 源码:Where 条件怎么过滤记录?】FilterIterator::Read() 从存储引擎读取一条记录,Item_cond_and::val_int() 判断该记录是否匹配 where 条件 。
从堆栈中可以看到,Item_cond_and::val_int() 的下一层有两个 Item::val_bool():


推荐阅读