溫馨提示×

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

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

MySQL寫集合是什么

發(fā)布時(shí)間:2021-11-10 11:41:21 來(lái)源:億速云 閱讀:131 作者:iii 欄目:MySQL數(shù)據(jù)庫(kù)

本篇內(nèi)容介紹了“MySQL寫集合是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

實(shí)際上寫集合定義在類Rpl_transaction_write_set_ctx中,其中主要包含兩個(gè)數(shù)據(jù)結(jié)構(gòu)

  • std::vector<uint64> write_set;

  • std::set<uint64> write_set_unique;

第一個(gè)是一個(gè)vecotr數(shù)組,第二個(gè)是一個(gè)set集合,它們中的每一元素都是一個(gè)hash值,其hash來(lái)源自函數(shù)add_pke,包含了:

  • 非唯一索引名稱+分隔符+庫(kù)名+分隔符+庫(kù)名長(zhǎng)度+表名+分隔符+表名長(zhǎng)度+索引字段1數(shù)值+分隔符 +索引字段1長(zhǎng)度 [+ 索引字2段數(shù)值+分隔符 +索引字段2長(zhǎng)度 .....]

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

  • 按照MySQL字段格式的字段值和長(zhǎng)度

  • 按照字符串格式記錄的字段值和長(zhǎng)度

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

二、寫集合原始數(shù)據(jù)(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)

我們寫入一行數(shù)據(jù):

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

這一行數(shù)據(jù)一共會(huì)生成4個(gè)寫集合元素分別為:
注意:這里顯示的?是分隔符

  • 寫集合元素1:

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

主鍵 PRIMARY+分隔符+庫(kù)名 test+分隔符+庫(kù)名長(zhǎng)度 4+表名 jj10+分隔符+表名長(zhǎng)度 4+主鍵值 0X80 00 00 24 +分隔符+int字段類型長(zhǎng)度 4

  • 寫集合元素2:

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

主鍵 PRIMARY+分隔符+庫(kù)名 test+分隔符+庫(kù)名長(zhǎng)度 4+表名 jj10+分隔符+表名長(zhǎng)度 4+主鍵值字符串顯示 "36" +分隔符+字符串"36"長(zhǎng)度為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

三、函數(shù)add_pke解析

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

如果表中存在索引:
   將數(shù)據(jù)庫(kù)名,表名信息寫入臨時(shí)變量   
   循環(huán)掃描表中每個(gè)索引:
        如果不是唯一索引:
             退出本次循環(huán)繼續(xù)循環(huán)。
        循環(huán)兩種生成數(shù)據(jù)的方式(MySQL格式和字符串格式):
             將索引名字寫入到pke中。
             將臨時(shí)變量信息寫入到pke中。
             循環(huán)掃描索引中的每一個(gè)字段:
                將每一個(gè)字段的信息寫入到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(); //本內(nèi)存空間在線程初始化的時(shí)候分配    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); //數(shù)據(jù)庫(kù)名字 存入。
    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]); //存儲(chǔ)的是字符形式的長(zhǎng)度 返回為char指針 '1' '3' 代表 長(zhǎng)度13 
    pke_schema_table.append(value_length);//將轉(zhuǎn)換后的長(zhǎng)度以字符串的方式存入
    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]);//存儲(chǔ)的是字符形式的長(zhǎng)度 返回為char指針 '1' '3' 代表 長(zhǎng)度13 
    pke_schema_table.append(value_length);//將轉(zhuǎn)換后的長(zhǎng)度以字符串的方式存入
    //因此上面的存儲(chǔ)的為 分隔符+dbname+分隔符+dbname長(zhǎng)度+分隔符+tablename+分隔符+tablename長(zhǎng)度 這里就是代表了數(shù)據(jù)庫(kù)和表信息
    std::string pke; //初始化pke 這是存儲(chǔ)寫集合元素hash前數(shù)據(jù)的中間變量
    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); //讀取當(dāng)前行數(shù)據(jù)到buffer#ifndef DBUG_OFF //如果沒有定義 非DEBUG 模式
    std::vector<std::string> write_sets;#endif
    for (uint key_number=0; key_number < table->s->keys; key_number++) //依次掃描每個(gè)索引   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)) //跳過(guò)非唯一的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--) //校隊(duì)和非校隊(duì)算法  也就是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長(zhǎng)度+分隔符+tablename+分隔符+tablename長(zhǎng)度 "
        uint i= 0;        for (/*empty*/; i < table->key_info[key_number].user_defined_key_parts; i++) //開始掃描每一個(gè)相應(yīng)的字段
        {          // 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 字段在表中的相應(yīng)位置
          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...] 這里有多態(tài)每種字段類型有自己的各種算法
            break; //如果字段為空 或者 值為 空 返回
          // convert using collation support conversion algorithm
          if (COLLATION_CONVERSION_ALGORITHM == collation_conversion_algorithm) //如果采用校隊(duì)算法
          {            const CHARSET_INFO* cs= table->field[index-1]->charset();
            length= cs->coll->strnxfrmlen(cs,
                                       table->field[index-1]->pack_length()); //獲取長(zhǎng)度主鍵值
          }          // 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函數(shù)
            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); //將主鍵值計(jì)入
          pke.append(HASH_STRING_SEPARATOR);//分隔符
          value_length= my_safe_itoa(10, length,
                                     &value_length_buffer[VALUE_LENGTH_BUFFER_SIZE-1]);//存儲(chǔ)的是字符形式的長(zhǎng)度 返回為char指針 '1' '3' 代表 長(zhǎng)度13 
          pke.append(value_length);//計(jì)入長(zhǎng)度
        }        /*
          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) //如果所有的索引字段都掃描完成
        {//最后得到的字符串為  非唯一索引名稱+分隔符+庫(kù)名+分隔符+庫(kù)名長(zhǎng)度+表名+分隔符+表名長(zhǎng)度+索引字段1數(shù)值+分隔符 +索引字段1長(zhǎng)度 [+ 索引字段2數(shù)值+分隔符 +索引字段2長(zhǎng)度 .....]
          generate_hash_pke(pke, collation_conversion_algorithm, thd); //對(duì)pke內(nèi)存空間做HASH 
          writeset_hashes_added++; 
#ifndef DBUG_OFF
          write_sets.push_back(pke); //寫入到write set 并且加入到寫集合中#endif
        }

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

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI