您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

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

时间:2023-05-25 13:42:04  来源:微信公众号  作者:一树一溪
从存储引擎读取一条记录之后,对 Or 连接的 N 个 Where 条件(N >= 2)调用 Item->val_bool(),只要其中一个返回值等于True,记录就匹配 Or 连接的 N 个 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 t1
where 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.cc
bool 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 t1
where i2 > 20 and (i1 = 50 or i1 = 80)G

***************************[ 1. row ]***************************
id            | 1
select_type   | SIMPLE
table         | t1
partitions    | <null>
type          | ALL
possible_keys | <null>
key           | <null>
key_len       | <null>
ref           | <null>
rows          | 8
filtered      | 12.5
Extra         | Using where

m_source->Read() 从存储引擎读取一条记录之后,m_condition->val_int() 会判断这条记录是否匹配 where 条件。

m_condition 代表 SQL 的 where 条件,对于示例 SQL 来说,它是 Item_cond_and 对象。

m_condition->val_int() 实际执行的方法是 Item_cond_and::val_int(),这就是判断记录是否匹配示例 SQL where 条件的入口。

compare_int_signed()

// sql/item_cmpfunc.cc
int Arg_comparator::compare_int_signed() {
  // 获取 where 条件操作符左边的值
  // 例如:i2 > 20
  // 获取当前读取记录的 i2 字段值
  longlong val1 = (*left)->val_int();
  if (current_thd->is_error()) return 0;
  // where 条件操作符左边的值不为 NULL
  // 才进入 if 分支
  if (!(*left)->null_value) {
    // 获取 where 条件操作符右边的值
    // 例如:i2 > 20
    // val2 的值就等于 20
    longlong val2 = (*right)->val_int();
    if (current_thd->is_error()) return 0;
    // where 条件操作符右边的值不为 NULL
    // 才进入 if 分支
    if (!(*right)->null_value) {
      // 到这里,where 条件操作符左右两边的值都不为 NULL
      // 把 where 条件的 null_value 设置为 false
      if (set_null) owner->null_value = false;
      // 接下来 3 行代码
      // 比较 where 条件操作符左右两边的值的大小
      if (val1 < val2) return -1;
      if (val1 == val2) return 0;
      return 1;
    }
  }
  // 如果执行到下面这行代码
  // 说明 where 条件操作符左右两边的值
  // 至少有一个是 NULL
  // 把 where 条件的 null_value 设置为 true
  if (set_null) owner->null_value = true;
  return -1;
}

我们以 id = 2、3 的两条记录和示例 SQL 的 where 条件 i2 > 20 为例介绍 compare_int_signed() 的逻辑:

图片

对于 where 条件 i2 > 20,longlong val1 = (*left)->val_int() 中的 *left 表示 i2 字段。

读取 id = 2 的记录:

i2 字段值为 NULL,if (!(*left)->null_value) 条件不成立,执行流程直接来到 if (set_null) owner->null_value = true,把 where 条件的 null_value 设置为 true,表示对于当前读取的记录,where 条件包含 NULL 值。

然后,return -1,compare_int_signed() 方法执行结束。

读取 id = 3 记录:

i2 字段值为 31(即 val1 = 31),if (!(*left)->null_value) 条件成立,执行流程进入该 if 分支。

对于 where 条件 i2 > 20,longlong val2 = (*right)->val_int() 中的 *right 表示大于号右边的 20(即 val2 = 20),if (!(*right)->null_value) 条件成立,进入该 if 分支:

if (set_null) owner->null_value = false,把 where 条件的 null_value 设置为 false,表示对于当前读取的记录,where 条件不包含 NULL 值。

  • if (val1 < val2),val1 = 31 大于 val2 = 20,if 条件不成立。
  • if (val1 == val2),val1 = 31 大于 val2 20,if 条件不成立。
  • return 1,因为 val1 = 31 大于 val2 = 20,返回 1,表示当前读取的记录匹配 where 条件 i2 > 20。

Arg_comparator::compare()

// sql/item_cmpfunc.h
inline int compare() { return (this->*func)(); }

Arg_comparator::compare() 只有一行代码,就是调用 *func 方法,比较两个值的大小。

func 属性保存了用于比较两个值大小的方法的地址,在 Arg_comparator::set_cmp_func(...) 中赋值。

