海山数据库(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,后续只进行日志解析,不进行日志应用。
ml307 二次开发 中移物联网旗下4g模组官方网站官方教程id=514官方客户不知道啥时候上班 早上10点也说不在服务时间内 找不到官方的sdk下载路径。 阅读详情

相关推荐

Glue)《Multi-omics single-cell data integration and regulatory inference with graph-linked embedding》

论文引入GLUE框架,通过知识引导图(guidance graph)显式建模跨层调控交互,桥接特征空间,实现准确、鲁棒、可扩展的整合。近年来,单细胞测序技术(如scATAC-seq用于染色质可及性、snmC-seq用于DNA甲基化、scRNA-seq用于转录组)允许同时探测多个组学层,揭示细胞异质性和调控机制。GLUE优于现有方法:桥接特征空间,避免信息丢失,支持多组学和调控推断。用于整合未配对的单细胞多组学数据(如scRNA-seq、scATAC-seq和snmC-seq),并同时进行调控交互推断。

m0_58293236的博客 1151

移动云海山数据库(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

3dMax效率翻倍!用Octopus章鱼插件打造你的专属快捷菜单(附MaxScript入门指南)

本文详细介绍了如何利用Octopus插件和MaxScript脚本在3dMax中打造高效快捷菜单,显著提升三维建模与动画制作的工作效率。通过饼状菜单系统、自动化宏录制和脚本控制,用户可实现个性化工作流,节省大量操作时间。文章还提供了MaxScript入门指南,帮助开发者快速掌握这一强大工具。

weixin_30920853的博客 151

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

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

dbkernel 的博客 1773

大模型训练显存瘦身全记录:从ZeRO-3参数分区到CPU Offload的硬件协奏曲

本文详细解析了如何通过ZeRO-3参数分区与CPU/NVMe卸载技术优化大模型训练的显存使用。结合bf16混合精度与梯度累积等策略,实现在消费级GPU上高效训练百亿参数模型,显著提升硬件利用率与训练效率。

weixin_29051245的博客 435

西门子S7-1200和IFM IO-Link主站连接与设定

硬件配置:S7-1200 1214DC/DC/DC,IFM AL1102支持Profinet,IFM支持IO-Link的流量传感器SBG233,流量开关SI5010,接近开关IF6123。第三步:打开IFM moneo configure可查看到流量传感器SBG233 PLC映射所有输出共占用4个byte,也可在IFM官网下载传感器对应IODD文件。第五步:组态编程编译下载。最近新做了一个项目,用到了IO-Link设备,今天来分享一下S7-1200和IFM IO-Link主站模块AL1102的连接过程。

GreatShen168的博客 6492

移动云海山数据库(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

【语音识别入门】特征提取(Python完整代码)

给定一段音频,请提取12维MFCC特征和23维FBank,阅读代码预加重、分帧、加窗部分,完善作业代码中FBank特征提取和MFCC特征提取部分,并给出最终的FBank特征和MFCC特征,存储在纯文本中,用默认的配置参数,无需进行修改。抽样时频率不够高,抽样出来的点既代表了信号中的低频信号的样本值,也同时代表高频信号样本值,在信号重建的时候,高频信号被低频信号代替,两种波形完全重叠在一起,形成严重失真。DFT得到了每个频带上信号的能量,但是人耳对频率的感知不是等间隔的,近似于对数函数。

weixin_51293984的博客 1万+

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

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

He3DB_ht的博客 1097

紫光展锐面试——FPGA/IC设计面试面经案例总结

自我介绍;研究生的主要研究方向是什么?为什么想要应聘数字芯片相关的工作?简单介绍下数字IC设计流程。逻辑综合主要做什么?你有实际做过逻辑综合工作吗?你了解SS corner和FF corner吗?综合时应该使用哪个corner?综合时需要考虑hold吗?为什么?提高时钟频率对setup和hold有什么影响?为什么?hvt、rvt/svt、lvt有什么区别?c40、c50是指什么概念?有什么区别?你对EDA工具有过了解吗?

FPGA/MATLAB学习教程/源码/项目合作开发 1562

飞牛fnNAS搭建Web网页版OFFICE(WPS)软件

本文介绍了如何在飞牛NAS上通过Docker容器部署WPS Office办公软件。主要内容包括:启用Docker服务、准备项目文件夹结构、使用Compose方式构建容器、解决端口冲突问题、配置中文输入法和字体库,以及通过ZeroTier实现远程访问。文章详细说明了每个步骤的操作方法,包括容器启动、输入法设置、字体添加和文件存储位置配置。最后还介绍了使用ZeroTier内网穿透技术实现远程访问NAS上WPS服务的方法,让用户可以在不同设备上随时编辑NAS中的文件。整个部署过程完整清晰,为NAS用户提供了便捷的

dgnankai的专栏 1699

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

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

weixin_43544348的博客 1219

WPF+MVVM案例实战(二十三)- 阴影效果详解

通过附加属性实现阴影效果,通过改变不同的阴影属性值达到不同阴影效果

lingshuangcanxue 559

海山数据库(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

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

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

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

yang123456213的博客 2164

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

上一篇: 海山数据库(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、付费专栏及课程。

余额充值