MySQL 大字段问题

来源:这里教程网 时间:2026-03-01 16:45:13 作者:

开发规范中,一般不让使用大字段,大部分关于给出的原因有影响缓存,高并发查询的情况会打满带宽,溢出存储,查询的时候多了一次指针查找,在姜老师的书中还看到了一个原因,就是大字段的dml 会使用悲观的方式处理,这种对二叉树加x-latch,会导致并发性能的下降。 姜老师的书中描述是大字段的插入,更新,删除会使用悲观操作,但是我测试的情况是针对insert没有使用悲观操作,update是使用了悲观操作 update的堆栈如下

btr_cur_pessimistic_update(unsigned long flags, btr_cur_t * cursor, big_rec_t ** big_rec, upd_t * update, unsigned long cmpl_info, que_thr_t * thr, mtr_t * mtr) (/root/mysql-5.0.15/innobase/btr/btr0cur.c:1766)
row_upd_clust_rec(upd_node_t * node, dict_index_t * index, que_thr_t * thr, mtr_t * mtr) (/root/mysql-5.0.15/innobase/row/row0upd.c:1530)
row_upd_clust_step(upd_node_t * node, que_thr_t * thr) (/root/mysql-5.0.15/innobase/row/row0upd.c:1742)
row_upd(que_thr_t * thr, upd_node_t * node) (/root/mysql-5.0.15/innobase/row/row0upd.c:1819)
row_upd_step(que_thr_t * thr) (/root/mysql-5.0.15/innobase/row/row0upd.c:1948)
row_update_for_mysql(unsigned char * mysql_rec, row_prebuilt_t * prebuilt) (/root/mysql-5.0.15/innobase/row/row0mysql.c:1386)
ha_innobase::update_row(ha_innobase * const this, const mysql_byte * old_row, mysql_byte * new_row) (/root/mysql-5.0.15/sql/ha_innodb.cc:3622)
mysql_update(THD * thd, TABLE_LIST * table_list, List<Item> & fields, List<Item> & values, COND * conds, uint order_num, ORDER * order, ha_rows limit, enum_duplicates handle_duplicates, bool ignore) (/root/mysql-5.0.15/sql/sql_update.cc:429)
mysql_execute_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:3192)
mysql_parse(THD * thd, char * inBuf, uint length) (/root/mysql-5.0.15/sql/sql_parse.cc:5536)
dispatch_command(enum_server_command command, THD * thd, char * packet, unsigned int packet_length) (/root/mysql-5.0.15/sql/sql_parse.cc:1697)
do_command(THD * thd) (/root/mysql-5.0.15/sql/sql_parse.cc:1498)
handle_one_connection(void * arg) (/root/mysql-5.0.15/sql/sql_parse.cc:1143)
libpthread.so.0!start_thread (Unknown Source:0)
libc.so.6!clone (Unknown Source:0)

在更新的时候,先加的s index->lock,然后加了x锁堆栈如下