对于示例 SQL 来说,where 条件中的 i1、i2 字段类型都是 int,func 属性保存的是用于比较两个整数大小的 Arg_comparator::compare_int_signed() 方法的地址。(this->*func)() 调用的方法就是 Arg_comparator::compare_int_signed()。

Item_func_gt::val_int()

// sql/item_cmpfunc.cc
longlong Item_func_gt::val_int() {
  assert(fixed == 1);
  int value = cmp.compare();
  return value > 0 ? 1 : 0;
}

这里调用的 cmp.compare() 就是上一小节介绍的 Arg_comparator::compare() 方法。

对于示例 SQL 来说,Arg_comparator::compare() 会调用 Arg_comparator::compare_int_signed() 方法,返回值只有 3 种:

  • -1:表示 where 条件操作符左边的值小于右边的值。
  • 0:表示 where 条件操作符左边的值等于右边的值。
  • 1:表示 where 条件操作符左边的值大于右边的值。

我们以 id = 3 的记录和示例 SQL 的 where 条件 i2 > 20 为例,介绍 Item_func_gt::val_int() 的逻辑:

图片

i2 字段值为 31,对 where 条件 i2 > 20 调用 cmp.compare(),得到的返回值为 1(即 value = 1)。

value > 0 ? 1 : 0 表达式的值为 1,这就是 Item_func_ge::val_int() 的返回值,表示 id = 3 的记录匹配 where 条件 i2 > 20。

Item_cond_and::val_int()

// sql/item_cmpfunc.cc
longlong Item_cond_and::val_int() {
  assert(fixed == 1);
  // and 连接的 N 个 where 条件都保存到 list 中
  // 根据 list 构造迭代器
  List_iterator_fast<Item> li(list);
  Item *item;
  null_value = false;
  // 迭代 where 条件
  while ((item = li++)) {
    if (!item->val_bool()) {
      if (ignore_unknown() || !(null_value = item->null_value))
        return 0;  // return false
    }
    if (current_thd->is_error()) return error_int();
  }
  return null_value ? 0 : 1;
}

Item_cond_and::val_int() 的逻辑:

  • 判断当前读取的记录是否匹配 Item_cond_and 对象所代表的 and 连接的 N 个 where 条件(N >= 2)。
  • 如果对每个条件调用 item->val_bool() 的返回值都是 true,说明记录匹配 and 连接的 N 个 where 条件。
  • 如果对某一个或多个条件调用 item->val_bool() 的返回值是 false,就说明记录不匹配 and 连接的 N 个 where 条件。

由于 if (ignore_unknown() || !(null_value = item->null_value)) 中的 ignore_unknown() 用于控制 where 条件中包含 NULL 值时怎么处理,我们需要展开介绍 Item_cond_and::val_int() 的代码。

想要深入了解 Item_cond_and::val_int() 代码细节的读者朋友,可以做个心理建设:内容有点长(但不会太长)。

首先,我们来看一下 null_value = false:

null_value 的初始值被设置为 false,表示 and 连接的 N 个 where 条件中,还没出现哪个 where 条件包含 NULL 值的情况(毕竟还啥都没干)。

null_value 比较重要,它有可能最终决定 Item_cond_and::val_int() 的返回值(后面会介绍)。

然后,再来看看 while 循环的逻辑,这块内容会有一点点多:

while 循环迭代 and 连接的 N 个 where 条件。

每迭代一个 where 条件,都调用 item->val_bool() 方法,判断当前读取的记录是否匹配该条件。

如果 val_bool() 的返回值是 true,说明记录匹配该条件,进入下一轮循环,迭代下一个 where 条件(如果有的话)。

if (current_thd->is_error()),这行代码表示执行过程中出现了错误,我们先忽略它。

如果 val_bool() 的返回值是 false,说明记录不匹配该条件。

接下来是进入下一轮循环,还是执行 return 0 结束 Item_cond_and::val_int() 方法,就要由 if (ignore_unknown() || !(null_value = item->null_value)) 决定了。

展开介绍 if (ignore_unknown() || ...) 之前,先来看看 ignore_unknown() 的定义:

class Item_cond : public Item_bool_func {
  ...
  /// Treat UNKNOWN result like FALSE 
  /// because callers see no difference
  bool ignore_unknown() const { return abort_on_null; }
  ...
}

从代码注释可以看到,ignore_unknown() 用于决定是否把 UNKNOWN 当作 FALSE 处理。

那么,什么是 UNKNOWN?

在 MySQL 中,NULL 会被特殊对待。NULL 和任何值(包含 NULL 本身)通过关系操作符(=、>、<、...)比较,得到的结果都是 NULL,这个结果就被认为是 UNKNOWN。

如果想知道某个值是否为 NULL,只能使用 IS NULL、IS NOT NULL 进行判断。

说完了 ignore_unknown(),我们回到 if (ignore_unknown() || !(null_value = item->null_value)),它包含两个表达式:

  • ignore_unknown()
  • !(null_value = item->null_value))

如果 ignore_unknown() 的返回值为 true,if 条件成立,执行流程就会进入 if 分支,执行 return 0,Item_cond_and::val_int() 方法的执行流程就此结束,表示当前读取的记录不匹配 and 连接的 N 个 where 条件。

如果 ignore_unknown() 的返回值为 false,那么还需要再判断 !(null_value = item->null_value)) 的值是 true 还是 false。

我们先分解一下 !(null_value = item->null_value)),其中包含 2 个步骤:

  • null_value = item->null_value
  • !null_value

如果 item->null_value 的值为 false,赋值给 null_value 之后,!null_value 的值为 true,if 条件成立,执行流程就会进入 if (ignore_unknown() || ...) 分支,执行 return 0,Item_cond_and::val_int() 方法的执行流程就此结束,表示当前读取的记录不匹配 and 连接的 N 个 where 条件。

item->null_value = false,表示对于当前读取的记录,where 条件不包含 NULL 值。

如果 item->null_value 的值为 true,赋值给 null_value 之后,!null_value 的值为 false,即 !(null_value = item->null_value)) 的值为 false,if 条件不成立,执行流程不会进入 if (ignore_unknown() || ...) 分支,也就不会执行 return 0 了,接下来就会进入下一轮循环,迭代下一个 where 条件(如果有的话)。

item->null_value = true,表示对于当前读取的记录,where 条件包含 NULL 值。

最后,再来看看 return null_value ? 0 : 1:

while 循环迭代完 and 连接的 N 个 where 条件之前,如果 Item_cond_and::val_int() 方法的执行流程都没有被 while 代码块中包含的 return 0 提前结束,执行流程就会来到 return null_value ? 0 : 1。

有两种场景会导致这种情况的出现:

场景 1:

while 循环迭代 and 连接的 N 个 where 条件的过程中,对每个条件调用 item->val_bool() 的返回值都是 true。

此时,null_value 属性的值为 false,null_value ? 0 : 1 表达式的值为 1,说明当前读取的记录匹配 and 连接的 N 个 where 条件。

场景 2:

while 循环迭代 and 连接的 N 个 where 条件的过程中,某个条件同时满足以下 4 个要求:

调用 item->val_bool() 的返回值是 false,说明当前读取的记录不匹配该条件。

ignore_unknown() 的返回值也是 false,表示包含 NULL 值的 where 条件的比较结果(UNKNOWN)不按 false 处理,而是要等到 while 循环结束之后,根据 null_value 属性的值(true 或 false)算总帐。

这是由 Item_cond_and 对象控制的行为,而不是 and 连接的某个 where 条件控制的行为。

!(null_value = item->null_value)) 表达式的值为 false,说明该条件包含 NULL 值,那么它就是 ignore_unknown() = false 时需要等到 while 循环结束之后,根据 null_value 属性的值算总帐的条件。

该条件之后的其它 where 条件,不会导致 while 循环被提前中止(这样执行流程才能来到 return null_value ? 0 : 1)。

此时,null_value 属性的值为 true,null_value ? 0 : 1 表达式的值为 0,说明当前读取的记录不匹配 and 连接的 N 个 where 条件。

Item_func_eq::val_int()

// sql/item_cmpfunc.cc
longlong Item_func_eq::val_int() {
  assert(fixed == 1);
  int value = cmp.compare();
  return value == 0 ? 1 : 0;
}

