2023-06-05 stonedb-在派生表的场景查询为空无法传递默认值-问题分析

news2024/9/24 5:32:52

摘要:

stonedb-在派生表的场景查询为空无法传递默认值-问题分析.

本文对该问题的成因, 相关功能的代码设计, 在下一步设计时如何应对这种问题, 做相关的分析。

https://stoneatom.yuque.com/staff-ft8n1u/lsztbl/rxlhws22n0f1otxn/edit#AqyB

相关ISSUE: https://github.com/stoneatom/stonedb/issues/1784

查询SQL:

SELECT 'aaaaa' inner_code,
       a.exchange_rate,
       sum(a.balance) AS balance,
       'b' std_balance
  FROM (SELECT b.inner_code,
               CASE
                 WHEN r.row_id IS NOT NULL THEN
                  r.exchange_rate
                 ELSE
                  1.0000
               END AS exchange_rate,
               c.fiscal_date,
               c.balance
          FROM c1md_bank_acct a
          JOIN c1md_company b
            ON a.company_id = b.row_id
           AND b.deleted_flag = '0'
          LEFT JOIN c1cd_exchange_rate r
            ON a.currency_id = r.currency_id
           AND r.deleted_flag = '0'
           AND r.using_flag = '1'
           AND r.inure_date <= now()
           AND (r.abate_date >= now() OR r.abate_date IS NULL)
          LEFT JOIN (SELECT b.account_id, b.fiscal_date, b.balance
                      FROM (SELECT account_id, max(fiscal_date) fiscal_date
                              FROM c1am_acct_day
                             WHERE deleted_flag = '0'
                             GROUP BY account_id) a
                      JOIN c1am_acct_day b
                        ON a.account_id = b.account_id
                       AND a.fiscal_date = b.fiscal_date
                     WHERE b.deleted_flag = '0') c
            ON a.row_id = c.account_id
         WHERE a.deleted_flag = '0'
           AND a.acct_flag IN ('2', '4')
           AND a.company_id IN (3000000000027265)) a;

处理该问题的思路分析:

  1. 对于复杂的查询SQL, 涉及的操作符过多, 首先要进行问题定位
    1. 缩减到具体哪些操作符的执行出现了问题
    2. 这个过程涉及条件削减, 排查, 甚至摸索试探
  2. 找到出问题的操作符的对应的代码实现, 分析该处代码的设计意图,以及为什么会导致当前查询出错
    1. 该过程涉及架构能力,代码能力,以及理解他人代码设计意图的能力
    2. 对于复杂的代码, 牵扯到的因素过多,存在试探来猜测设计意图的情况, 但是一定需要事实的验证来辅助对设计意图的定位
  3. 重新对该操作符做设计
    1. 需要注意要兼容此前的设计意图
    2. 为了避免出现类似的问题, 需要评估代码修改对其他模块的影响

削减查询SQL的操作符

tianmu引擎的查询SQL

create table t1 (age int) engine=tianmu;

select case when  age IS NOT NULL THEN age else 33 end,  sum(age) from (  select * from t1) ta;

innodb引擎的查询SQL

create table t1_innodb (age int) engine=innodb;

select case when  age IS NOT NULL THEN age else 33 end,  sum(age) from (  select * from t1_innodb) ta;

mysql/sql和innodb执行分析:

执行过程的trace日志:

2022-10-18 mysql-5.7-开启debug日志_mysql 5.7 debug_财阀悟世的博客-CSDN博客

https://download.csdn.net/download/adofsauron/87864107

思路分析:

  1. 该debug的trace日志包含这个查询SQL的所有关键函数的执行过程
  2. 从这个日志中可以分析出mysql/sql层所有的查询优化和查询执行的细节
  3. 但是将问题聚焦下, 当前的目标是解决tianmu查询出错, 那么对应的就需要关注mysql/sql在处理case属性时是如何处理的
  4. 也就是说,要将重点,放在mysql/sql层是如何处理case的item的
  5. 这里需要注意, 分析case的item并不是为了要记住这块代码的处理逻辑, 而是作为一个正确处理case的解决方案的对标, 分析tianmu引擎在处理case的属性时的逻辑,从而有个逻辑处理的对比
  6. tianmu引擎对于case的属性有自己的一套处理逻辑, 在此处分析的一个歧途就是容易将mysql/sql当作唯一正确的接, 从而照抄mysql/sql的方案, 而忽略了tianmu引擎对于列属性处理的特殊性, 此特殊的地方在以下分析tianmu引擎对查询执行时具体分析

mysql/sql层的核心处理:

Item_func_case::fix_fields

调用堆栈:

#0  Item_func_case::fix_fields (this=0x7fd254005850, thd=0x7fd254000e10, ref=0x7fd254005ac0) at /root/work/stonedb-dev-20230605/sql/item_cmpfunc.cc:4001
#1  0x00000000023a8a7e in setup_fields (thd=0x7fd254000e10, ref_pointer_array=..., fields=..., want_privilege=1, sum_func_list=0x7fd254004d30, allow_sum_func=true, column_update=false)
    at /root/work/stonedb-dev-20230605/sql/sql_base.cc:9138
#2  0x00000000024650da in st_select_lex::prepare (this=0x7fd254004bd0, thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_resolver.cc:200
#3  0x000000000247165f in handle_query (thd=0x7fd254000e10, lex=0x7fd254003138, result=0x7fd254006a88, added_options=0, removed_options=0, is_optimize_after_tianmu=0, free_join_from_tianmu=0)
    at /root/work/stonedb-dev-20230605/sql/sql_select.cc:139