mysqld!btr_cur_search_to_nth_level(dict_index_t*, unsigned long, dtuple_t const*, page_cur_mode_t, unsigned long, btr_cur_t*, unsigned long, char const*, unsigned long, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/btr/btr0cur.cc:996)
mysqld!btr_pcur_open_with_no_init_func(dict_index_t*, dtuple_t const*, page_cur_mode_t, unsigned long, btr_pcur_t*, unsigned long, char const*, unsigned long, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/include/btr0pcur.ic:528)
mysqld!btr_pcur_restore_position_func(unsigned long, btr_pcur_t*, char const*, unsigned long, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/btr/btr0pcur.cc:365)
mysqld!row_upd_clust_rec(unsigned long, upd_node_t*, dict_index_t*, unsigned long*, mem_block_info_t**, que_thr_t*, mtr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:2694)
mysqld!row_upd_clust_step(upd_node_t*, que_thr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:2957)
mysqld!row_upd(upd_node_t*, que_thr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:3054)
mysqld!row_upd_step(que_thr_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0upd.cc:3200)
mysqld!row_update_for_mysql_using_upd_graph(unsigned char const*, row_prebuilt_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0mysql.cc:2582)
mysqld!row_update_for_mysql(unsigned char const*, row_prebuilt_t*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/row/row0mysql.cc:2672)
mysqld!ha_innobase::update_row(unsigned char const*, unsigned char*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/innobase/handler/ha_innodb.cc:8257)
mysqld!handler::ha_update_row(unsigned char const*, unsigned char*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/handler.cc:8134)
mysqld!mysql_update(THD*, List<Item>&, List<Item>&, unsigned long long, enum_duplicates, unsigned long long*, unsigned long long*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_update.cc:894)
mysqld!Sql_cmd_update::try_single_table_update(THD*, bool*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_update.cc:2906)
mysqld!Sql_cmd_update::execute(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_update.cc:3037)
mysqld!mysql_execute_command(THD*, bool) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:3616)
mysqld!mysql_parse(THD*, Parser_state*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:5584)
mysqld!dispatch_command(THD*, COM_DATA const*, enum_server_command) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1491)
mysqld!do_command(THD*) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/sql_parse.cc:1032)
mysqld!::handle_connection(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/sql/conn_handler/connection_handler_per_thread.cc:313)
mysqld!::pfs_spawn_thread(void *) (/Users/xiaoyu.bai/Downloads/mysql-5.7.29/storage/perfschema/pfs.cc:2197)

在btr_cur_search_to_nth_level函数中,执行下面的case分支,加x

switch (latch_mode) {
case BTR_MODIFY_TREE:
        /* Most of delete-intended operations are purging.
        Free blocks and read IO bandwidth should be prior
        for them, when the history list is glowing huge. */
if (lock_intention == BTR_INTENTION_DELETE
&& trx_sys->rseg_history_len > BTR_CUR_FINE_HISTORY_LENGTH
&& buf_get_n_pending_read_ios()) {
mtr_x_lock(dict_index_get_lock(index), mtr);

blob,text 更新都会执行mtr_x_lock(dict_index_get_lock(index), mtr);即使是更新表中的其他字段,比如varchar 字段也会加x锁,不包含text,blob类型的表更新是不会有的。所以在规范说明中,可以并发影响这一条。 5.7中的 btr_cur_search_to_nth_level 伪代码 用s_latch_by_caller 判断caller是否已经有了s锁设置btr_op类型 通过modify_external判断caller在修改叶子结点的时候是否要分配或释放外部存储的字段 如果有搜索latch,先释放 根据latch_mode判断对index加什么锁 如果是修改tree,删除操作,如果history list过大超过10万,并且buffer pool有pending的io ,那么会对index tree加x锁,如果是spatial index悲观删除,加x锁,否则加sx锁,这个是5.7优化的。 其他的如果caller没有s 锁,不修改外部存储,加s锁,修改外部存储加sx锁 在root开始搜索在非页节点搜索的时候,搜索的模式会改变下,在搜索leaf的时候,使用原本的搜索模式。进入循环搜索直到需要的level 开始读取page,tree_bocks维护整个树的block 在重试搜索之前节点的时候,对left兄弟也是加latch,在修改节点的时候,也会获取父的lock,否则不会更改的。 如果是叶节点,就对叶节点加latch ,btr_cur_latch_leaves 如果已经到了想要的level推出循环 不是想要的level,如果删除的是页中的第一个或最后一个记录,那么要修改上层的node_ptr,要反向修改,这个会释放root以及所有block,将block标记unfix , 释放锁上的latch。 边界条件的处理到了指定的level,如果root latch已经释放,重新加sx锁。对branch block加x latch 如果是叶子节点,为了防止其他修改tree操作,对tree 加sx-latch,因为有了sx,所以上层的块加的锁可以释放了。 在对叶子节点加锁的时候,最左右兄弟节点也是加了x-latch的。 sx latch已经降低了对读的影响。在释放tree的s锁时候,如果是修改外部存储字段,那么会继续保持tree的sx锁。 在悲观更新的时候,是持有tree的x-latch以及page的x-latch的,如果更新发生在leaf,那么page的兄弟节点也会加x-latch,这种的并发性比sx低。 有兴趣学习源码的加群一起学习啊 QQ: 700072075

相关推荐