这里调用的 cmp.compare() 就是前面介绍的 Arg_comparator::compare() 方法。

对于示例 SQL 来说,Arg_comparator::compare() 调用的是 Arg_comparator::compare_int_signed() 方法,返回值只有 3 种:

  • -1:表示 where 条件操作符左边的值小于右边的值。
  • 0:表示 where 条件操作符左边的值等于右边的值。
  • 1:表示 where 条件操作符左边的值大于右边的值。

我们以 id = 5 的记录和示例 SQL 的 where 条件 i1 = 50 为例,介绍 Item_func_eq::val_int() 的逻辑:

图片

i1 字段值为 50,对 where 条件 i1 = 50 调用 cmp.compare(),得到的返回值为 0(即 value = 0)。

value == 0 ? 1 : 0 表达式的值为 1,这就是 Item_func_eq::val_int() 的返回值,表示 id = 5 的记录匹配 where 条件 i1 = 50。

Item_cond_or::val_int()

// sql/item_cmpfunc.cc
longlong Item_cond_or::val_int() {
  assert(fixed == 1);
  List_iterator_fast<Item> li(list);
  Item *item;
  null_value = false;
  while ((item = li++)) {
    if (item->val_bool()) {
      null_value = false;
      return 1;
    }
    if (item->null_value) null_value = true;
    ...
  }
  return 0;
}

我们以 id = 8 的记录和示例 SQL 的 where 条件 i1 = 50 or i1 = 80 为例,介绍 Item_cond_or::val_int() 的逻辑:

图片

Item_cond_or 对象的 list 属性包含 2 个条件:i1 = 50、i1 = 80,List_iterator_fastli(list) 根据 list 构造一个迭代器。

对于 id = 8 的记录,i1 字段值为 80,while 循环每次迭代一个 where 条件:

第 1 次迭代,对 where 条件 i1 = 50 调用 item->val_bool(),返回值为 false,不进入 if (item->val_bool()) 分支。

if (item->null_value) 条件不成立,不执行 null_value = true。

第 2 次迭代,对 where 条件 i1 = 80 调用 item->val_bool(),返回值为 true,进入 if (item->val_bool()) 分支。

设置 Item_cond_or 对象的 null_value 属性值为 false,表示 Item_cond_or 所代表的 or 连接的 where 条件(i1 = 50、i1 = 80)都不包含 NULL 值。

return 1,这就是 Item_cond_or::val_int() 的返回值,表示 id = 8 的记录匹配 where 条件 i1 = 50 or i1 = 80。

总结

本文介绍了 SQL 的 where 条件中包含 and、or 的实现逻辑:

从存储引擎读取一条记录之后,对 and 连接的 N 个 where 条件(N >= 2)调用 item->val_bool() 的返回值必须全部等于 true,记录才匹配 and 连接的 N 个 where 条件。

Item_cond_and::val_int() 的代码不多,但是这个方法中调用了 ignore_known() 用于控制怎么处理 where 条件包含 NULL 值的场景,代码细节并不太好理解,所以花了比较长的篇幅介绍 Item_cond_and::val_int() 方法的逻辑,需要多花点时间去理解其中的逻辑。

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

本文转载自微信公众号「一树一溪」



