☰
MySQL 8.0 GDB源码调试MVCC一致性读
2026/9/29 4:29:19 网站建设 项目流程

在 MySQL 中,一致性读,也被称为"快照读"。一致性读是通过 read view + undo 版本重建,不会加锁,也不会阻塞其他事务的读,大大提高了并发读取的效率。

启动 gdb,设置断点,跟踪对应的函数

  • MVCC 一致性读主入口:只看一致性读 + 指定表
[root@ecs-test-node2 ~]# gdb -p 1129464breakrow_search_mvccifprebuilt->select_lock_type==LOCK_NONE&&$_streq(prebuilt->index->table->name.m_name,"summer/emp")(gdb)breakrow_search_mvccifprebuilt->select_lock_type==LOCK_NONE&&$_streq(prebuilt->index->table->name.m_name,"summer/emp")Breakpoint1at 0x4ff9bb8:file/data/mysql-8.0.42/storage/innobase/row/row0sel.cc, line4427.
  • 快照建立
breaktrx_assign_read_view commands silentprintf"== [snapshot] trx=%p id=%lu\n", trx, trx->id bt4continueend(gdb)breaktrx_assign_read_view Breakpoint2at 0x50b91a3:file/data/mysql-8.0.42/storage/innobase/trx/trx0trx.cc, line2320.(gdb)commands Type commandsforbreakpoint(s)2, one per line. End with a line saying just"end".\>silent\>printf"== [snapshot] trx=%p id=%lu\n", trx, trx->id\>bt4\>continue\>end
  • 可见性判定
breaklock_clust_rec_cons_read_sees commands silentprintf"== [cons-sees] table=%s\n", index->table->name.m_namecontinueend Breakpoint3at 0x4e2c620:file/data/mysql-8.0.42/storage/innobase/lock/lock0lock.cc, line242.(gdb)commands Type commandsforbreakpoint(s)3, one per line. End with a line saying just"end".>silent>printf"== [cons-sees] table=%s\n", index->table->name.m_name>continue>end(gdb)infobreakNum Type Disp Enb Address What1breakpoint keep y 0x0000000004ff9bb8inrow_search_mvcc(unsigned char*, page_cur_mode_t, row_prebuilt_t*, unsigned long, unsigned long)at /data/mysql-8.0.42/storage/innobase/row/row0sel.cc:4427 stop onlyifprebuilt->select_lock_type==LOCK_NONE&&$_streq(prebuilt->index->table->name.m_name,"summer/emp")2breakpoint keep y 0x00000000050b91a3intrx_assign_read_view(trx_t*)at /data/mysql-8.0.42/storage/innobase/trx/trx0trx.cc:2320 silentprintf"== [snapshot] trx=%p id=%lu\n", trx, trx->id bt4continue3breakpoint keep y 0x0000000004e2c620inlock_clust_rec_cons_read_sees(unsigned char const*, dict_index_t*, unsigned long const*, ReadView*)at /data/mysql-8.0.42/storage/innobase/lock/lock0lock.cc:242 silentprintf"== [cons-sees] table=%s\n", index->table->name.m_namecontinue
  • undo 版本重建
breakrow0sel.cc:3071# row_sel_build_prev_vers_for_mysqlbreakrow_vers_build_for_consistent_readbreaktrx_undo_prev_version_build(gdb)breakrow0sel.cc:3071 Breakpoint4at 0x4ff6755:file/data/mysql-8.0.42/storage/innobase/row/row0sel.cc, line3076.(gdb)breakrow_vers_build_for_consistent_read Breakpoint5at 0x502334d:file/data/mysql-8.0.42/storage/innobase/row/row0vers.cc, line1254.(gdb)breaktrx_undo_prev_version_build Breakpoint6at 0x5097ae6:file/data/mysql-8.0.42/storage/innobase/trx/trx0rec.cc, line2452.

开启测试会话

提前开启两个会话,一个会话用于一致性读,一个用于更新记录。

-- 会话1BEGIN;SELECT*FROMempWHEREid=1;-- 建立快照(命中 trx_assign_read_view)-- 会话2BEGIN;UPDATEempSETloc='Beijing'WHEREid=1;-- 提交COMMIT;-- 会话1(同一事务再读)SELECT*FROMempWHEREid=1;-- 可见性判定失败 → 走 undo 重建链