#4  0x0000000002427ae2 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5205
#5  0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#6  0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#7  0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#8  0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#9  0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#10 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#11 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#12 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

bool Item_func_case::fix_fields(THD *thd, Item **ref)
{
  /*
    buff should match stack usage from
    Item_func_case::val_int() -> Item_func_case::find_item()
  */
  uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2];
  bool res= Item_func::fix_fields(thd, ref);
  /*
    Call check_stack_overrun after fix_fields to be sure that stack variable
    is not optimized away
  */
  if (check_stack_overrun(thd, STACK_MIN_SIZE, buff))
    return TRUE;				// Fatal error flag is set!
  return res;
}

参数ref的数据:

(gdb) p ref[0][0]
$4 = (Item_func_case) {
  <Item_func> = {
    <Item_result_field> = {
      <Item> = {
        <Parse_tree_node> = {
          _vptr.Parse_tree_node = 0x4355418 <vtable for Item_func_case+16>, 
          contextualized = true, 
          transitional = false
        }, 
        members of Item: 
        is_expensive_cache = -1 '\377', 
        rsize = 0, 
        str_value = {
          m_ptr = 0x0, 
          m_length = 0, 
          m_charset = 0x44664e0 <my_charset_bin>, 
          m_alloced_length = 0, 
          m_is_alloced = false
        }, 
        item_name = {
          <Name_string> = {
            <Simple_cstring> = {
              m_str = 0x7fd2540065e0 "case when  age IS NOT NULL THEN age else 33 end", 
              m_length = 47
            }, <No data fields>}, 
          members of Item_name_string: 
          m_is_autogenerated = true
        }, 
        orig_name = {
          <Name_string> = {
            <Simple_cstring> = {
              m_str = 0x0, 
              m_length = 0
            }, <No data fields>}, 
          members of Item_name_string: 
          m_is_autogenerated = true
        }, 
        next = 0x7fd2540059d0, 
        max_length = 0, 
        marker = 0, 
        decimals = 0 '\000', 
        maybe_null = 0 '\000', 
        null_value = 0 '\000', 
        unsigned_flag = 0 '\000', 
        with_sum_func = 0 '\000', 
        fixed = 0 '\000', 
        collation = {
          collation = 0x44664e0 <my_charset_bin>, 
--Type <RET> for more, q to quit, c to continue without paging--
          derivation = DERIVATION_COERCIBLE, 
          repertoire = 3
        }, 
        cmp_context = 4294967295, 
        runtime_item = false, 
        derived_used = false, 
        with_subselect = 0 '\000', 
        with_stored_program = 0 '\000', 
        tables_locked_cache = false, 
        is_parser_item = true
      }, 
      members of Item_result_field: 
      result_field = 0x0
    }, 
    members of Item_func: 
    args = 0x7fd2540059b8, 
    tmp_arg = {0x8f8f8f8f8f8f8f8f, 0x8f8f8f8f8f8f8f8f}, 
    const_item_cache = 143, 
    allowed_arg_cols = 1, 
    used_tables_cache = 10344644715844964239, 
    not_null_tables_cache = 10344644715844964239, 
    arg_count = 3
  }, 
  members of Item_func_case: 
  first_expr_num = -1, 
  else_expr_num = 2, 
  cached_result_type = INT_RESULT, 
  left_result_type = INT_RESULT, 
  tmp_value = {
    m_ptr = 0x0, 
    m_length = 0, 
    m_charset = 0x44664e0 <my_charset_bin>, 
    m_alloced_length = 0, 
    m_is_alloced = false
  }, 
  ncases = 2, 
  cmp_type = 2408550287, 
  cmp_collation = {
    collation = 0x44664e0 <my_charset_bin>, 
    derivation = DERIVATION_NONE, 
    repertoire = 3
  }, 
  cached_field_type = 2408550287, 
  cmp_items = {0x0, 0x0, 0x0, 0x0, 0x0}, 
  case_item = 0x0
}

(gdb) p ( (Item_func*) ref[0]).args[0][0]
$8 = (Item_func_isnotnull) {
  <Item_bool_func> = {
    <Item_int_func> = {
      <Item_func> = {
        <Item_result_field> = {
          <Item> = {
            <Parse_tree_node> = {
              _vptr.Parse_tree_node = 0x4351ba0 <vtable for Item_func_isnotnull+16>, 
              contextualized = true, 
              transitional = false
            }, 
            members of Item: 
            is_expensive_cache = -1 '\377', 
            rsize = 0, 
            str_value = {
              m_ptr = 0x0, 
              m_length = 0, 
              m_charset = 0x44664e0 <my_charset_bin>, 
              m_alloced_length = 0, 
              m_is_alloced = false
            }, 
            item_name = {
              <Name_string> = {
                <Simple_cstring> = {
                  m_str = 0x0, 
                  m_length = 0
                }, <No data fields>}, 
              members of Item_name_string: 
              m_is_autogenerated = true
            }, 
            orig_name = {
              <Name_string> = {
                <Simple_cstring> = {
                  m_str = 0x0, 
                  m_length = 0
                }, <No data fields>}, 
              members of Item_name_string: 
              m_is_autogenerated = true
            }, 
            next = 0x7fd254005850, 
            max_length = 21, 
            marker = 0, 
            decimals = 0 '\000', 
            maybe_null = 0 '\000', 
            null_value = 0 '\000', 
            unsigned_flag = 0 '\000', 
            with_sum_func = 0 '\000', 
            fixed = 0 '\000', 
--Type <RET> for more, q to quit, c to continue without paging--
            collation = {
              collation = 0x446eae0 <my_charset_latin1>, 
              derivation = DERIVATION_NUMERIC, 
              repertoire = 1
            }, 
            cmp_context = 4294967295, 
            runtime_item = false, 
            derived_used = false, 
            with_subselect = 0 '\000', 
            with_stored_program = 0 '\000', 
            tables_locked_cache = false, 
            is_parser_item = true
          }, 
          members of Item_result_field: 
          result_field = 0x0
        }, 
        members of Item_func: 
        args = 0x7fd254005678, 
        tmp_arg = {0x7fd2540063a0, 0x8f8f8f8f8f8f8f8f}, 
        const_item_cache = 143, 
        allowed_arg_cols = 1, 
        used_tables_cache = 10344644715844964239, 
        not_null_tables_cache = 10344644715844964239, 
        arg_count = 1
      }, <No data fields>}, 
    members of Item_bool_func: 
    m_created_by_in2exists = false
  }, 
  members of Item_func_isnotnull: 
  abort_on_null = false
}