Tags:MySQL   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  Search: MySQL  点击:(6)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  Search: MySQL  点击:(11)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  Search: MySQL  点击:(10)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  Search: MySQL  点击:(24)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10  Search: MySQL  点击:(6)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  Search: MySQL  点击:(28)  评论:(0)  加入收藏
MySQL数据恢复,你会吗?
今天分享一下binlog2sql,它是一款比较常用的数据恢复工具,可以通过它从MySQL binlog解析出你要的SQL,并根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。主要...【详细内容】
2024-02-22  Search: MySQL  点击:(45)  评论:(0)  加入收藏
如何在MySQL中实现数据的版本管理和回滚操作?
实现数据的版本管理和回滚操作在MySQL中可以通过以下几种方式实现,包括使用事务、备份恢复、日志和版本控制工具等。下面将详细介绍这些方法。1.使用事务:MySQL支持事务操作,可...【详细内容】
2024-02-20  Search: MySQL  点击:(53)  评论:(0)  加入收藏
为什么高性能场景选用Postgres SQL 而不是 MySQL
一、 数据库简介 TLDR;1.1 MySQL MySQL声称自己是最流行的开源数据库,它属于最流行的RDBMS (Relational Database Management System,关系数据库管理系统)应用软件之一。LAMP...【详细内容】
2024-02-19  Search: MySQL  点击:(38)  评论:(0)  加入收藏
MySQL数据库如何生成分组排序的序号
经常进行数据分析的小伙伴经常会需要生成序号或进行数据分组排序并生成序号。在MySQL8.0中可以使用窗口函数来实现,可以参考历史文章有了这些函数,统计分析事半功倍进行了解。...【详细内容】
2024-01-30  Search: MySQL  点击:(54)  评论:(0)  加入收藏
▌简易百科推荐
MySQL 核心模块揭秘
server 层会创建一个 SAVEPOINT 对象,用于存放 savepoint 信息。binlog 会把 binlog offset 写入 server 层为它分配的一块 8 字节的内存里。 InnoDB 会维护自己的 savepoint...【详细内容】
2024-04-03  爱可生开源社区    Tags:MySQL   点击:(6)  评论:(0)  加入收藏
MySQL 核心模块揭秘,你看明白了吗?
为了提升分配 undo 段的效率,事务提交过程中,InnoDB 会缓存一些 undo 段。只要同时满足两个条件,insert undo 段或 update undo 段就能被缓存。1. 关于缓存 undo 段为了提升分...【详细内容】
2024-03-27  爱可生开源社区  微信公众号  Tags:MySQL   点击:(11)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  MySQL学习  微信公众号  Tags:MySQL   点击:(10)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  字节跳动技术团队    Tags:ByteHouse   点击:(24)  评论:(0)  加入收藏
MySQL自增主键一定是连续的吗?
测试环境:MySQL版本:8.0数据库表:T (主键id,唯一索引c,普通字段d)如果你的业务设计依赖于自增主键的连续性,这个设计假设自增主键是连续的。但实际上,这样的假设是错的,因为自增主键不...【详细内容】
2024-03-10    dbaplus社群  Tags:MySQL   点击:(6)  评论:(0)  加入收藏
准线上事故之MySQL优化器索引选错
1 背景最近组里来了许多新的小伙伴,大家在一起聊聊技术,有小兄弟提到了MySQL的优化器的内部策略,想起了之前在公司出现的一个线上问题,今天借着这个机会,在这里分享下过程和结论...【详细内容】
2024-03-07  转转技术  微信公众号  Tags:MySQL   点击:(28)  评论:(0)  加入收藏
MySQL数据恢复,你会吗?
今天分享一下binlog2sql,它是一款比较常用的数据恢复工具,可以通过它从MySQL binlog解析出你要的SQL,并根据不同选项,可以得到原始SQL、回滚SQL、去除主键的INSERT SQL等。主要...【详细内容】
2024-02-22  数据库干货铺  微信公众号  Tags:MySQL   点击:(45)  评论:(0)  加入收藏
如何在MySQL中实现数据的版本管理和回滚操作?
实现数据的版本管理和回滚操作在MySQL中可以通过以下几种方式实现,包括使用事务、备份恢复、日志和版本控制工具等。下面将详细介绍这些方法。1.使用事务:MySQL支持事务操作,可...【详细内容】
2024-02-20  编程技术汇    Tags:MySQL   点击:(53)  评论:(0)  加入收藏
MySQL数据库如何生成分组排序的序号
经常进行数据分析的小伙伴经常会需要生成序号或进行数据分组排序并生成序号。在MySQL8.0中可以使用窗口函数来实现,可以参考历史文章有了这些函数,统计分析事半功倍进行了解。...【详细内容】
2024-01-30  数据库干货铺  微信公众号  Tags:MySQL   点击:(54)  评论:(0)  加入收藏
mysql索引失效的场景
MySQL中索引失效是指数据库查询时无法有效利用索引,这可能导致查询性能显著下降。以下是一些常见的MySQL索引失效的场景:1.使用非前导列进行查询: 假设有一个复合索引 (A, B)。...【详细内容】
2024-01-15  小王爱编程  今日头条  Tags:mysql索引   点击:(85)  评论:(0)  加入收藏
站内最新
站内热门
站内头条