本篇内容介绍了“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>usetestDatabasechangedmysql>showcreatetablejj10\G***************************1.row***************************Table:jj10CreateTable:CREATETABLE`jj10`(`id1`int(11)DEFAULTNULL,`id2`int(11)DEFAULTNULL,`id3`int(11)NOTNULL,PRIMARYKEY(`id3`),UNIQUEKEY`id1`(`id1`),KEY`id2`(`id2`))ENGINE=InnoDBDEFAULTCHARSET=latin11rowinset(0.00sec)

我们写入一行数据:

insertintojj10values(36,36,36);

这一行数据一共会生成4个写集合元素分别为:
注意:这里显示的½是分隔符

写集合元素1:

(gdb)ppke$1="PRIMARY½test½4jj10½4\200\000\000$½4"注意:\200\000\000$为:3个八进制字节+ASCII$16进制就是0X80000024

主键 PRIMARY+分隔符+库名 test+分隔符+库名长度 4+表名 jj10+分隔符+表名长度 4+主键值 0X80 00 00 24 +分隔符+int字段类型长度 4

写集合元素2:

(gdb)ppke$2="PRIMARY½test½4jj10½436½2"

主键 PRIMARY+分隔符+库名 test+分隔符+库名长度 4+表名 jj10+分隔符+表名长度 4+主键值字符串显示 "36" +分隔符+字符串"36"长度为2

写集合元素3:

(gdb)ppke$3="id1½test½4jj10½4\200\000\000$½4"

同上只是这里不是主键是唯一键id1

写集合元素4:

(gdb)ppke$4="id1½test½4jj10½436½2"

同上只是这里不是主键是唯一键id1

三、函数add_pke解析

这里抛开了外键的逻辑主要逻辑如下:

如果表中存在索引:将数据库名,表名信息写入临时变量循环扫描表中每个索引:如果不是唯一索引:退出本次循环继续循环。循环两种生成数据的方式(MySQL格式和字符串格式):将索引名字写入到pke中。将临时变量信息写入到pke中。循环扫描索引中的每一个字段:将每一个字段的信息写入到pke中。如果字段扫描完成:将pke生成hash值并且写入到写集合中。

源码注释如下:

Rpl_transaction_write_set_ctx*ws_ctx=//THDTransaction_ctxm_transaction_write_set_ctxthd->get_transaction()->get_transaction_write_set_ctx();//本内存空间在线程初始化的时候分配m_transaction(newTransaction_ctx()),intwriteset_hashes_added=0;if(table->key_info&&(table->s->primary_key<MAX_KEY))//typedefstructst_key{charvalue_length_buffer[VALUE_LENGTH_BUFFER_SIZE];char*value_length=NULL;std::stringpke_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'代表长度13pke_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'代表长度13pke_schema_table.append(value_length);//将转换后的长度以字符串的方式存入//因此上面的存储的为分隔符+dbname+分隔符+dbname长度+分隔符+tablename+分隔符+tablename长度这里就是代表了数据库和表信息std::stringpke;//初始化pke这是存储写集合元素hash前数据的中间变量pke.reserve(NAME_LEN*5);char*pk_value=NULL;size_tpk_value_size=0;//Buffertoreadthenamesofthedatabaseandtablenameswhichisless//than1024.Soitsasafelimit.charname_read_buffer[NAME_READ_BUFFER_SIZE];//Buffertoreadtherowdatafromthetablerecord[0].Stringrow_data(name_read_buffer,sizeof(name_read_buffer),&my_charset_bin);//读取当前行数据到buffer#ifndefDBUG_OFF//如果没有定义非DEBUG模式std::vector<std::string>write_sets;#endiffor(uintkey_number=0;key_number<table->s->keys;key_number++)//依次扫描每个索引EXP:createtablejj10(id1int,id2int,id3intprimarykey,uniquekey(id1),key(id2));{//table->key_info[0].name$12=0x7fffd8003631"PRIMARY"able->key_info[1].name$13=0x7fffd8003639"id1"//Skipnonunique.//table->key_info[2].name$14=0x7fffd800363d"id2"if(!((table->key_info[key_number].flags&(HA_NOSAME))==HA_NOSAME))//跳过非唯一的KEYcontinue;/*Tohandlebothmembershavinghashvalueswithandwithoutcollationinthesamegroup,wegenerateandsendbothversions(withandwithoutcollation)ofthehashinthenewerversions.Thiswouldmeanthatarowchangewillgenerate2insteadof1writeset,and4insteadof2,whenPKareinvolved.Thiswillmeanthatatransactionwillbecertifiedagainsttwowritesetsinsteadofjustone.Togeneratebothversions(withandwithoutcollation)ofthehash,itfirstconvertsusingwithoutcollationsupportalgorithm(oldalgorithm),andthenusingwithcollationsupportconversionalgorithm,andaddsgeneratedvaluetokey_list_to_hashvector,forhashgenerationlater.Sincethecollationwritesetisbiggerorequalthantherawone,wedogeneratefirstthecollationandreusethebufferwithouttheneedtoresizefortheraw.*/KEY_PART_INFOFieldfor(intcollation_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长度"uinti=0;for(/*empty*/;i<table->key_info[key_number].user_defined_key_parts;i++)//开始扫描每一个相应的字段{//readtheprimarykeyfieldvaluesinstr.intindex=table->key_info[key_number].key_part[i].fieldnr;//TABLEst_keyKEY_PART_INFO字段在表中的相应位置size_tlength=0;/*IgnoreifthevalueisNULL.*/if(table->field[index-1]->is_null())//Field**field;/*Pointertofields*/**point->[*field,*field,*field...]这里有多态每种字段类型有自己的各种算法break;//如果字段为空或者值为空返回//convertusingcollationsupportconversionalgorithmif(COLLATION_CONVERSION_ALGORITHM==collation_conversion_algorithm)//如果采用校队算法{constCHARSET_INFO*cs=table->field[index-1]->charset();length=cs->coll->strnxfrmlen(cs,table->field[index-1]->pack_length());//获取长度主键值}//convertusingwithoutcollationsupportalgorithmelse{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));}//convertusingcollationsupportconversionalgorithmif(COLLATION_CONVERSION_ALGORITHM==collation_conversion_algorithm){/*converttonormalizedstringandstoresothatitcanbesortedusingbinarycomparisonfunctionslikememcmp.*/table->field[index-1]->make_sort_key((uchar*)pk_value,length);//将字段的值存入到pk_value中,各种类型都有make_sort_key函数pk_value[length]=0;}//convertusingwithoutcollationsupportalgorithmelse{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'代表长度13pke.append(value_length);//计入长度}/*IfanypartofthekeyisNULL,ignoreaddingittohashkeys.NULLcannotconflictwithanyvalue.Eg:createtablet1(iintprimarykeynotnull,jint,kint,uniquekey(j,k));insertintot1values(1,2,NULL);insertintot1values(2,2,NULL);=>thisisallowed.*/if(i==table->key_info[key_number].user_defined_key_parts)//如果所有的索引字段都扫描完成{//最后得到的字符串为非唯一索引名称+分隔符+库名+分隔符+库名长度+表名+分隔符+表名长度+索引字段1数值+分隔符+索引字段1长度[+索引字段2数值+分隔符+索引字段2长度.....]generate_hash_pke(pke,collation_conversion_algorithm,thd);//对pke内存空间做HASHwriteset_hashes_added++;#ifndefDBUG_OFFwrite_sets.push_back(pke);//写入到writeset并且加入到写集合中#endif}

“MySQL写集合是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!