(gdb) p ( (Item_func*) ref[0]).args[1][0]
$9 = (Item_field) {
  <Item_ident> = {
    <Item> = {
      <Parse_tree_node> = {
        _vptr.Parse_tree_node = 0x43485f8 <vtable for Item_field+16>, 
        contextualized = true, 
        transitional = false
      }, 
      members of Item: 
      is_expensive_cache = -1 '\377', 
      rsize = 0, 
      str_value = {
        m_ptr = 0x0, 
        m_length = 0, 
        m_charset = 0x44664e0 <my_charset_bin>, 
        m_alloced_length = 0, 
        m_is_alloced = false
      }, 
      item_name = {
        <Name_string> = {
          <Simple_cstring> = {
            m_str = 0x7fd2540056a8 "age", 
            m_length = 3
          }, <No data fields>}, 
        members of Item_name_string: 
        m_is_autogenerated = true
      }, 
      orig_name = {
        <Name_string> = {
          <Simple_cstring> = {
            m_str = 0x0, 
            m_length = 0
          }, <No data fields>}, 
        members of Item_name_string: 
        m_is_autogenerated = true
      }, 
      next = 0x7fd2540056b0, 
      max_length = 0, 
      marker = 0, 
      decimals = 0 '\000', 
      maybe_null = 0 '\000', 
      null_value = 0 '\000', 
      unsigned_flag = 0 '\000', 
      with_sum_func = 0 '\000', 
      fixed = 0 '\000', 
      collation = {
        collation = 0x44664e0 <my_charset_bin>, 
        derivation = DERIVATION_IMPLICIT, 
--Type <RET> for more, q to quit, c to continue without paging--
        repertoire = 3
      }, 
      cmp_context = 4294967295, 
      runtime_item = false, 
      derived_used = false, 
      with_subselect = 0 '\000', 
      with_stored_program = 0 '\000', 
      tables_locked_cache = false, 
      is_parser_item = true
    }, 
    members of Item_ident: 
    orig_db_name = 0x0, 
    orig_table_name = 0x0, 
    orig_field_name = 0x7fd2540056a8 "age", 
    m_alias_of_expr = false, 
    context = 0x7fd254004c30, 
    db_name = 0x0, 
    table_name = 0x0, 
    field_name = 0x7fd2540056a8 "age", 
    cached_field_index = 4294967295, 
    cached_table = 0x0, 
    depended_from = 0x0
  }, 
  members of Item_field: 
  table_ref = 0x0, 
  field = 0x0, 
  result_field = 0x0, 
  item_equal = 0x0, 
  no_const_subst = false, 
  have_privileges = 0, 
  any_privileges = false
}

函数目的分析:

  1. 此处是构建case属性的相关列属性的过程, ref的参数包含两种列属性
    1. 参与case运算的 Item_func_isnotnull
    2. 表的列属性Item_field
  2. 此时尚未读取列属性Item_field中的值进行计算取值, 目的在于构建case列属性相关信息的完整结构

Item_func_case::val_int

调用堆栈:

#0  Item_func_case::val_int (this=0x7fd254005850) at /root/work/stonedb-dev-20230605/sql/item_cmpfunc.cc:3897
#1  0x0000000001dde09f in Item_copy_int::copy (this=0x7fd2540217b0, thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/item.cc:4868
#2  0x00000000023e3292 in copy_fields (param=0x7fd254020ff8, thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:4381
#3  0x000000000247902f in JOIN::clear (this=0x7fd254020ec0) at /root/work/stonedb-dev-20230605/sql/sql_select.cc:3365
#4  0x00000000023dfb7b in end_send_group (join=0x7fd254020ec0, qep_tab=0x7fd254021730, end_of_records=true) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:3060
#5  0x00000000023db32a in sub_select (join=0x7fd254020ec0, qep_tab=0x7fd2540215b8, end_of_records=true) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:1233
#6  0x00000000023dae9e in do_select (join=0x7fd254020ec0) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:959
#7  0x00000000023d8ded in JOIN::exec (this=0x7fd254020ec0) at /root/work/stonedb-dev-20230605/sql/sql_executor.cc:206
#8  0x00000000024717e3 in handle_query (thd=0x7fd254000e10, lex=0x7fd254003138, result=0x7fd254006a88, added_options=0, removed_options=0, is_optimize_after_tianmu=0, free_join_from_tianmu=0)
    at /root/work/stonedb-dev-20230605/sql/sql_select.cc:195
#9  0x0000000002427ae2 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5205
#10 0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#11 0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#12 0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#13 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#14 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#15 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#16 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#17 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