MVCC 主要入口函数

开启一个事务,执行查询。

会话1root[summer]>begin;Query OK,0rowsaffected(0.00sec)root[summer]>select*fromempwhereid=1;+----+------+--------+|id|name|loc|+----+------+--------+|1|lily|Dalian|+----+------+--------+1rowinset(0.73sec)
==[snapshot]trx=0x7f116a6d83e8id=0#0 trx_assign_read_view (trx=0x7f116a6d83e8) at /data/mysql-8.0.42/storage/innobase/trx/trx0trx.cc:2320#1 0x0000000004ffac94 in row_search_mvcc (buf=0x7f10e80353e0 "\214\002", mode=PAGE_CUR_GE, prebuilt=0x7f10e81b4228, match_mode=1, direction=0)at /data/mysql-8.0.42/storage/innobase/row/row0sel.cc:4833#2 0x0000000004d31ab8 in ha_innobase::index_read (this=0x7f10e80ffa20, buf=0x7f10e80353e0 "\214\002", key_ptr=0x7f10e8d539b8 "\001", key_len=4,find_flag=HA_READ_KEY_EXACT)at /data/mysql-8.0.42/storage/innobase/handler/ha_innodb.cc:10312#3 0x0000000003a12382 in handler::index_read_map (this=0x7f10e80ffa20, buf=0x7f10e80353e0 "\214\002", key=0x7f10e8d539b8 "\001", keypart_map=1,find_flag=HA_READ_KEY_EXACT)at /data/mysql-8.0.42/sql/handler.h:5403==[cons-sees]table=summer/emp

会话2执行更新操作。

