溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MySQL寫集合是什么

發布時間:2021-11-10 11:41:21 來源:億速云 閱讀:172 作者:iii 欄目:MySQL數據庫

本篇內容介紹了“MySQL寫集合是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

一、什么是寫集合(Write set)

實際上寫集合定義在類Rpl_transaction_write_set_ctx中,其中主要包含兩個數據結構

  • std::vector<uint64> write_set;

  • std::set<uint64> write_set_unique;

第一個是一個vecotr數組,第二個是一個set集合,它們中的每一元素都是一個hash值,其hash來源自函數add_pke,包含了:

  • 非唯一索引名稱+分隔符+庫名+分隔符+庫名長度+表名+分隔符+表名長度+索引字段1數值+分隔符 +索引字段1長度 [+ 索引字2段數值+分隔符 +索引字段2長度 .....]

注意唯一索引也會計入到寫集合中。
在MGR中主鍵是有著極其重要的地位,是判斷是否沖突的重要依據,最后寫集合信息會封裝進Transaction_context_log_event,同其他binlog event信息一起發送給其他節點。同時函數add_pke在生成寫集合成員原始數據的時候(hash之前的數據)對每行索引值還記錄兩種格式:

  • 按照MySQL字段格式的字段值和長度

  • 按照字符串格式記錄的字段值和長度

而生成寫集合的是在Innodb層完成更改操作,MySQL層寫入binlog event之前。

二、寫集合原始數據(hash前)的列子

如下表:

mysql> use test
Database changed
mysql> show create table jj10 \G
*************************** 1. row ***************************
       Table: jj10
Create Table: CREATE TABLE `jj10` (  `id1` int(11) DEFAULT NULL,  `id2` int(11) DEFAULT NULL,  `id3` int(11) NOT NULL,
  PRIMARY KEY (`id3`),
  UNIQUE KEY `id1` (`id1`),
  KEY `id2` (`id2`)
) ENGINE=InnoDB DEFAULT CHARSET=latin11 row in set (0.00 sec)

我們寫入一行數據:

insert into jj10 values(36,36,36);

這一行數據一共會生成4個寫集合元素分別為:
注意:這里顯示的?是分隔符

  • 寫集合元素1:

(gdb) p pke
$1 = "PRIMARY?test?4jj10?4\200\000\000$?4"注意:\200\000\000$ 為:3個八進制字節+ASCII$  16進制就是0X80 00 00 24

主鍵 PRIMARY+分隔符+庫名 test+分隔符+庫名長度 4+表名 jj10+分隔符+表名長度 4+主鍵值 0X80 00 00 24 +分隔符+int字段類型長度 4

  • 寫集合元素2:

(gdb) p pke$2 = "PRIMARY?test?4jj10?436?2"

主鍵 PRIMARY+分隔符+庫名 test+分隔符+庫名長度 4+表名 jj10+分隔符+表名長度 4+主鍵值字符串顯示 "36" +分隔符+字符串"36"長度為2

  • 寫集合元素3:

(gdb) p pke$3 = "id1?test?4jj10?4\200\000\000$?4"

同上只是這里不是主鍵是唯一鍵id1

  • 寫集合元素4:

(gdb) p pke$4 = "id1?test?4jj10?436?2"

同上只是這里不是主鍵是唯一鍵id1

三、函數add_pke解析

這里拋開了外鍵的邏輯主要邏輯如下:

如果表中存在索引:
   將數據庫名,表名信息寫入臨時變量   
   循環掃描表中每個索引:
        如果不是唯一索引:
             退出本次循環繼續循環。
        循環兩種生成數據的方式(MySQL格式和字符串格式):
             將索引名字寫入到pke中。
             將臨時變量信息寫入到pke中。
             循環掃描索引中的每一個字段:
                將每一個字段的信息寫入到pke中。
                如果字段掃描完成:
                   將pke生成hash值并且寫入到寫集合中。

源碼注釋如下:

Rpl_transaction_write_set_ctx* ws_ctx=                     //THD  Transaction_ctx  m_transaction_write_set_ctx
    thd->get_transaction()->get_transaction_write_set_ctx(); //本內存空間在線程初始化的時候分配    m_transaction(new Transaction_ctx()), 
  int writeset_hashes_added= 0;  if(table->key_info && (table->s->primary_key < MAX_KEY)) //typedef struct st_key  
  {
    char value_length_buffer[VALUE_LENGTH_BUFFER_SIZE];
    char* value_length= NULL;
    std::string pke_schema_table;
    pke_schema_table.reserve(NAME_LEN * 3);
    pke_schema_table.append(HASH_STRING_SEPARATOR); //分隔符
    pke_schema_table.append(table->s->db.str, table->s->db.length); //數據庫名字 存入。
    pke_schema_table.append(HASH_STRING_SEPARATOR);//分隔符
    value_length= my_safe_itoa(10, table->s->db.length,
                               &value_length_buffer[VALUE_LENGTH_BUFFER_SIZE-1]); //存儲的是字符形式的長度 返回為char指針 '1' '3' 代表 長度13 
    pke_schema_table.append(value_length);//將轉換后的長度以字符串的方式存入
    pke_schema_table.append(table->s->table_name.str, table->s->table_name.length);//表名 字符存入。
    pke_schema_table.append(HASH_STRING_SEPARATOR);//分隔符
    value_length= my_safe_itoa(10, table->s->table_name.length,
                               &value_length_buffer[VALUE_LENGTH_BUFFER_SIZE-1]);//存儲的是字符形式的長度 返回為char指針 '1' '3' 代表 長度13 
    pke_schema_table.append(value_length);//將轉換后的長度以字符串的方式存入
    //因此上面的存儲的為 分隔符+dbname+分隔符+dbname長度+分隔符+tablename+分隔符+tablename長度 這里就是代表了數據庫和表信息
    std::string pke; //初始化pke 這是存儲寫集合元素hash前數據的中間變量
    pke.reserve(NAME_LEN * 5);
    char *pk_value= NULL;
    size_t pk_value_size= 0;    // Buffer to read the names of the database and table names which is less
    // than 1024. So its a safe limit.
    char name_read_buffer[NAME_READ_BUFFER_SIZE];    // Buffer to read the row data from the table record[0].
    String row_data(name_read_buffer, sizeof(name_read_buffer), &my_charset_bin); //讀取當前行數據到buffer#ifndef DBUG_OFF //如果沒有定義 非DEBUG 模式
    std::vector<std::string> write_sets;#endif
    for (uint key_number=0; key_number < table->s->keys; key_number++) //依次掃描每個索引   EXP:create table jj10(id1 int,id2 int,id3 int primary key,unique key(id1),key(id2));             
    {                                                                  //table->key_info[0].name  $12 = 0x7fffd8003631 "PRIMARY"  able->key_info[1].name $13 = 0x7fffd8003639 "id1"
      // Skip non unique.                                             //table->key_info[2].name $14 = 0x7fffd800363d "id2"
      if (!((table->key_info[key_number].flags & (HA_NOSAME )) == HA_NOSAME)) //跳過非唯一的KEY
        continue;      /*
        To handle both members having hash values with and without collation
        in the same group, we generate and send both versions (with and without
        collation) of the hash in the newer versions. This would mean that a row
        change will generate 2 instead of 1 writeset, and 4 instead of 2, when PK
        are involved. This will mean that a transaction will be certified against
        two writesets instead of just one.
        To generate both versions (with and without collation) of the hash, it
        first converts using without collation support algorithm (old algorithm),
        and then using with collation support conversion algorithm, and adds
        generated value to key_list_to_hash vector, for hash generation later.
        Since the collation writeset is bigger or equal than the raw one, we do
        generate first the collation and reuse the buffer without the need to
        resize for the raw.
      */KEY_PART_INFO Field      for (int collation_conversion_algorithm= COLLATION_CONVERSION_ALGORITHM;
           collation_conversion_algorithm >= 0;
           collation_conversion_algorithm--) //校隊和非校隊算法  也就是MySQL字段格式和字符串格式2種格式
      {
        pke.clear();
        pke.append(table->key_info[key_number].name); //table->key_info[0]  $15 = 0x7fffd8003631 "PRIMARY"
        pke.append(pke_schema_table);//將上面得到字符串寫入 那么這里就是 主鍵 "primary + dbname+分隔符+dbname長度+分隔符+tablename+分隔符+tablename長度 "
        uint i= 0;        for (/*empty*/; i < table->key_info[key_number].user_defined_key_parts; i++) //開始掃描每一個相應的字段
        {          // read the primary key field values in str.
          int index= table->key_info[key_number].key_part[i].fieldnr; // TABLE  st_key  KEY_PART_INFO 字段在表中的相應位置
          size_t length= 0;          /* Ignore if the value is NULL. */
          if (table->field[index-1]->is_null()) //Field **field;            /* Pointer to fields */   **point ->[*field,*field,*field...] 這里有多態每種字段類型有自己的各種算法
            break; //如果字段為空 或者 值為 空 返回
          // convert using collation support conversion algorithm
          if (COLLATION_CONVERSION_ALGORITHM == collation_conversion_algorithm) //如果采用校隊算法
          {            const CHARSET_INFO* cs= table->field[index-1]->charset();
            length= cs->coll->strnxfrmlen(cs,
                                       table->field[index-1]->pack_length()); //獲取長度主鍵值
          }          // convert using without collation support algorithm
          else
          {
            table->field[index-1]->val_str(&row_data);
            length= row_data.length();
          }          if (pk_value_size < length+1)
          {
            pk_value_size= length+1;
            pk_value= (char*) my_realloc(key_memory_write_set_extraction,
                                         pk_value, pk_value_size,
                                         MYF(MY_ZEROFILL));
          }          // convert using collation support conversion algorithm
          if (COLLATION_CONVERSION_ALGORITHM == collation_conversion_algorithm)
          {            /*
              convert to normalized string and store so that it can be
              sorted using binary comparison functions like memcmp.
            */
            table->field[index-1]->make_sort_key((uchar*)pk_value, length); // 將字段的值存入到pk_value中,各種類型都有make_sort_key函數
            pk_value[length]= 0;
          }          // convert using without collation support algorithm
          else
          {
            strmake(pk_value, row_data.c_ptr_safe(), length);
          }
          pke.append(pk_value, length); //將主鍵值計入
          pke.append(HASH_STRING_SEPARATOR);//分隔符
          value_length= my_safe_itoa(10, length,
                                     &value_length_buffer[VALUE_LENGTH_BUFFER_SIZE-1]);//存儲的是字符形式的長度 返回為char指針 '1' '3' 代表 長度13 
          pke.append(value_length);//計入長度
        }        /*
          If any part of the key is NULL, ignore adding it to hash keys.
          NULL cannot conflict with any value.
          Eg: create table t1(i int primary key not null, j int, k int,
                                                  unique key (j, k));
              insert into t1 values (1, 2, NULL);
              insert into t1 values (2, 2, NULL); => this is allowed.
        */
        if (i == table->key_info[key_number].user_defined_key_parts) //如果所有的索引字段都掃描完成
        {//最后得到的字符串為  非唯一索引名稱+分隔符+庫名+分隔符+庫名長度+表名+分隔符+表名長度+索引字段1數值+分隔符 +索引字段1長度 [+ 索引字段2數值+分隔符 +索引字段2長度 .....]
          generate_hash_pke(pke, collation_conversion_algorithm, thd); //對pke內存空間做HASH 
          writeset_hashes_added++; 
#ifndef DBUG_OFF
          write_sets.push_back(pke); //寫入到write set 并且加入到寫集合中#endif
        }

“MySQL寫集合是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女