longlong Item_func_case::val_int()
{
  assert(fixed == 1);
  char buff[MAX_FIELD_WIDTH];
  String dummy_str(buff,sizeof(buff),default_charset());
  Item *item=find_item(&dummy_str);
  longlong res;

  if (!item)
  {
    null_value=1;
    return 0;
  }
  res=item->val_int();
  null_value=item->null_value;
  return res;
}

函数实现的运算逻辑的数据:

函数目的分析:

  1. 该函数实现了case属性的取值和计算, 代码逻辑非常简单直接,这里不做过多分析
  2. 值得注意的是该函数被调用的位置, 是在end_send_group中, 由copy_fields完成
  3. 需要注意myql/sql中的item的概念, 是作为一个基本元素来理解, 即使是一个包含case运算的列, 也是一个item. 而这个包含case运算的item又包含了其他属性的item。在当前场景下具体为 Item_func_case
  4. item对上层提供的统一抽象接口是val_int和val_str, 即使是逻辑分支运算例如case, 也是被包括在了val_int中进行

tianmu执行分析:

构建查询序列:

T:-1 = TABLE_ALIAS(T:0,"t1")
T:-2 = TMP_TABLE(T:4294967295)
VC:-2.0 = CREATE_VC(T:-2,EXPR("case"))
A:-1 = T:-2.ADD_COLUMN(VC:-2.0,LIST,"case when  age IS NOT NULL THEN age else 33 end","ALL"
VC:-2.1 = CREATE_VC(T:-2,PHYS_COL(T:-1,A:0))
A:-2 = T:-2.ADD_COLUMN(VC:-2.1,SUM,"sum(age)","ALL")
T:-2.APPLY_CONDS()
RESULT(T:-2)

tianmu的构建查询序列的分析思路:

  1. 需要注意这个日志仅仅是将mysql/sql的查询树, 转换成tianmu的查询序列的过程, 是一个构建的过程,而非执行查询序列的过程
  2. 查询序列的执行的入口是APPLY_CONDS()
  3. 查询序列的每一个操作符的具体的执行过程, 需要从APPLY_CONDS()入口开始逐步定位
  4. 具体到本问题, 就是要分析EXPR的列属性,是被如何具体处理

tianmu执行的核心处理:

MysqlExpression::EvalType

调用堆栈:

#0  Tianmu::core::MysqlExpression::EvalType (this=0x7fd254024e70, tv=0x7fd254eb5bd8) at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:373
#1  0x0000000002e406d2 in Tianmu::vcolumn::ExpressionColumn::ExpressionColumn (this=0x7fd254eb5a30, expr=0x7fd254024e70, temp_table=0x7fd254eb56f0, temp_table_alias=-2, 
    multi_index=0x7fd254026e40) at /root/work/stonedb-dev-20230605/storage/tianmu/vc/expr_column.cpp:72
#2  0x0000000002cfd078 in Tianmu::core::Query::CreateColumnFromExpression (this=0x7fd519f6e750, exprs=std::vector of length 1, capacity 1 = {...}, temp_table=0x7fd254eb56f0, 
    temp_table_alias=-2, mind=0x7fd254026e40) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query.cpp:498
#3  0x0000000002cff9d5 in Tianmu::core::Query::Preexecute (this=0x7fd519f6e750, qu=..., sender=0x71c5c40, display_now=true) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query.cpp:820
#4  0x0000000002cebb38 in Tianmu::core::Engine::Execute (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result_output=0x7fd254006a68, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:513
#5  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#6  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fd254000e10, lex=0x7fd254003138, result_output=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#7  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#8  0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#9  0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#10 0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#11 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#12 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#13 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#14 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#15 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

DataType MysqlExpression::EvalType(TypOfVars *tv) {
  // set types of variables (_tianmufieldsCache)
  if (tv) {
    DataType fieldtype;
    auto tianmufield_set = tianmu_fields_cache.begin();
    while (tianmufield_set != tianmu_fields_cache.end()) {
      auto it = tv->find(tianmufield_set->first);
      if (it != tv->end()) {
        for (auto &tianmufield : tianmufield_set->second) {
          fieldtype = it->second;
          tianmufield->SetType(fieldtype);
        }
      }
      tianmufield_set++;
    }
  }

函数目的分析:

  1. 最大的一个问题便是为什么要在这个函数打断点做追踪?
    1. 对expr的处理的代码并不熟悉, 不知道tianmu具体是怎么处理case的, 无法直接在逻辑处理的地方打断点, 或者说对代码的熟悉程度不足以直接定位问题。这也是现在大部分的解决问题的时候面临的难题, 对这块代码的熟悉程度不足, 是在解决这块代码的问题时才第一次理解该块逻辑的代码
    2. 但是EvalType是必定要走到的, 必须要拿到expr的类型
  2. 从这个函数的调用堆栈出发, 理解expr的属性的处理流程

MysqlExpression::GetTianmufieldItem

调用堆栈:

#0  Tianmu::core::MysqlExpression::GetTianmufieldItem (this=0x7fd254024e70, ifield=0x7fd254eb2a08) at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:165
#1  0x00000000030c5a05 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd254eb2a08, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:240
#2  0x00000000030c5f09 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd254eb2f50, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:333
#3  0x00000000030c5e05 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd2540055c0, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:317
#4  0x00000000030c5e05 in Tianmu::core::MysqlExpression::TransformTree (this=0x7fd254024e70, root=0x7fd254005840, dir=Tianmu::core::MysqlExpression::TransformDirection::FORWARD)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:317
#5  0x00000000030c4ce1 in Tianmu::core::MysqlExpression::MysqlExpression (this=0x7fd254024e70, item=0x7fd254005840, item2varid=std::map with 1 element = {...})
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/mysql_expression.cpp:50
#6  0x0000000002d24303 in Tianmu::core::Query::WrapMysqlExpression (this=0x7fd519f6e750, item=0x7fd254005840, tmp_table=..., expr=@0x7fd519f6e1c8: 0x0, in_where=false, aggr_used=false)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:834
#7  0x0000000002d22279 in Tianmu::core::Query::AddFields (this=0x7fd519f6e750, fields=..., tmp_table=..., base_table=..., group_by_clause=false, num_of_added_fields=@0x7fd519f6e4c4: 0, 
    ignore_minmax=false, aggregation_used=@0x7fd519f6e40f: false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:495
#8  0x0000000002d25efd in Tianmu::core::Query::Compile (this=0x7fd519f6e750, compiled_query=0x7fd519f6e680, selects_list=0x7fd254004bc0, last_distinct=0x0, res_tab=0x0, ignore_limit=false, 
    left_expr_for_subselect=0x0, oper_for_subselect=0x0, ignore_minmax=false, for_subq_in_where=false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:1210
#9  0x0000000002ceb69c in Tianmu::core::Engine::Execute (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result_output=0x7fd254006a68, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:472
#10 0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#11 0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fd254000e10, lex=0x7fd254003138, result_output=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#12 0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#13 0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#14 0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#15 0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#16 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#17 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#18 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#19 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#20 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

Item_tianmufield *MysqlExpression::GetTianmufieldItem(Item_field *ifield) {
  auto key = item2varid->find(ifield);
  DEBUG_ASSERT(key != item2varid->end());
  auto it = tianmu_fields_cache.find(key->second);
  Item_tianmufield *tianmufield = nullptr;
  if (it != tianmu_fields_cache.end()) {
    tianmufield = *it->second.begin();
    tianmufield->varID.push_back(key->second);
  } else {
    tianmufield = new Item_tianmufield(ifield, key->second);
    std::set<Item_tianmufield *> s_tmp;
    s_tmp.insert(tianmufield);
    tianmu_fields_cache.insert(make_pair(key->second, s_tmp));
  }
  return (tianmufield);
}

函数目的分析:

  1. 由于上层逻辑是在执行Query::AddFields和TransformTree, 所以不能从GetTianmufieldItem中获取具体的业务处理相关的信息
  2. 那么为什么还要在这个函数挂断点分析执行过程呢? 因为这个函数是在处理Item_tianmufield的其中一个过程

Tianmu::core::Query::Preexecute

执行结果的临时表的case里属性的数据:

(gdb) p output_table[0].attrs[0][0]
$27 = (Tianmu::core::TempTable::Attr) {
  <Tianmu::core::PhysicalColumn> = {
    <Tianmu::core::Column> = {
      ct = {
        type = Tianmu::common::ColumnType::BIGINT, 
        unsigned_flag_ = false, 
        precision = 19, 
        scale = 0, 
        internal_size = 8, 
        display_size = 20, 
        collation = {
          collation = 0x44664e0 <my_charset_bin>, 
          derivation = DERIVATION_NONE, 
          repertoire = 3
        }, 
        fmt = Tianmu::common::PackFmt::DEFAULT, 
        flag = std::bitset
      }
    }, 
    members of Tianmu::core::PhysicalColumn: 
    _vptr.PhysicalColumn = 0x44057d0 <vtable for Tianmu::core::TempTable::Attr+16>, 
    is_unique = false, 
    is_unique_updated = false
  }, 
  members of Tianmu::core::TempTable::Attr: 
  si = {
    separator = "", 
    order = st_order::ORDER_NOT_RELEVANT
  }, 
  buffer = 0x0, 
  no_obj = 0, 
  no_power = 16, 
  no_materialized = 0, 
  page_size = 1, 
  alias = 0x7fd254024a70 "case when  age IS NOT NULL THEN age else 33 end", 
  mode = Tianmu::common::ColOperation::LISTING, 
  distinct = false, 
  term = {
    type = Tianmu::common::ColumnType::UNK, 
    vc = 0x7fd254eb5a30, 
    cond_value = std::vector of length 0, capacity 0, 
    cond_numvalue = std::shared_ptr<Tianmu::utils::Hash64> (empty) = {
      get() = 0x0
    }, 
    vc_id = 0, 
    is_vc_owner = false, 
    item = 0x0
  }, 
--Type <RET> for more, q to quit, c to continue without paging--
  dim = -2, 
  orig_precision = 19, 
  not_complete = true
}

此函数目的:

  1. 追踪这个函数是从结果出发, 来分析case列相关的属性和数据信息, 倒推case列属性是如何执行的
  2. 对output_table[0].attrs[0][0]的数据做分析, 有以下奇怪的地方
    1. mode的值是Tianmu::common::ColOperation::LISTING, 这个是自然属性, 而并不是case这种计算的属性。这种属性会导致直接取值, 而非取值后的case计算
    2. buffer = 0x0 缓存列的中间结果, 其值为空, 也就是没有拿到数据, 结合mode是LISTING, 其值符合LISTING属性的预期
  3. 所以可以分析出, case属性的列, 在执行的时候, 是按照LISTING进行处理的, 而并没有做case运算

Query::AddColumnForMysqlExpression

调用堆栈:

#0  Tianmu::core::Query::AddColumnForMysqlExpression (this=0x7fd519f6e750, mysql_expression=0x7fd254024e70, tmp_table=..., 
    alias=0x7fd2540065c8 "case when  age IS NOT NULL THEN age else 33 end", oper=Tianmu::common::ColOperation::LISTING, distinct=false, group_by=false)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:932
