海山数据库(He3DB)源码详解:海山Mysql 崩溃恢复(3)-日志应用

海山数据库(He3DB)源码详解:海山Mysql 崩溃恢复(2)-日志解析 MySQL redo日志崩溃恢复整体流程如图所示,本文将针对日志解析过程中涉及到的几个函数源码进行详细解析。 阅读详情

海山数据库(He3DB)源码详解:海山Mysql 崩溃恢复(3)-日志应用

崩溃恢复之日志应用

recv_recovery_from_checkpoint_start_for_se(flushed_lsn)为崩溃恢复的入口函数,其从innobase_start_or_create_for_mysql()调用,传入参数flushed_lsn是读取存在系统表空间的fil_write_flushed_lsn获得的,该函数在日志解析、日志应用等线程创建之前,进行崩溃恢复操作。

MySQL redo日志崩溃恢复整体流程如图所示,其中,日志应用的流程主要集中在右边部分,recv_parse_or_apply_log_rec_body用于解析或应用日志记录体,recv_recover_page_func用于恢复页面函数,这是应用日志记录到数据库页面的关键步骤。

图中还显示了日志解析和日志应用流程之间的交互,特别是在解析日志记录后,如何将它们应用到数据库页面以完成恢复过程。本文将针对日志应用过程中涉及到的几个函数源码进行详细解析。

在这里插入图片描述

1、recv_apply_hashed_log_recs()函数流程

void
recv_apply_hashed_log_recs(
/*=======================*/
	ibool	allow_ibuf)	
{
	recv_addr_t* recv_addr;
	ulint	i;
	ibool	has_printed	= FALSE;
	mtr_t	mtr;
loop:
	mutex_enter(&(recv_sys->mutex));

	if (recv_sys->apply_batch_on) {

		mutex_exit(&(recv_sys->mutex));

		os_thread_sleep(500000);

		goto loop;
	}

	ut_ad(!allow_ibuf == log_mutex_own());

	if (!allow_ibuf) {
		recv_no_ibuf_operations = true;
	}

	recv_sys->apply_log_recs = TRUE;
	recv_sys->apply_batch_on = TRUE;

	for (i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {

		for (recv_addr = static_cast<recv_addr_t*>(
				HASH_GET_FIRST(recv_sys->addr_hash, i));
		     recv_addr != 0;
		     recv_addr = static_cast<recv_addr_t*>(
				HASH_GET_NEXT(addr_hash, recv_addr))) {

			if (srv_is_tablespace_truncated(recv_addr->space)) {
				ut_a(recv_sys->n_addrs);
				recv_addr->state = RECV_DISCARDED;
				recv_sys->n_addrs--;
				continue;
			}

			if (recv_addr->state == RECV_DISCARDED) {
				ut_a(recv_sys->n_addrs);
				recv_sys->n_addrs--;
				continue;
			}

			const page_id_t		page_id(recv_addr->space,
							recv_addr->page_no);
			bool			found;
			const page_size_t&	page_size
				= fil_space_get_page_size(recv_addr->space,
							  &found);

			ut_ad(found);

			if (recv_addr->state == RECV_NOT_PROCESSED) {
				if (!has_printed) {
					ib::info() << "Starting an apply batch"
						" of log records"
						" to the database...";
					fputs("InnoDB: Progress in percent: ",
					      stderr);
					has_printed = TRUE;
				}

				mutex_exit(&(recv_sys->mutex));

				if (buf_page_peek(page_id)) {
					buf_block_t*	block;

					mtr_start(&mtr);

					block = buf_page_get(
						page_id, page_size,
						RW_X_LATCH, &mtr);

					buf_block_dbg_add_level(
						block, SYNC_NO_ORDER_CHECK);

					recv_recover_page(FALSE, block);
					mtr_commit(&mtr);
				} else {
					recv_read_in_area(page_id);
				}

				mutex_enter(&(recv_sys->mutex));
			}
		}

		if (has_printed
		    && (i * 100) / hash_get_n_cells(recv_sys->addr_hash)
		    != ((i + 1) * 100)
		    / hash_get_n_cells(recv_sys->addr_hash)) {

			fprintf(stderr, "%lu ", (ulong)
				((i * 100)
				 / hash_get_n_cells(recv_sys->addr_hash)));
		}
	}

	/* Wait until all the pages have been processed */

	while (recv_sys->n_addrs != 0) {

		mutex_exit(&(recv_sys->mutex));

		os_thread_sleep(500000);

		mutex_enter(&(recv_sys->mutex));
	}

	if (has_printed) {

		fprintf(stderr, "\n");
	}

	if (!allow_ibuf) {

		/* Flush all the file pages to disk and invalidate them in
		the buffer pool */

		ut_d(recv_no_log_write = true);
		mutex_exit(&(recv_sys->mutex));
		log_mutex_exit();

		/* Stop the recv_writer thread from issuing any LRU
		flush batches. */
		mutex_enter(&recv_sys->writer_mutex);

		/* Wait for any currently run batch to end. */
		buf_flush_wait_LRU_batch_end();

		os_event_reset(recv_sys->flush_end);
		recv_sys->flush_type = BUF_FLUSH_LIST;
		os_event_set(recv_sys->flush_start);
		os_event_wait(recv_sys->flush_end);

		buf_pool_invalidate();

		/* Allow batches from recv_writer thread. */
		mutex_exit(&recv_sys->writer_mutex);

		log_mutex_enter();
		mutex_enter(&(recv_sys->mutex));
		ut_d(recv_no_log_write = false);

		recv_no_ibuf_operations = false;
	}

	recv_sys->apply_log_recs = FALSE;
	recv_sys->apply_batch_on = FALSE;

	recv_sys_empty_hash();

	if (has_printed) {
		ib::info() << "Apply batch completed";
	}

	mutex_exit(&(recv_sys->mutex));
}
  • recv_apply_hashed_log_recs()函数的作用是将存储的日志记录从哈希表中清空,并将它们应用到相应的页面中。下面是对该函数的详细解析。

1、设置正在进行应用状态

recv_sys->apply_log_recs = TRUE;
recv_sys->apply_batch_on = TRUE;

2、取出哈希节点:
redo日志最终存储在recv_sys->addr_hash哈希表结构如下所示:
在这里插入图片描述

  • 因此想要取出哈希节点需要进行2次for循环:
  • 第1次for循环,是对哈希桶进行遍历。
  • 第2次for循环,对哈希链表进行变量,此时每个节点代表每个将进行应用的数据页
for (i = 0; i < hash_get_n_cells(recv_sys->addr_hash); i++) {

		for (recv_addr = static_cast<recv_addr_t*>(
				HASH_GET_FIRST(recv_sys->addr_hash, i));
		     recv_addr != 0;
		     recv_addr = static_cast<recv_addr_t*>(
				HASH_GET_NEXT(addr_hash, recv_addr)))

3、如果表已经truncate:

  • 检查当前表是否truncate,修改truncate表的哈希节点状态为RECV_DISCARDED,并且减少n_addrs。
    if (srv_is_tablespace_truncated(recv_addr->space)) {
        /* Avoid applying REDO log for the tablespace
        that is schedule for TRUNCATE. */
        ut_a(recv_sys->n_addrs);
        recv_addr->state = RECV_DISCARDED;
        recv_sys->n_addrs--;
        continue;
    }

4、缓冲池中存在当前数据页,调用recv_recover_page宏。

  • recv_recover_page宏将调用recv_recover_page_func()进行应用操作。
  • 当前if条件下的流程是1个mtr。

5、如果不在缓冲池中,调用recv_read_in_area()读取数据页到缓冲池。

  • recv_read_in_area()函数后续采用异步读取的方式,由io_handler_thread完成读取,并在读取完成后判断是否在崩溃恢复过程中,在崩溃恢复过程中随后进行应用操作。
    if (buf_page_peek(page_id)) {
        buf_block_t*	block;

        mtr_start(&mtr);

        block = buf_page_get(
            page_id, page_size,
            RW_X_LATCH, &mtr);

        buf_block_dbg_add_level(
            block, SYNC_NO_ORDER_CHECK);

        recv_recover_page(FALSE, block);
        mtr_commit(&mtr);
    } else {
        recv_read_in_area(page_id);
    }

6、等待所有数据页应用完成。

 	while (recv_sys->n_addrs != 0) {

		mutex_exit(&(recv_sys->mutex));

		os_thread_sleep(500000);

		mutex_enter(&(recv_sys->mutex));
	}

7、修改全局状态并且情况recv_sys的哈希表。

  • recv_sys_empty_hash()在清理了recv_sys旧的哈希表后,为recv_sys分配了新的哈希表。
	recv_sys->apply_log_recs = FALSE;
	recv_sys->apply_batch_on = FALSE;

	recv_sys_empty_hash();

该函数的大致流程如下图所示:
在这里插入图片描述

2、recv_recover_page_func()函数流程

void
recv_recover_page_func(
	buf_block_t*	block)	/*!< in/out: buffer block */
{
	page_t*		page;
	page_zip_des_t*	page_zip;
	recv_addr_t*	recv_addr;
	recv_t*		recv;
	byte*		buf;
	lsn_t		start_lsn;
	lsn_t		end_lsn;
	lsn_t		page_lsn;
	lsn_t		page_newest_lsn;
	ibool		modification_to_page;
	mtr_t		mtr;

	mutex_enter(&(recv_sys->mutex));

	if (recv_sys->apply_log_recs == FALSE) {

		mutex_exit(&(recv_sys->mutex));

		return;
	}

	recv_addr = recv_get_fil_addr_struct(block->page.id.space(),
					     block->page.id.page_no());

	if ((recv_addr == NULL)
	    || (recv_addr->state == RECV_BEING_PROCESSED)
	    || (recv_addr->state == RECV_PROCESSED)) {
		ut_ad(recv_addr == NULL || recv_needed_recovery);

		mutex_exit(&(recv_sys->mutex));

		return;
	}

	recv_addr->state = RECV_BEING_PROCESSED;

	mutex_exit(&(recv_sys->mutex));

	mtr_start(&mtr);
	mtr_set_log_mode(&mtr, MTR_LOG_NONE);

	page = block->frame;
	page_zip = buf_block_get_page_zip(block);

	/* Read the newest modification lsn from the page */
	page_lsn = mach_read_from_8(page + FIL_PAGE_LSN);

	modification_to_page = FALSE;
	start_lsn = end_lsn = 0;

	recv = UT_LIST_GET_FIRST(recv_addr->rec_list);

	while (recv) {
		end_lsn = recv->end_lsn;

		ut_ad(end_lsn
		      <= UT_LIST_GET_FIRST(log_sys->log_groups)->scanned_lsn);

		if (recv->len > RECV_DATA_BLOCK_SIZE) {
			/* We have to copy the record body to a separate
			buffer */

			buf = static_cast<byte*>(ut_malloc_nokey(recv->len));

			recv_data_copy_to_buf(buf, recv);
		} else {
			buf = ((byte*)(recv->data)) + sizeof(recv_data_t);
		}

		if (recv->type == MLOG_INIT_FILE_PAGE) {
			page_lsn = page_newest_lsn;

			memset(FIL_PAGE_LSN + page, 0, 8);
			memset(UNIV_PAGE_SIZE - FIL_PAGE_END_LSN_OLD_CHKSUM
			       + page, 0, 8);

			if (page_zip) {
				memset(FIL_PAGE_LSN + page_zip->data, 0, 8);
			}
		}

		bool	skip_recv = false;
		if (srv_was_tablespace_truncated(fil_space_get(recv_addr->space))) {
			lsn_t	init_lsn =
				truncate_t::get_truncated_tablespace_init_lsn(
				recv_addr->space);
			skip_recv = (recv->start_lsn < init_lsn);
		}

		if (recv->start_lsn >= page_lsn
		    && !srv_is_tablespace_truncated(recv_addr->space)
		    && !skip_recv) {

			lsn_t	end_lsn;

			if (!modification_to_page) {

				modification_to_page = TRUE;
				start_lsn = recv->start_lsn;
			}

			DBUG_PRINT("ib_log",
				   ("apply " LSN_PF ":"
				    " %s len " ULINTPF " page %u:%u",
				    recv->start_lsn,
				    get_mlog_string(recv->type), recv->len,
				    recv_addr->space,
				    recv_addr->page_no));

			recv_parse_or_apply_log_rec_body(
				recv->type, buf, buf + recv->len,
				recv_addr->space, recv_addr->page_no,
				block, &mtr);

			end_lsn = recv->start_lsn + recv->len;
			mach_write_to_8(FIL_PAGE_LSN + page, end_lsn);
			mach_write_to_8(UNIV_PAGE_SIZE
					- FIL_PAGE_END_LSN_OLD_CHKSUM
					+ page, end_lsn);

			if (page_zip) {
				mach_write_to_8(FIL_PAGE_LSN
						+ page_zip->data, end_lsn);
			}
		}

		if (recv->len > RECV_DATA_BLOCK_SIZE) {
			ut_free(buf);
		}

		recv = UT_LIST_GET_NEXT(rec_list, recv);
	}

	mtr.discard_modifications();

	mtr_commit(&mtr);

	mutex_enter(&(recv_sys->mutex));

	if (recv_max_page_lsn < page_lsn) {
		recv_max_page_lsn = page_lsn;
	}

	recv_addr->state = RECV_PROCESSED;

	ut_a(recv_sys->n_addrs);
	recv_sys->n_addrs--;

	mutex_exit(&(recv_sys->mutex));

}
  • 在recv_recover_page_func()函数中,如果页面的日志序列号(LSN)小于日志记录的LSN,则将哈希日志记录应用到该页面。这可以在缓冲区页面刚刚被读入时调用,也可以对已经在缓冲区池中的页面调用。下面是对该函数的详细解析。

1、根据表空间号和页号取出对应日志链表。

	recv_addr = recv_get_fil_addr_struct(block->page.id.space(),
					     block->page.id.page_no());

	if ((recv_addr == NULL)
	    || (recv_addr->state == RECV_BEING_PROCESSED)
	    || (recv_addr->state == RECV_PROCESSED)) {
		ut_ad(recv_addr == NULL || recv_needed_recovery);

		mutex_exit(&(recv_sys->mutex));

		return;
	}

2、修改日志链表状态为RECV_BEING_PROCESSED。

recv_addr->state = RECV_BEING_PROCESSED;

3、从数据页中读取最后被修改的lsn值,如果是在热备份状态下,从缓冲池控制块读取lsn。

	page_lsn = mach_read_from_8(page + FIL_PAGE_LSN);

#ifndef UNIV_HOTBACKUP

	page_newest_lsn = buf_page_get_newest_modification(&block->page);

	if (page_newest_lsn) {

		page_lsn = page_newest_lsn;
	}
#else
	page_newest_lsn = 0;
#endif 

4、从日志链表读取日志,并且对每个日志进行循环。

recv = UT_LIST_GET_FIRST(recv_addr->rec_list);

while (recv) {
    ...
}

5、如果日志长度过长,采用动态分配空间将redo日志拷贝到这块内存中,否则直接使用recv中的数据。

    if (recv->len > RECV_DATA_BLOCK_SIZE) {

        buf = static_cast<byte*>(ut_malloc_nokey(recv->len));

        recv_data_copy_to_buf(buf, recv);
    } else {
        buf = ((byte*)(recv->data)) + sizeof(recv_data_t);
    }

6、如果当前表truncate,跳过truncate操作之前的redo日志

    bool	skip_recv = false;
    if (srv_was_tablespace_truncated(fil_space_get(recv_addr->space))) {
        lsn_t	init_lsn =
            truncate_t::get_truncated_tablespace_init_lsn(
            recv_addr->space);
        skip_recv = (recv->start_lsn < init_lsn);
    }

7、如果日志的lsn>页的lsn,调用recv_parse_or_apply_log_rec_body()进行日志应用,并在应用完成后,更新数据页中的lsn字段。

    if (!modification_to_page) {

        modification_to_page = TRUE;
        start_lsn = recv->start_lsn;
    }

    DBUG_PRINT("ib_log",
            ("apply " LSN_PF ":"
            " %s len " ULINTPF " page %u:%u",
            recv->start_lsn,
            get_mlog_string(recv->type), recv->len,
            recv_addr->space,
            recv_addr->page_no));

    recv_parse_or_apply_log_rec_body(
        recv->type, buf, buf + recv->len,
        recv_addr->space, recv_addr->page_no,
        block, &mtr);

    end_lsn = recv->start_lsn + recv->len;
    mach_write_to_8(FIL_PAGE_LSN + page, end_lsn);
    mach_write_to_8(UNIV_PAGE_SIZE
            - FIL_PAGE_END_LSN_OLD_CHKSUM
            + page, end_lsn);

    if (page_zip) {
        mach_write_to_8(FIL_PAGE_LSN
                + page_zip->data, end_lsn);
    }

8、如果当前redo日志使用的动态分配的内存,释放这部分内存,并且调用UT_LIST_GET_NEXT宏,获取日志链表的下一条日志,之后进入下一轮循环。

    if (recv->len > RECV_DATA_BLOCK_SIZE) {
        ut_free(buf);
    }

    recv = UT_LIST_GET_NEXT(rec_list, recv);

9、调用buf_flush_recv_note_modificatio()函数,更新数据页缓冲池控制块的newest_modification,并且在flush链表中重新排序
10、应用流程接收修改日志链表状态为RECV_PROCESSED并且减少n_addrs。

	recv_addr->state = RECV_PROCESSED;

	ut_a(recv_sys->n_addrs);
	recv_sys->n_addrs--;

	mutex_exit(&(recv_sys->mutex));

该函数的大致流程如下图所示:
在这里插入图片描述

3、recv_parse_or_apply_log_rec_body()函数流程

static
byte*
recv_parse_or_apply_log_rec_body(
	mlog_id_t	type,
	byte*		ptr,
	byte*		end_ptr,
	ulint		space_id,
	ulint		page_no,
	buf_block_t*	block,
	mtr_t*		mtr)
{
	ut_ad(!block == !mtr);

	switch (type) {

    /* 根据日志类型,调用对应函数 */
    ...

	default:
		break;
	}

	dict_index_t*	index	= NULL;
	page_t*		page;
	page_zip_des_t*	page_zip;
#ifdef UNIV_DEBUG
	ulint		page_type;
#endif /* UNIV_DEBUG */

	if (block) {
		/* Applying a page log record. */
		page = block->frame;
		page_zip = buf_block_get_page_zip(block);
		ut_d(page_type = fil_page_get_type(page));
	} else {
		/* Parsing a page log record. */
		page = NULL;
		page_zip = NULL;
		ut_d(page_type = FIL_PAGE_TYPE_ALLOCATED);
	}

	const byte*	old_ptr = ptr;

	switch (type) {
         /* 根据日志类型,调用对应函数 */
         ...
				break;
			}
		ptr = mlog_parse_nbytes(type, ptr, end_ptr, page, page_zip);
		if (ptr != NULL && page != NULL
		    && page_no == 0 && type == MLOG_4BYTES) {
			ulint	offs = mach_read_from_2(old_ptr);
			switch (offs) {
				fil_space_t*	space;
				ulint		val;
			default:
				break;
			 /* 根据日志类型,调用对应函数 */
             ...
				}
			}
		}
		break;
	case MLOG_REC_INSERT: case MLOG_COMP_REC_INSERT:
		...
    ...

	if (index) {
		dict_table_t*	table = index->table;

		dict_mem_index_free(index);
		dict_mem_table_free(table);
	}

	return(ptr);
}
  • recv_parse_or_apply_log_rec_body()函数的主要作用是根据redo日志类型,对redo日志的数据部分进行解析和应用的操作都是在该函数中完成的,该函数根据传入参数blcok是否为NULL,来区分是否进行日志应用操作,如果block == NULL,后续只进行日志解析,不进行日志应用。
ISO 26262中的失效率计算:Mission profile的使用 Mission profile:任务剖面,即产品在完成规定任务这段时间内所经历的时间和环境的时序描述;集成电路在工作时环境温度是在不停变化的,同时也存在运行状态和非运行状态;为此标准提出了根据任务剖面(Mission profile)以及集成电路的实际结温来评估热幅度变化、循环温度幅值等因素的影响;汽车电子元器件相关的Mission profile参考IEC TR 62380:Table 11。 阅读详情

相关推荐

彻底清理Easy Connect残留:从服务到驱动的完整卸载指南

在企业网络接入场景中,远程客户端常因内核驱动、系统服务和网络过滤组件导致卸载残留,进程无法结束、文件被锁定、注册表项顽固存在。其原理在于驱动挂载网络协议栈,服务守护进程,卸载时资源占用导致清理不彻底。彻底清理的技术价值在于恢复系统纯净、避免网络异常和软件冲突。典型应用场景包括企业办公机、个人笔记本更换或重装客户端。本文针对深信服Easy Connect,提供从停止服务、删除驱动到清理注册表和虚拟网卡的分步操作链路,并给出验证方法和避坑经验。

weixin_31292729的博客 235

移动云海山数据库(He3DB)-MySQL关键类之THD

父类2:Query_arena 看它的元素定义Item * m_item_list,它内部一定维护着一个很大的列表 而Item又继承自Parse_tree_node,所以它内部一定维护着Query语句(存储过程)的相关解析后的元素,也就是抽象语法树的结节。MDL_context 元数据锁的上下文,维护元数据的锁,mdl.h代码中有其详细描述:元数据库锁的上下文信息,每个server都有这样一个上下文。THR_LOCK_INFO 线程锁信息,thr_lock.h代码中有其详细描述:拥有锁的线程的描述。

于巍,花名漠雪,移动云数据库技术委员会主任,海山数据库研发负责人,PG中文社区常委,PG中文社区海外联络人。15年数据库内核研发经验,4年美国、以色列等海外研发经历,华为GaussDB、阿里云PolarDB、移动云He3DB等核心架构 595

ESP32—C3实现网页跳转

用户点击HTML页面中的按钮。浏览器根据按钮的href属性向服务器发送HTTP GET请求。服务器根据请求的URL调用相应的回调函数。服务器发送包含新页面HTML内容的HTTP响应。浏览器解析并显示新的HTML页面。这个过程对于用户来说是无缝的,看起来就像是直接从一个页面跳转到另一个页面。实际上,背后发生了HTTP请求和响应的过程。

qq_59527512的博客 1022

特性介绍 | MySQL生态现有计算下推方案汇总

计算下推是数据库优化器优化查询性能的一种常见手段,早期的数据库系统提及的计算下推一般是指谓词下推,其理论源自关系代数理论。2000 年以后,随着 Oracle RAC 的盛行以及一众开源分布式数据库的崛起,存算分离的概念逐步流行,计算下推的涵盖范围由此从基本的谓词+投影下推延伸到了数据库所支持的一切可能计算的下推(JOIN、聚合、完整 query、部分 query 等)。

dbkernel 的博客 1773

工业焊缝缺陷检测实战:我用PatchCore在自建数据集上踩过的那些坑

本文详细介绍了PatchCore算法在工业焊缝缺陷检测中的实战应用,包括数据准备、模型调参和阈值优化等关键环节。通过分析焊缝图像的特殊性和anomaly detection技术的优化策略,分享了在自建数据集上提升检测精度的实用经验,为工业质检领域提供有价值的参考方案。

weixin_42624889的博客 397

200smart 模拟量转换库的建立

模拟量当前值 =(工程值 - 工程量下限)*(模拟量上限 - 模拟量下限)/ (工程量上限 - 工程量下限)+ 模拟量下限2. 建立变量表3. 程序。

mmxxoo_xxmmp的博客 4213

移动云海山数据库(He3DB)-MySQL关键类之Lex

unit是整个表达式 fake:Helper query block for query expression with UNION or multi-level ORDER BY/LIMIT select是一个个查询块,通过链表连接all_select_list连接连接 第一个select查询块是unit的salve、unit是select的master节点。TABLE_LIST **query_tables_last: 指向前序链表中next_global数据的最后一个元素。执行案例:联表子查询。

于巍,花名漠雪,移动云数据库技术委员会主任,海山数据库研发负责人,PG中文社区常委,PG中文社区海外联络人。15年数据库内核研发经验,4年美国、以色列等海外研发经历,华为GaussDB、阿里云PolarDB、移动云He3DB等核心架构 847

SAP 实例解析分期付款配置及应用

SAP系统的付款条件支持分期付款功能,其奥妙之处就在于需要定义一个母条款和一个子条款,其中,母条款需要标识“分期付款”。 首先,定义分期付款的母条款OBB8:             然后,定义子条款(即分期付款的具体方式)OBB9:   由上图可以看出,分期付款的配置还是很强大的,针对每次付款,都可以进行第二层次付款条件的设置。   然后,我们做一笔发票,

heng0757的专栏 1万+

海山数据库(He3DB)源码详解:海山MySQL redo日志-MTR

一、对底层页面中的一次原子访问的过程称之为一个,简称mtr。一个事务可以包含若干条语句,每一条语句其实是由若干个mtr组成,每一个mtr又可以包含若干条redo日志。

He3DB_ht的博客 1097

simulink的电动汽车制动系统热管理仿真

本文系统研究了电动汽车制动系统热管理仿真技术。通过建立机电液多物理场耦合模型,分析了不同工况下的动态热平衡特性。提出基于MPC的动态调节和NSGA-II多目标优化算法,使电池温度波动降低60%。创新性采用石墨烯相变材料与微通道液冷复合技术,冷却系统能耗减少40%。研究构建了完整的工程验证平台,涵盖200kW电制动测试台和高精度热成像系统。展望指出,未来将结合Transformer预测和强化学习算法,进一步优化热管理策略。该研究为电动汽车制动系统热管理提供了从理论建模到工程实施的完整解决方案。

xiaoheshang_123的博客 121

国科大学习资料--高级软件工程-复习题简答题答案

国科大学习资料–高级软件工程(罗铁坚 魏峻 刘丹)-复习题简答题答案 def fib3 n (1…n).inject([0,1]){|mem| puts mem[0]+mem[1]; [mem[1],mem[0]+mem[1]]} end =====================================================================================================================================

无名何许人的博客 1397

大云海山数据库(He3DB)兼容 MySQL 日期函数:DATE_ADD的内核实现与复合间隔支持

本文针对大云海山数据库(He3DB)兼容MySQL生态时缺乏DATE_ADD和DATE_SUB函数的问题,提出了一种内核级解决方案。通过在语法解析器和时间处理模块中新增C函数,实现了对MySQL特有的11种复合时间间隔格式的完整支持。该方案采用"语法扩展+格式转换+原生运算"的三层架构,成功实现了与MySQL完全兼容的时间运算功能,显著降低了数据库迁移成本,为异构数据库兼容提供了可复用的技术范式。

weixin_43544348的博客 1219

Simulink仿真状态空间中状态变量时域响应

起因 今天老师留了一道关于状态空间变量响应的题,于是想用simulink仿真,一开始使用state-space模块,但是发现它只能输出y,没办法显示状态变量的变化情况。然而状态变量之间的关系都可以表示为一阶微分方程组,因此我们考虑在Simulink中搭建微分方程组来求解这个问题。 微分方程组 x1˙=x2x2˙=−4x1−5x2+1(1)\dot{x_1}=x_2\\ \dot{x_2}=-4x_...

ZYunfei的博客 7448

海山数据库(He3DB)源码详解:海山Mysql 崩溃恢复(1)-准备及结束

选取最近发生的那次checkpoint的信息。衡量checkpoint发生时间早晚的信息就是所谓的,只要把和这两个block中的值读出来比一下大小,哪个的值更大,说明哪个block存储的就是最近的一次checkpoint信息。这样就能拿到最近发生的checkpoint对应的值以及它在redo日志文件组中的偏移量。为什么要把checkpoint信息存储到2个block中?万一就在某次写checkpoint信息的过程中MySQL崩溃了,有可能导致正在写入的这个block中的checkpoint信息不正确。

于巍,花名漠雪,移动云数据库技术委员会主任,海山数据库研发负责人,PG中文社区常委,PG中文社区海外联络人。15年数据库内核研发经验,4年美国、以色列等海外研发经历,华为GaussDB、阿里云PolarDB、移动云He3DB等核心架构 1121

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

海山数据库(He3DB)源码详解:海山MySQL redo日志(1)

redo日志即重做日志,记录了事务对数据库做了哪些修改。当发生数据库服务器宕机、或者脏页未写入磁盘,可以通过redo日志恢复。

yang123456213的博客 2164

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

上一篇: 海山数据库(He3DB)源码详解:海山Mysql 崩溃恢复(2)-日志解析
下一篇: 大云海山数据库(He3DB)源码详解:He3DB-MultiXactIdCreateFromMembers
DAWN1221
博客等级 码龄7年 893粉丝 95原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值