返回顶部
首页 > 资讯 > 数据库 >MySQL的join buffer原理
  • 590
分享到

MySQL的join buffer原理

MySQLjoinbuffer 2022-05-19 23:05:35 590人浏览 薄情痞子
摘要

一、Mysql的join buffer 在mysql对于join操作的处理过程中,join buffer是一个重要的概念,也是Mysql对于table join的一个重要的优化手段。虽然这个概念实现并不复杂,但是这

一、Mysql的join buffer

mysql对于join操作的处理过程中,join buffer是一个重要的概念,也是Mysql对于table join的一个重要的优化手段。虽然这个概念实现并不复杂,但是这个是实现MySQL join连接优化的一个重要方法,在"暴力"连接的时候可以极大提高join查询的效率。

关于这个概念的权威说明当然是来自MySQL文档中对于这个概念的说明,说明的文字不多,但是言简意赅,说明了这个优化的主要实现思想:
Assume you have the following join:


Table name      Type
t1              range
t2              ref
t3              ALL
The join is then done as follows:
 
- While rows in t1 matching range
 - Read through all rows in t2 according to reference key
  - Store used fields from t1, t2 in cache
  - If cache is full
    - Read through all rows in t3
      - Compare t3 row against all t1, t2 combinations in cache
        - If row satisfies join condition, send it to client
    - Empty cache
 
- Read through all rows in t3
 - Compare t3 row against all stored t1, t2 combinations in cache
   - If row satisfies join condition, send it to client

二、join buffer cache存储空间的分配

下面函数中table_count表示的就是所有join table中在该table之前的非const table数量,因为这个table要缓存自己之前所有table中的每条记录中"需读取"(tables[i].table->read_set置位)。

其中两重循环每次执行都是复制下需要缓存的field的描述结构(及其对应的数据源),或者说,二重循环只是为了赋值和保存元数据,而最后的cache->buff=(uchar*) my_malloc(size,MYF(0))才是真正的分配满足条件的记录内容。


static int
join_init_cache(THD *thd,JOIN_TAB *tables,uint table_count)
{
……
  for (i=0 ; i < table_count ; i++)
  {
    bool have_bit_fields= FALSE;
    uint null_fields=0,used_fields;
    Field **f_ptr,*field;
    MY_BITMAP *read_set= tables[i].table->read_set;
    for (f_ptr=tables[i].table->field,used_fields=tables[i].used_fields ;
 used_fields ;
 f_ptr++)
    {
      field= *f_ptr;
      if (bitmap_is_set(read_set, field->field_index))
      {
used_fields--;
length+=field->fill_cache_field(copy);
……
      }
  }
 
  cache->length=length+blobs*sizeof(char*);
  cache->blobs=blobs;
  *blob_ptr=0; 
  size=max(thd->variables.join_buff_size, cache->length);
  if (!(cache->buff=(uchar*) my_malloc(size,MYF(0))))
    DBUG_RETURN(1);  
  cache->end=cache->buff+size;
  reset_cache_write(cache);
  DBUG_RETURN(0);
}

三、普通的多表查询实现

这个"普通"当然也可以理解为"朴素"、"直观"的意思,也是大部分情况下的执行流程。普通查询其实就是对于对于各个表格进行递归调用,和矩阵的乘法一样一样的,这个对应非常直观,也非常通用。

而这个常规的查询动作就是通过sub_select函数来实现,这个函数本质性上是执行


tsecer_select()
{
for (r = first ; r != end ; r = next)
{
if(sofartest())
{
nexttable.tsecer_select()
}
}
}

其中的sofartest()表示"使用所有当前已读取表格可以进行的判断",也就是where中下推的表达式。例如 select * from a, b where a.a > 10 and b.b + a.a = 10,在a表读取之后,其实已经可以执行 a.a > 10的判断。当然这个是一个甚至算不上伪代码的描述方法,而真正的代码对应为:


enum_nested_loop_state
sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
……
    error= (*join_tab->read_first_record)(join_tab);
    rc= evaluate_join_record(join, join_tab, error);
……
  while (rc == NESTED_LOOP_OK)
  {
    error= info->read_record(info);
    rc= evaluate_join_record(join, join_tab, error);
  }
……
  return rc;
}
static enum_nested_loop_state
evaluate_join_record(JOIN *join, JOIN_TAB *join_tab,
                     int error)
{
……
  if (select_cond)
  {
    select_cond_result= test(select_cond->val_int());
 
    
    if (join->thd->is_error())
      return NESTED_LOOP_ERROR;
  }
……
    if (found)
    {
      enum enum_nested_loop_state rc;
      
      rc= (*join_tab->next_select)(join, join_tab+1, 0);
      if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
        return rc;
      if (join->return_tab < join_tab)
        return NESTED_LOOP_OK;
      
      if (not_used_in_distinct && found_records != join->found_records)
        return NESTED_LOOP_NO_MORE_ROWS;
    }
……
}