#1  0x0000000002d223ec in Tianmu::core::Query::AddFields (this=0x7fd519f6e750, fields=..., tmp_table=..., base_table=..., group_by_clause=false, num_of_added_fields=@0x7fd519f6e4c4: 0, 
    ignore_minmax=false, aggregation_used=@0x7fd519f6e40f: false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:507
#2  0x0000000002d25efd in Tianmu::core::Query::Compile (this=0x7fd519f6e750, compiled_query=0x7fd519f6e680, selects_list=0x7fd254004bc0, last_distinct=0x0, res_tab=0x0, ignore_limit=false, 
    left_expr_for_subselect=0x0, oper_for_subselect=0x0, ignore_minmax=false, for_subq_in_where=false) at /root/work/stonedb-dev-20230605/storage/tianmu/core/query_compile.cpp:1210
#3  0x0000000002ceb69c in Tianmu::core::Engine::Execute (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result_output=0x7fd254006a68, unit_for_union=0x0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:472
#4  0x0000000002cea825 in Tianmu::core::Engine::HandleSelect (this=0x7218d50, thd=0x7fd254000e10, lex=0x7fd254003138, result=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/core/engine_execute.cpp:243
#5  0x0000000003084b13 in Tianmu::DBHandler::ha_my_tianmu_query (thd=0x7fd254000e10, lex=0x7fd254003138, result_output=@0x7fd519f6edc8: 0x7fd254006a68, setup_tables_done_option=0, 
    res=@0x7fd519f6edc4: 0, is_optimize_after_tianmu=@0x7fd519f6edbc: 1, tianmu_free_join=@0x7fd519f6edc0: 1, with_insert=0)
    at /root/work/stonedb-dev-20230605/storage/tianmu/sql/ha_my_tianmu.cpp:95
#6  0x0000000002427aa8 in execute_sqlcom_select (thd=0x7fd254000e10, all_tables=0x7fd254014a88) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5204
#7  0x0000000002420e1e in mysql_execute_command (thd=0x7fd254000e10, first_level=true) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:2847
#8  0x0000000002428b0d in mysql_parse (thd=0x7fd254000e10, parser_state=0x7fd519f6ff90) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:5642
#9  0x000000000241db04 in dispatch_command (thd=0x7fd254000e10, com_data=0x7fd519f70730, command=COM_QUERY) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1495
#10 0x000000000241c945 in do_command (thd=0x7fd254000e10) at /root/work/stonedb-dev-20230605/sql/sql_parse.cc:1034
#11 0x000000000254eeb5 in handle_connection (arg=0x965db20) at /root/work/stonedb-dev-20230605/sql/conn_handler/connection_handler_per_thread.cc:313
#12 0x0000000002c1e6f4 in pfs_spawn_thread (arg=0x9490860) at /root/work/stonedb-dev-20230605/storage/perfschema/pfs.cc:2197
#13 0x00007fd569c2b1ca in start_thread () from /lib64/libpthread.so.0
#14 0x00007fd566aa1e73 in clone () from /lib64/libc.so.6

函数实现:

Query::AddFields