会话2root[summer]>BEGIN;Query OK,0rowsaffected(0.00sec)root[summer]>UPDATEempSETloc='Beijing'WHEREid=1;Query OK,1rowaffected(0.01sec)Rowsmatched:1Changed:1Warnings:0root[summer]>COMMIT;Query OK,0rowsaffected(0.00sec)会话1root[summer]>SELECT*FROMempWHEREid=1;
Thread35"connection"hit Breakpoint1, row_search_mvcc(buf=0x7f10e80353e0"\214\001",mode=PAGE_CUR_GE,prebuilt=0x7f10e81b4228,match_mode=1,direction=0)at /data/mysql-8.0.42/storage/innobase/row/row0sel.cc:44274427DBUG_TRACE;(gdb)p prebuilt->trx->id$1=0(gdb)p *trx->read_view Cannot access memory at address 0x4860ec8348535441(gdb)s AutoDebugTrace::AutoDebugTrace(this=0x7f11306f3aa0,function=0x73c0388"dberr_t row_search_mvcc(byte*, page_cur_mode_t, row_prebuilt_t*, ulint, ulint)",filename=0x73bead8"/data/mysql-8.0.42/storage/innobase/row/row0sel.cc",line=4427)at /data/mysql-8.0.42/include/my_dbug.h:122122const char *begin=strchr(function,' ');(gdb)s123if(begin!=nullptr){(gdb)li118class AutoDebugTrace{119public:120AutoDebugTrace(const char *function, const char *filename, int line){121// Remove thereturntype,ifit's there. 122 const char *begin = strchr(function, '');123if(begin!=nullptr){124function=begin +1;125}126127// Cut it off at the first parenthesis;the argument list is

undo 版本重建

Thread35"connection"hit Breakpoint4, row_sel_build_prev_vers_for_mysql(read_view=0x7f1164324d20,clust_index=0x7f10e8101178,prebuilt=0x7f10e81b4228,rec=0x7f115526fbe6"\200",offsets=0x7f11306f31f8,offset_heap=0x7f11306f3528,old_vers=0x7f11306f2ea8,vrow=0x0,mtr=0x7f11306f3530,lob_undo=0x7f10e81b4440)at /data/mysql-8.0.42/storage/innobase/row/row0sel.cc:30763076DBUG_TRACE;(gdb)li3071[[nodiscard]]static dberr_t row_sel_build_prev_vers_for_mysql(3072ReadView *read_view, dict_index_t *clust_index, row_prebuilt_t *prebuilt,3073const rec_t *rec, ulint **offsets, mem_heap_t **offset_heap,3074rec_t **old_vers, const dtuple_t **vrow, mtr_t *mtr,3075lob::undo_vers_t *lob_undo){3076DBUG_TRACE;30773078dberr_t err;30793080if(prebuilt->old_vers_heap){(gdb)c Continuing. Thread35"connection"hit Breakpoint5, row_vers_build_for_consistent_read(rec=0x7f115526fbe6"\200",mtr=0x7f11306f3530,index=0x7f10e8101178,offsets=0x7f11306f31f8,view=0x7f1164324d20,offset_heap=0x7f11306f3528,in_heap=0x7f10e8e794a0,old_vers=0x7f11306f2ea8,vrow=0x0,lob_undo=0x7f10e81b4440)at /data/mysql-8.0.42/storage/innobase/row/row0vers.cc:12541254DBUG_TRACE;(gdb)li1249@return DB_SUCCESS or DB_MISSING_HISTORY */1250dberr_t row_vers_build_for_consistent_read(1251const rec_t *rec, mtr_t *mtr, dict_index_t *index, ulint **offsets,1252ReadView *view, mem_heap_t **offset_heap, mem_heap_t *in_heap,1253rec_t **old_vers, const dtuple_t **vrow, lob::undo_vers_t *lob_undo){1254DBUG_TRACE;1255const rec_t *version;1256rec_t *prev_version;1257trx_id_t trx_id;1258mem_heap_t *heap=nullptr;(gdb)c Continuing. Thread35"connection"hit Breakpoint6, trx_undo_prev_version_build(index_rec=0x7f115526fbe6"\200",index_mtr=0x7f11306f3530,rec=0x7f115526fbe6"\200",index=0x7f10e8101178,offsets=0x7f11306f3200,heap=0x7f10e8f19070,old_vers=0x7f11306f2bb8,v_heap=0x0,vrow=0x0,v_status=0,lob_undo=0x7f10e81b4440)at /data/mysql-8.0.42/storage/innobase/trx/trx0rec.cc:24522452DBUG_TRACE;(gdb)li2447const rec_t *index_rec ATTRIB_USED_ONLY_IN_DEBUG,2448mtr_t *index_mtr ATTRIB_USED_ONLY_IN_DEBUG, const rec_t *rec,2449const dict_index_t *const index, ulint *offsets, mem_heap_t *heap,2450rec_t **old_vers, mem_heap_t *v_heap, const dtuple_t **vrow, ulint v_status,2451lob::undo_vers_t *lob_undo){2452DBUG_TRACE;24532454trx_undo_rec_t *undo_rec=nullptr;2455dtuple_t *entry;2456trx_id_t rec_trx_id;(gdb)c Continuing. Thread35"connection"hit Breakpoint1, row_search_mvcc(buf=0x7f10e80353e0"\214\001",mode=PAGE_CUR_GE,prebuilt=0x7f10e81b4228,match_mode=1,direction=0)at /data/mysql-8.0.42/storage/innobase/row/row0sel.cc:44274427DBUG_TRACE;(gdb)n4429dict_index_t *index=prebuilt->index;(gdb)n4430bool comp=dict_table_is_comp(index->table);(gdb)n4431const dtuple_t *search_tuple=prebuilt->search_tuple;(gdb)n4432btr_pcur_t *pcur=prebuilt->pcur;(gdb)n4433trx_t *trx=prebuilt->trx;(gdb)n4439const rec_t *prev_rec=nullptr;(gdb)n4441const rec_t *prev_rec_debug=nullptr;(gdb)n304622unique_search=true;(gdb)li4617rows with the same key valueifone of the columns is the SQL4618null. A clustered index under MySQL can never contain null4619columns because we demand that all the columnsinprimary key4620are non-null. */46214622unique_search=true;46234624/* Evenifthe condition is unique, MySQL seems to try to4625retrieve also a second rowifa primary key containsmorethan46261column. Return immediatelyifthis is not a HANDLER(gdb)bt#0 row_search_mvcc (buf=0x7f10e80353e0 "\214\001", mode=PAGE_CUR_GE, prebuilt=0x7f10e81b4228, match_mode=1, direction=0)at /data/mysql-8.0.42/storage/innobase/row/row0sel.cc:4622#1 0x0000000004d31ab8 in ha_innobase::index_read (this=0x7f10e80ffa20, buf=0x7f10e80353e0 "\214\001", key_ptr=0x7f10e8d539b8 "\001", key_len=4,find_flag=HA_READ_KEY_EXACT)at /data/mysql-8.0.42/storage/innobase/handler/ha_innodb.cc:10312#2 0x0000000003a12382 in handler::index_read_map (this=0x7f10e80ffa20, buf=0x7f10e80353e0 "\214\001", key=0x7f10e8d539b8 "\001", keypart_map=1,find_flag=HA_READ_KEY_EXACT)at /data/mysql-8.0.42/sql/handler.h:5403#3 0x0000000003a00728 in handler::ha_index_read_map (this=0x7f10e80ffa20, buf=0x7f10e80353e0 "\214\001", key=0x7f10e8d539b8 "\001", keypart_map=1,find_flag=HA_READ_KEY_EXACT)at /data/mysql-8.0.42/sql/handler.cc:3239#4 0x00000000035b023a in read_const (table=0x7f10e80880c0, ref=0x7f10e8d52ec0) at /data/mysql-8.0.42/sql/sql_executor.cc:3675#5 0x00000000035afcff in join_read_const_table (tab=0x7f10e8d52de0, pos=0x7f10e8d52f98) at /data/mysql-8.0.42/sql/sql_executor.cc:3579#6 0x00000000035f5ed4 in JOIN::extract_func_dependent_tables (this=0x7f10e8d526e8) at /data/mysql-8.0.42/sql/sql_optimizer.cc:5837#7 0x00000000035f4a58 in JOIN::make_join_plan (this=0x7f10e8d526e8) at /data/mysql-8.0.42/sql/sql_optimizer.cc:5366#8 0x00000000035e77e6 in JOIN::optimize (this=0x7f10e8d526e8, finalize_access_paths=true) at /data/mysql-8.0.42/sql/sql_optimizer.cc:695#9 0x00000000036ae86d in Query_block::optimize (this=0x7f10e8f0e078, thd=0x7f10e80118e0, finalize_access_paths=true) at /data/mysql-8.0.42/sql/sql_select.cc:2001#10 0x000000000375c348 in Query_expression::optimize (this=0x7f10e8f0df90, thd=0x7f10e80118e0, materialize_destination=0x0, create_iterators=true,finalize_access_paths=true)at /data/mysql-8.0.42/sql/sql_union.cc:1019#11 0x00000000036ac55e in Sql_cmd_dml::execute_inner (this=0x7f10e8d52260, thd=0x7f10e80118e0) at /data/mysql-8.0.42/sql/sql_select.cc:999#12 0x00000000036aba23 in Sql_cmd_dml::execute (this=0x7f10e8d52260, thd=0x7f10e80118e0) at /data/mysql-8.0.42/sql/sql_select.cc:785#13 0x00000000036266d2 in mysql_execute_command (thd=0x7f10e80118e0, first_level=true) at /data/mysql-8.0.42/sql/sql_parse.cc:4724#14 0x0000000003628aae in dispatch_sql_command (thd=0x7f10e80118e0, parser_state=0x7f11306f6970) at /data/mysql-8.0.42/sql/sql_parse.cc:5385#15 0x000000000361e683 in dispatch_command (thd=0x7f10e80118e0, com_data=0x7f11306f7a60, command=COM_QUERY) at /data/mysql-8.0.42/sql/sql_parse.cc:2055#16 0x000000000361c5ca in do_command (thd=0x7f10e80118e0) at /data/mysql-8.0.42/sql/sql_parse.cc:1440#17 0x0000000003844598 in handle_connection (arg=0xd0a6bd0) at /data/mysql-8.0.42/sql/conn_handler/connection_handler_per_thread.cc:303--Type<RET>formore, q to quit, c tocontinuewithout paging--#18 0x00000000058bf53d in pfs_spawn_thread (arg=0xd03ce30) at /data/mysql-8.0.42/storage/perfschema/pfs.cc:3050#19 0x00007f117b3281ca in start_thread () from /lib64/libpthread.so.0#20 0x00007f1179848953 in clone () from /lib64/libc.so.6

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询