这里可以看到,这个地方是一个递归,用来产生一个笛卡尔叉乘集合,从程序实现和数学表达上看都非常简洁可爱。
在MySQL的实现中,tsecer_select函数中的for循环大致相当sub_select中的while循环,而tsecer_select函数中循环体内的内容被放在了evaluate_join_record函数中,其中的sofartest对应evaluate_join_record::test(select_cond->val_int());tsecer_select中的nexttable.tsecer_select()语句对应evaluate_join_record::(*join_tab->next_select)(join, join_tab+1, 0)。

四、join buffer的select实现

当使用join buffer cache时,next_select函数指向sub_select_cache


enum_nested_loop_state
sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
  enum_nested_loop_state rc;
 
  if (end_of_records)
  {
    rc= flush_cached_records(join,join_tab,FALSE);
    if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS)
      rc= sub_select(join,join_tab,end_of_records);
    return rc;
  }
  if (join->thd->killed) // If aborted by user
  {
    join->thd->send_kill_message();
    return NESTED_LOOP_KILLED;                   
  }
  if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
  {
    if (!store_record_in_cache(&join_tab->cache))
      return NESTED_LOOP_OK;                     // There is more room in cache
    return flush_cached_records(join,join_tab,FALSE);
  }
  rc= flush_cached_records(join, join_tab, TRUE);
  if (rc == NESTED_LOOP_OK || rc == NESTED_LOOP_NO_MORE_ROWS)
    rc= sub_select(join, join_tab, end_of_records);
  return rc;
}

结合MySQL文档中的说明,这里的代码意义就比较明显。开始对于end_of_records的判断对应的就是


    if (!store_record_in_cache(&join_tab->cache))
      return NESTED_LOOP_OK;                     // There is more room in cache
    return flush_cached_records(join,join_tab,FALSE);

对应


  - Store used fields from t1, t2 in cache
  - If cache is full

其中store_record_in_cache函数会判断cache是否已满,如果cache可以放入更多的缓存,则把之前table的组合记录存储在cache中,并返回NESTED_LOOP_OK。注意:这个地方可以说是整个cache优化的关键,因为这里并没有启动对于table的扫描。反过来说,如果cache数据已经满了,则调用flush_cached_records函数来进行下面的流程


    - Read through all rows in t3
      - Compare t3 row against all t1, t2 combinations in cache
        - If row satisfies join condition, send it to client
    - Empty cache

这个流程的特殊之处在于遍历的驱动是通过对于table的每一条记录来和cache中所有t1、t2组合来进行比较,来判断是否满足下推where条件(If row satisfies join condition),则执行join_tab->next_select函数(send it to client)。


static enum_nested_loop_state
flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last)
{
……
  info= &join_tab->read_record;
  do
  {//遍历t3表格所有记录
……
        for (i=(join_tab->cache.records- (skip_last ? 1 : 0)) ; i-- > 0 ;)
        {//遍历cache中所有t1、t2记录组合
          read_cached_record(join_tab);
          skip_record= FALSE;
          if (select && select->skip_record(join->thd, &skip_record))
          {//
            reset_cache_write(&join_tab->cache);
            return NESTED_LOOP_ERROR;
          }
          if (!skip_record)
          {//满足下推的where条件
//执行下一个table的遍历
            rc= (join_tab->next_select)(join,join_tab+1,0);
            if (rc != NESTED_LOOP_OK && rc != NESTED_LOOP_NO_MORE_ROWS)
            {
              reset_cache_write(&join_tab->cache);
              return rc;
            }
          }
……
  } while (!(error=info->read_record(info)));

五、举例来说明下这个流程

这个实现的核心思想并不复杂,结合具体的例子来看就更加的简单直观。
举个例子,其中使用两个简单的table,其中分别存储一个x,和y的值,我们希望通过一个join操作来计算这两个表格中所有的满足 x

x + y

y == 5 * 5,也就是我们最常见的"勾三股四弦五"这样的经典勾股数数值。


mysql> create table harry (x int);
Query OK, 0 rows affected (0.03 sec)
 
mysql> insert harry values (1),(2),(3),(4),(5);
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
 
mysql> create table tsecer (y int);                   
Query OK, 0 rows affected (0.01 sec)
 
mysql> insert tsecer values (1),(2),(3),(4),(5);     
Query OK, 5 rows affected (0.00 sec)
Records: 5  Duplicates: 0  Warnings: 0
 
mysql> explain select * from harry, tsecer where x * x + y * y = 5 * 5;
+----+-------------+--------+------+---------------+------+---------+------+------+--------------------------------+
| id | select_type | table  | type | possible_keys | key  | key_len | ref  | rows | Extra                          |
+----+-------------+--------+------+---------------+------+---------+------+------+--------------------------------+
|  1 | SIMPLE      | harry  | ALL  | NULL          | NULL | NULL    | NULL |    5 |                                |
|  1 | SIMPLE      | tsecer | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where; Using join buffer |
+----+-------------+--------+------+---------------+------+---------+------+------+--------------------------------+
2 rows in set (0.00 sec)
 
mysql>

1、不使用joinbuffer

在不使用join buffer的情况下,对于harry表的每个x值,对应的tsecer表都要进行一次全表扫描,之后使用这个x和y的组合判断是否满足x

x + y

y == 5 * 5这条件。由于x总共有5个值,所以tsecer需要全表扫描的次数就是5次。

2、使用joinbuffer

对于x的每个值,tsecer表在执行的时候先是把这个值缓存到joinbuffer中,如果buffer缓冲内容非空,那么把此时的x的值存储在buffer中后直接返回;当join buffer满或者是最后一条记录的时候,此时开始启动对于tsecer表的扫描,对于tsecer表中读取的每一个记录,结合前面缓存的每一个记录,看是否满足自己判断条件。
对于我们看到的例子,这个地方harry表的5个值都在缓存中,在tsecer表的扫描过程中,对于从tsecer中读取的每一条记录,结合缓存中的“每一条”缓存,判断这个组合结果是否满足条件,如果任意一个组很满足,那么就继续next_select。
在这个使用buffer的例子中,可以看到这个地方只是对于tsecer表进行了一次扫描,而通常来说,数据库的扫描代码是最高的(因为要涉及到磁盘读取),这样使用buffer的方式将tsecer表的扫描降低为1次,所以这个效率提高很多,特别是在涉及到的多个table,并且/或者 每个table中的记录数量都很多的情况下。

3、cache可以优化的原因

本质上说,这个效率提高的原因在于提高了从table中获得的每条记录的“利用率”,在使用直观扫描方式时,table的全表扫描只是和一个组合进行匹配,而使用buffer之后则是和cache中的所有组合进行匹配。

以上就是MySQL的join buffer原理的详细内容,更多关于MySQL join buffer的资料请关注自学编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: MySQL的join buffer原理

本文链接: https://lsjlt.com/news/10306.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • MySQL的join buffer原理
    一、MySQL的join buffer 在MySQL对于join操作的处理过程中,join buffer是一个重要的概念,也是MySQL对于table join的一个重要的优化手段。虽然这个概念实现并不复杂,但是这...
    99+
    2022-05-19
    MySQL join buffer
  • MySQL中join buffer指的是什么意思
    小编给大家分享一下MySQL中join buffer指的是什么意思,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1、说明在MySQL对于join操作的处理过程中,...
    99+
    2023-06-15
  • 解析MySQL join查询的原理
    MySQL用Nested-Loop Join算法实现join查询 区分驱动表和被驱动表,以驱动表的结果集为循环的基础,访问被驱动表过滤数据,然后合并结果,驱动表在外循环、被驱动表在内...
    99+
    2024-04-02
  • 浅谈mysql join底层原理
    目录join算法驱动表和非驱动表的区别1、Simple Nested-Loop Join,简单嵌套-无索引的情况2、Index Nested-Loop Join-有索引的情况3、Block Nested-Loop J...
    99+
    2022-05-30
    mysql join底层原理 mysql join
  • mysql的Buffer Pool存储及原理解析
    目录一、前言1、buffer pool是什么2、buffer pool的工作流程3、buffer pool缓冲池和查询缓存(query cache)二、buffer pool的内存数...
    99+
    2024-04-02
  • 一文详解MySQL Join使用原理
    目录Join的类型Join原理Simpe Nested-Loop JoinIndex Nested-Loop Joinblock Nested-Loop JoinJoin优化Join的类型 left join,以左表为驱...
    99+
    2023-04-25
    MySQL Join原理 MySQL Join
  • 一文详解MySQL Join使用原理
    目录Join的类型Join原理Simpe Nested-Loop JoinIndex Nested-Loop JoinBlock Nested-Loop JoinJoin优化Join...
    99+
    2023-05-17
    MySQL Join原理 MySQL Join
  • MySQL JOIN关联查询的原理及优化
    目录1 关联查询的执行2 没有索引的算法1 关联查询的执行 关联查询的执行过程是:先遍历关联表t1(驱动表,全表扫描),然后根据从表t1中取出的每行数据中的a值,去表t2(被关联表,被驱动表)中查找满足条件的记录,可以走...
    99+
    2022-08-22
    MySQLJOIN关联查询 MySQLJOIN
  • Mysql连接join查询的原理是什么
    Mysql连接join查询的原理是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Mysql连接(join)查询1、基本概念将两个表的每...
    99+
    2024-04-02
  • Thread的Join方法原理
    Thread的join方法是用来等待一个线程执行完毕的方法。当一个线程调用另一个线程的join方法时,它会等待该线程执行完毕后再继续...
    99+
    2023-09-22
    Join
  • MySQL数据库连接查询 join原理
    目录1、连接查询的分类2、交叉连接2.1、原理2.2、基本语法2.3、应用3、内连接3.1、原理3.2、基本语法3.3、应用4、外连接4.1、原理4.2、基本语法4.3、特点4.4、...
    99+
    2024-04-02
  • 浅谈mysql join底层原理是什么
    本篇内容主要讲解“浅谈mysql join底层原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“浅谈mysql join底层原理是什么”吧!目录join算法驱动表和非驱动表的区别Simpl...
    99+
    2023-06-20
  • MYSQL数据库基础之Join操作原理
    Join使用的是Nested-Loop Join算法,Nested-Loop Join有三种 select * from t1 join t2 on t1.a = t2.a; -- a 100条数据, ...
    99+
    2022-05-27
    mysql的join用法 mysql join查询 mysql的join原理
  • MYSQL数据库中Join的操作原理是什么
    今天就跟大家聊聊有关MYSQL数据库中Join的操作原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Join使用的是Nested-Loop Join算法,Nested-Loo...
    99+
    2023-06-20
  • mysql的buffer pool 和 change buffer
    线上三张图吧:   一、innodb系统架构图  mysql的buffer pool 和 change buffer原文地址:https://www.cnblogs.com/mmh760/p/13500691.html...
    99+
    2016-11-06
    mysql的buffer pool change buffer 数据库入门 数据库基础教程 数据库 mysql
  • java中fork-join的原理解析
    ForkJoinTask就是ForkJoinPool里面的每一个任务。他主要有两个子类:RecursiveAction和RecursiveTask。然后通过fork()方法去分配任务...
    99+
    2024-04-02
  • 如何理解MySQL的Buffer Pool
    本篇内容介绍了“如何理解MySQL的Buffer Pool”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!前...
    99+
    2024-04-02
  • 怎么理解MySQL的Insert buffer
    本篇内容介绍了“怎么理解MySQL的Insert buffer”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成...
    99+
    2024-04-02
  • Java NIO Buffer实现原理详解
    目录1、Buffer的继承体系2、Buffer的操作API使用案例3、Buffer的基本原理4、allocate方法初始化一个指定容量大小的缓冲区5、slice方法缓冲区分片6、只读...
    99+
    2024-04-02
  • Fork跟Join原理是什么
    这篇文章将为大家详细讲解有关Fork跟Join原理是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。只听到P8大佬不急不慢问道:谈谈对JDK并发工具的认识...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作