大家都知道,galera cluster的作用是支持多节点写入,这样可以避免普通的mysql的主从切换时,造成数据冲突或者丢失。而支持多节点写入机制的原理是在事务提交层,做了主键冲突检查机制,也就是两个节点不能同时处理同一行数据,如果发现冲突,后面提交的那个事务则会回滚。
现在我们来分析一下原理,首先来张galera 官方的文档图:
从上面这个图可以看到,galera 集群跟普通的mysql处理sql语句不同的地方,就在后面提交的部分,也就是commit 命令之后的部分。 真正在数据库commit 之前,会做一下commit的认证(certification), 而认证的核心作用是,检查两个节点是否有事务冲突,也就是判断是否存在修改同一行的事务。
通过几番查找,找到了验证是否冲突的核心函数,如下:
/* returns true on collision, false otherwise */
staticbool
certify_v3(galera::Certification::CertIndexNG& cert_index_ng,
const galera::KeySet::KeyPart& key,
galera::TrxHandle* trx,
boolconst store_keys, boolconst log_conflicts)
{
galera::KeyEntryNG ke(key);
galera::Certification::CertIndexNG::iterator ci(cert_index_ng.find(&ke));
if (cert_index_ng.end() == ci)
{
if (store_keys)
{
galera::KeyEntryNG* const kep(new galera::KeyEntryNG(ke));
ci =cert_index_ng.insert(kep).first;
cert_debug << "created new entry";
}
return false;
}
else
{
cert_debug << "found existing entry";
galera::KeyEntryNG* const kep(*ci);
// Note: For we skip certification for isolated trxs, only
// cert index and key_list is populated.
return (!trx->is_toi() &&
certify_and_depend_v3(kep, key, trx, log_conflicts));
}
}
从上面函数的名字,我们就知道其是验证函数,来实现上述图表的验证功能。
certify_v3函数名中的v3,其实是该函数的版本号,因为还有其他版本的验证函数。下面来对上面的代码进行部分解析:
galera::KeyEntryNG ke(key); key 是函数的入参,表示的是想要提交的事务所涉及的其中一个key, 然后将做一下格式转换,方便下一行代码使用。
galera::Certification::CertIndexNG::iterator ci(cert_index_ng.find(&ke));
上行代码中cert_index_ng.find(&ke),就是在已有的key集合中查找是否存在跟事务中的key相冲突的key. 如果没有找到,则ci 就在cert_index_ng的末尾。于是有后面的判断。
if (cert_index_ng.end() == ci) 如果成立,则不存在相同的key, 然后将这个key进行cert_index_ng.insert(kep).first 操作,即放入cert_index_ng 集合列表中,并将其放在为集合列表的头部。 然后返回false , 即不存在冲突。
if (cert_index_ng.end() == ci) 如果不成立,则存在相同的key,需要做进一步的判断,即调用了函数 certify_and_depend_v3(kep, key, trx, log_conflicts)做进一步检查,如果该函数仍然返回true,则冲突。如果返回false ,则虽然存在相同的key, 但不冲突。
在一个sql事务提交时,galera集群中的节点都会执行这个函数,用于检查是否出现冲突。
下面是在执行sql节点上,certify_v3 函数的调用栈。
#1 0x00007f3b818be8f6 in certify_v3 (cert_index_ng=..., key=..., trx=0x7f3b0e8ae000, store_keys=true, log_conflicts=false) at galera/src/certification.cpp:571
#2 0x00007f3b818bea50 in galera::Certification::do_test_v3 (this=0x7f3b82f3a7b8, trx=0x7f3b0e8ae000, store_keys=true) at galera/src/certification.cpp:598
#3 0x00007f3b818bf55f in galera::Certification::do_test (this=0x7f3b82f3a7b8, trx=0x7f3b0e8ae000, store_keys=true) at galera/src/certification.cpp:749
#4 0x00007f3b818c0906 in galera::Certification::test (this=0x7f3b82f3a7b8, trx=0x7f3b0e8ae000, bval=true) at galera/src/certification.cpp:927
#5 0x00007f3b818c12f1 in galera::Certification::append_trx (this=0x7f3b82f3a7b8, trx=0x7f3b0e8ae000) at galera/src/certification.cpp:1033
#6 0x00007f3b818f4181 in galera::ReplicatorSMM::cert (this=0x7f3b82f39e00, trx=0x7f3b0e8ae000) at galera/src/replicator_smm.cpp:1700
#7 0x00007f3b818f0f29 in galera::ReplicatorSMM::cert_and_catch (this=0x7f3b82f39e00, trx=0x7f3b0e8ae000) at galera/src/replicator_smm.cpp:1785
#8 0x00007f3b818ec5c0 in galera::ReplicatorSMM::pre_commit (this=0x7f3b82f39e00, trx=0x7f3b0e8ae000, meta=0x7f3b1efff098) at galera/src/replicator_smm.cpp:730
#9 0x00007f3b819074a0 in galera_pre_commit (gh=0x7f3b82c993c0, conn_id=17, trx_handle=0x7f3b1efff0d0, flags=1, meta=0x7f3b1efff098) at galera/src/wsrep_provider.cpp:513
#10 0x00007f3b855f6f49 in wsrep_run_wsrep_commit (thd=thd@entry=0x7f3b1effa008, all=all@entry=true) at /data/mariadb/mariadb-10.1.14/sql/wsrep_hton.cc:483
#11 0x00007f3b855f7e58 in wsrep_prepare (hton=<optimized out>, thd=0x7f3b1effa008, all=<optimized out>) at /data/mariadb/mariadb-10.1.14/sql/wsrep_hton.cc:193
#12 0x00007f3b85656c32 in prepare_or_error (ht=ht@entry=0x7f3b82ee18c8, thd=thd@entry=0x7f3b1effa008, all=all@entry=true) at /data/mariadb/mariadb-10.1.14/sql/handler.cc:1147
#13 0x00007f3b856591bd in ha_commit_trans (thd=thd@entry=0x7f3b1effa008, all=all@entry=true) at /data/mariadb/mariadb-10.1.14/sql/handler.cc:1425
#14 0x00007f3b855bd0c4 in trans_commit (thd=thd@entry=0x7f3b1effa008) at /data/mariadb/mariadb-10.1.14/sql/transaction.cc:236
#15 0x00007f3b854f3786 in mysql_execute_command (thd=thd@entry=0x7f3b1effa008) at /data/mariadb/mariadb-10.1.14/sql/sql_parse.cc:4996
#16 0x00007f3b854f89f7 in mysql_parse (thd=thd@entry=0x7f3b1effa008, rawbuf=rawbuf@entry=0x7f3b0e872020 "commit", length=length@entry=6, parser_state=parser_state@entry=0x7f3b765f6610)
at /data/mariadb/mariadb-10.1.14/sql/sql_parse.cc:7314
#17 0x00007f3b854f9181 in wsrep_mysql_parse (thd=thd@entry=0x7f3b1effa008, rawbuf=0x7f3b0e872020 "commit", length=6, parser_state=parser_state@entry=0x7f3b765f6610)
at /data/mariadb/mariadb-10.1.14/sql/sql_parse.cc:7136
#18 0x00007f3b854fb6ab in dispatch_command (command=command@entry=COM_QUERY, thd=thd@entry=0x7f3b1effa008, packet=packet@entry=0x7f3b21787009 "commit", packet_length=packet_length@entry=6)
at /data/mariadb/mariadb-10.1.14/sql/sql_parse.cc:1484
#19 0x00007f3b854fc511 in do_command (thd=0x7f3b1effa008) at /data/mariadb/mariadb-10.1.14/sql/sql_parse.cc:1107
#20 0x00007f3b855afc54 in do_handle_one_connection (thd_arg=thd_arg@entry=0x7f3b1effa008) at /data/mariadb/mariadb-10.1.14/sql/sql_connect.cc:1350
#21 0x00007f3b855afe27 in handle_one_connection (arg=0x7f3b1effa008) at /data/mariadb/mariadb-10.1.14/sql/sql_connect.cc:1262
#22 0x00007f3b84c98dc5 in start_thread () from /lib64/libpthread.so.0
#23 0x00007f3b83935ced in clone () from /lib64/libc.so.6