QueryRouteTo Query::AddFields(List<Item> &fields, TabID const &tmp_table, TabID const &base_table,
                              bool const group_by_clause, int &num_of_added_fields, bool ignore_minmax,
                              bool &aggregation_used) {
  List_iterator_fast<Item> li(fields);
  Item *item;
  int added = 0;
  item = li++;
  while (item) {
    WrapStatus ws;
    common::ColOperation oper;
    bool distinct;
    if (QueryRouteTo::kToMySQL == OperationUnmysterify(item, oper, distinct, group_by_clause))
      return QueryRouteTo::kToMySQL;

    if (IsAggregationItem(item))
      aggregation_used = true;

    // in case of transformed subquery sometimes we need to revert back
    // transformation to MIN/MAX
    if (ignore_minmax && (oper == common::ColOperation::MIN || oper == common::ColOperation::MAX))
      oper = common::ColOperation::LISTING;

    // select PHYSICAL COLUMN or AGGREGATION over PHYSICAL COLUMN
    if ((IsFieldItem(item) || IsAggregationOverFieldItem(item)) &&
        (IsLocalColumn(item, tmp_table) || (!base_table.IsNullID() && IsLocalColumn(item, base_table))))
      AddColumnForPhysColumn(item, tmp_table, base_table, oper, distinct, false, item->item_name.ptr());
    // REF to FIELD_ITEM
    else if (item->type() == Item::REF_ITEM) {
      item = UnRef(item);
      continue;
    }
    // if ((UnRef(item)->type() == Item_tianmufield::enumTIANMUFiledItem::TIANMUFIELD_ITEM ||
    //      UnRef(item)->type() == Item_tianmufield::FIELD_ITEM) &&
    //     IsLocalColumn(UnRef(item), tmp_table))
    //   AddColumnForPhysColumn(UnRef(item), tmp_table, oper, distinct, false, false);
    // else {
    //   //
    // }
    else if (IsAggregationItem(item) && (((Item_sum *)item)->get_arg(0))->type() == Item::REF_ITEM &&
             (UnRef(((Item_sum *)item)->get_arg(0))->type() == Item_tianmufield::get_tianmuitem_type() ||
              (UnRef(((Item_sum *)item)->get_arg(0))->type() == Item_tianmufield::FIELD_ITEM)) &&
             IsLocalColumn(UnRef(((Item_sum *)item)->get_arg(0)), tmp_table))
      // AGGR on REF to FIELD_ITEM
      AddColumnForPhysColumn(UnRef(((Item_sum *)item)->get_arg(0)), tmp_table, TabID(), oper, distinct, false,
                             item->item_name.ptr());
    else if (IsAggregationItem(item)) {
      // select AGGREGATION over EXPRESSION
      Item_sum *item_sum = (Item_sum *)item;
      if (item_sum->get_arg_count() > 1 || HasAggregation(item_sum->get_arg(0)))
        return QueryRouteTo::kToMySQL;
      if (IsCountStar(item_sum)) {  // count(*) doesn't need any virtual column
        AttrID at;
        cq->AddColumn(at, tmp_table, CQTerm(), oper, item_sum->item_name.ptr(), false);
        field_alias2num[TabIDColAlias(tmp_table.n, item_sum->item_name.ptr())] = at.n;
      } else {
        MysqlExpression *expr;
        ws = WrapMysqlExpression(item_sum->get_arg(0), tmp_table, expr, false, false);
        if (ws == WrapStatus::FAILURE)
          return QueryRouteTo::kToMySQL;
        AddColumnForMysqlExpression(expr, tmp_table,
                                    ignore_minmax ? item_sum->get_arg(0)->item_name.ptr() : item_sum->item_name.ptr(),
                                    oper, distinct);
      }
    } else if (item->type() == Item::SUBSELECT_ITEM) {
      CQTerm term;
      AttrID at;
      if (Item2CQTerm(item, term, tmp_table,
                      /*group_by_clause ? HAVING_FILTER :*/ CondType::WHERE_COND) == QueryRouteTo::kToMySQL)
        return QueryRouteTo::kToMySQL;
      cq->AddColumn(at, tmp_table, term, common::ColOperation::LISTING, item->item_name.ptr(), distinct);
      field_alias2num[TabIDColAlias(tmp_table.n, item->item_name.ptr())] = at.n;
    } else {
      // select EXPRESSION
      if (HasAggregation(item)) {
        oper = common::ColOperation::DELAYED;
        aggregation_used = true;
      }
      MysqlExpression *expr(nullptr);
      ws = WrapMysqlExpression(item, tmp_table, expr, false, oper == common::ColOperation::DELAYED);
      if (ws == WrapStatus::FAILURE)
        return QueryRouteTo::kToMySQL;
      if (!item->item_name.ptr()) {
        Item_func_conv_charset *item_conv = dynamic_cast<Item_func_conv_charset *>(item);
        if (item_conv) {
          Item **ifunc_args = item_conv->arguments();
          AddColumnForMysqlExpression(expr, tmp_table, ifunc_args[0]->item_name.ptr(), oper, distinct);
        } else {
          AddColumnForMysqlExpression(expr, tmp_table, item->item_name.ptr(), oper, distinct);
        }
      } else
        AddColumnForMysqlExpression(expr, tmp_table, item->item_name.ptr(), oper, distinct);
    }
    added++;
    item = li++;
  }
  num_of_added_fields = added;
  return QueryRouteTo::kToTianmu;
}

(gdb) p item
$30 = (Item_func_case *) 0x7fd254005840

此函数目的:

  1. case的列属性构建tianmu的查询序列, mode设置为LISTING导致了case运算的缺失, 那么就需要分析构建查询序列的列属性的过程
  2. 分析该函数有这么几个目的:
    1. 尝试将tianmu的列属性与mysql/sql的列属性保持一致, 补充确实的case运算
    2. 如果tianmu引擎无法计算该属性, 直接拿到item的信息进行处理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/611207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

sql server 内存知识

SQL Server对服务器内存的使用策略是用多少内存就占用多少内存&#xff0c;只用在服务器内存不足时&#xff0c;才会释放一点占用的内存&#xff0c;至少释放多少&#xff0c;完全由sql server控制&#xff0c;所以SQL Server 服务器内存往往会占用很高。 SQL Server提供数据库…

华为OD机试真题 Java 实现【一种字符串压缩表示的解压】【2022Q4 100分】,附详细解题思路

一、题目描述 有一种简易压缩算法&#xff1a;针对全部由小写英文字母组成的字符串&#xff0c;将其中连续超过两个相同字母的部分压缩为连续个数加该字母&#xff0c;其他部分保持原样不变。例如&#xff1a;字符串“aaabbccccd”经过压缩成为字符串“3abb4cd”。 请您编写解…

基于深度学习的视频美颜SDK技术创新与应用案例分析

很多人在拍摄视频时会感到自己的皮肤不够好看&#xff0c;因此需要使用美颜功能。同时&#xff0c;视频美颜也是很多短视频App的核心功能之一。为了提供更加高效、准确的视频美颜功能&#xff0c;很多公司开始研发基于深度学习的视频美颜SDK技术。 与传统的图像处理技术相比&a…

kafka 安装快速入门

直接上干货&#xff0c;我们公司最近要进行消息推送指定软件kafka,直接走起。 1.下载 kafka 是apache的项目。下载地址&#xff1a;kafka.apache.org/ 点击download kafka 进入查看相关版本进行下载。 我这里用的版本比窘旧一点&#xff0c;公司技术一切求稳。 下载好安装包就已…

论文笔记:Normalizing Flows for Probabilistic Modeling and Inference

Abstract 正则流&#xff08;Normalizing flows&#xff09;提供了一种通用的机制来定义富有表达力的概率分布&#xff0c;只需要指定一个&#xff08;通常简单的&#xff09;基础分布和一系列可逆变换。 Intraduction 正则流通过将简单的密度通过一系列变换来产生更丰富、可…

怎么选择适合爬虫的代理IP,使用时需要注意什么

网络爬虫工作离不开代理服务器的支持&#xff0c;但并不是所有的代理服务器都适合爬虫工作。那么如何选择适合爬虫的代理服务器呢&#xff1f; 选择适合爬虫的代理服务器需要考虑以下几个方面&#xff1a; 1、代理服务器的稳定性&#xff1a;稳定可靠的代理服务器更能够保证爬虫…

JPEG压缩基本原理

JPEG算法的第一步是将图像分割成8X8的小块。 在计算机中&#xff0c;彩色图像最常见的表示方法是RGB格式&#xff0c;通过R(Red)、G(Green)A和(Blue)组合出各种颜色。 除此以外&#xff0c;还有一种表示彩色图像的方法&#xff0c;称为YUV格式。Y表示亮度&#xff0c;U和V表示…

【C++】一文带你吃透C++继承

&#x1f34e; 博客主页&#xff1a;&#x1f319;披星戴月的贾维斯 &#x1f34e; 欢迎关注&#xff1a;&#x1f44d;点赞&#x1f343;收藏&#x1f525;留言 &#x1f347;系列专栏&#xff1a;&#x1f319; C/C专栏 &#x1f319;那些看似波澜不惊的日复一日&#xff0c;…

Docker attach VS exec

我们知道&#xff0c;进入容器常用的两种方式为&#xff1a;docker exec ...、docker attach ...&#xff0c;那这两者有什么区别呢&#xff1f; 首先&#xff0c;运行一个测试容器&#xff0c;并在启动容器时运行相关指令&#xff0c;如下&#xff1a; docker run --name te…

JVM学习笔记一

程序计数器是一块儿较小的内存, 请你谈谈你对JVM的理解?java8虚拟机和之前的有什么变化更新?什么是OOM?什么是栈溢出(StackOverFlowError)?怎么分析JVM的常用调优参数?内存快照如何抓取?怎么分析Dump文件?谈谈JVM中类加载器你的认识?JVM的位置JVM的体系结构类加载器双…

科研热点|科研人专属身份证来了,国产ORCID ID启动!

2023年6月1日&#xff0c;国家自然科学基金委员会发布了《国家自然科学基金委员会关于推广和发布基础研究科研人员标识&#xff08;BRID&#xff09;有关工作安排的通告》&#xff0c;宣布从即日起&#xff0c;国家自然科学基金委员会&#xff08;以下简称自然科学基金委&#…

高完整性系统(4)Formal Logic (形式逻辑和 Alloy 简介)

文章目录 Story so far形式逻辑命题 proposition谓词 predicate连接词VariablesSet 集合Set operation 集合操作Set Relationship 集合关系Alloy Set alloy 的集合表示Quantification 量词Relations 关系案例Binary Relations 二元关系图Functions 函数Total v.s. Partial Func…

IO模型、select、poll、epoll

阻塞IO模型 阻塞IO是最通用的IO类型&#xff0c;使用这种模型进行数据接收的时候&#xff0c;在数据没有到之前程序会一直等待。例如&#xff0c;对于函数recvfrom(),内核会一直阻塞该请求直到有数据到来才返回。 非阻塞IO模型 当把套接字设置成非阻塞的IO,则对每次请求&…

Java网络开发(Tomcat)——遇到的 bug 汇总(持续更新)

目录 引出:bug::bug::bug:Tomcat开发的bug汇总项目启动就报错1.WebServlet()路径配置的问题2.由于之前的错误&#xff0c;Context[/day01]启动失败【困扰】3.启动过滤器异常---init方法 JSP使用相关报错1.后端传给jsp的数据&#xff0c;前端jsp不显示2.jsp的包没有导&#xff0…

6 vue

前端开发 1.前端开发 前端工程师“Front-End-Developer”源自于美国。大约从2005年开始正式的前端工程师角色被行业所认可&#xff0c;到了2010年&#xff0c;互联网开始全面进入移动时代&#xff0c;前端开发的工作越来越重要。 最初所有的开发工作都是由后端工程师完成的&…

‘jupyter‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

目录 0.问题背景环境介绍 1.解决步骤 2.测试步骤 0.问题背景环境介绍 1&#xff09;环境&#xff1a;windows64 2&#xff09;问题背景&#xff1a;在搭建jupyter notebook的过程中&#xff0c;想用windows的任务管理器启动jupyter notebook或者使用【jupyter notebook --…

降低成本,快速搭建企业帮助文档的方法盘点

企业帮助文档是企业为了解决客户疑问和提高客户满意度而制作的一种文档&#xff0c;通常包括产品的使用指南、故障排除、常见问题解答等内容。一个好的帮助文档可以帮助企业降低客服成本、提高客户满意度&#xff0c;进而提高产品销量和企业品牌形象。但是&#xff0c;有些企业…

基于html+css的图展示108

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

MFC(十二)多个对话框

我们来制定多个对话框&#xff0c;每个对话框都有不同的功能&#xff0c;单击下一步&#xff0c;即可跳转到下一个对话框 1.新建一个启动按钮 2.在资源视图&#xff0c;Dialog里面&#xff0c;右键-->添加资源---->dialog>选择IDD PROPPAGE_SMALL新建 属性页&#…

「移动机器人行业应用分析」锂电行业

锂电池作为目前一种比较成熟和先进的电池&#xff0c;因其质量轻&#xff0c;储电量大等特点&#xff0c;受到了人们的广泛应用。中国作为全球最大的锂电生产和消费国&#xff0c;也是全球最大的电动汽车市场&#xff0c;随着“碳中和”这一目标的提出&#xff0c;锂离子电池技…