本篇內容介紹了“MySQL寫集合是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
實際上寫集合定義在類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之前。
如下表:
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
這里拋開了外鍵的邏輯主要邏輯如下:
如果表中存在索引: 將數據庫名,表名信息寫入臨時變量 循環掃描表中每個索引: 如果不是唯一索引: 退出本次循環繼續循環。 循環兩種生成數據的方式(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寫集合是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。