Postgresql源码(92)深入分析HOT更新

news2024/10/2 10:36:48

0 概述与总结

  • hot更新已经有几篇分析了,这里是最后一篇(总结性的,前面的可以忽略)。
  • 前面在看update代码时,大部分集中在heap_update上,没有涉及寻找HOT链的逻辑。本篇重点看HOT链是如何使用的。

(总结速查)

(lp=line pointer:页面中等宽数组,每一个指向页面底部的数据区域)

关键步骤总结(no vacuum场景):

  • HOT链的头部元素的lp始终保存,索引始终指向这个lp(即使这个lp指向行更新了也只是把数据删了,保留lp指针)
  • HOT链的中间元素都带HEAP_HOT_UPDATED标记,HOT链的最后一个元素只有HEAP_ONLY_TUPLE标记。
  • HOT链在更新时有三个关键步骤:
    • 1 走索引找到链头lp:table_index_fetch_tuple(下文3.1)
    • 2 遍历HOT链确定需要的lp:heap_hot_search_buffer(下文3.2)
    • 3 碎片整理,使数据区域更紧凑,会更新lp的指向位置:compactify_tuples(下文3.3)
    • 4 使用找到的lp获取页面位置,memcopy数据上去完成update:heap_update(下文3.4)

堆栈:

ExecModifyTable
  ExecProcNode        // ExecProcNode = 0x783005 <ExecIndexScan>
  | ExecIndexScan
  |   ExecScan
  |     ExecScanFetch
  |       IndexNext
  |         index_getnext_slot
  |           tid = index_getnext_tid                       // 3.1 总是拿到ip_posid = 130
  |           index_fetch_heap
  |             table_index_fetch_tuple(ItemPointer tid)    // {ip_posid = 130}
  |               heapam_index_fetch_tuple
  |                 heap_hot_search_buffer                  // 3.2 遍历HOT链找旧元组
  |                 heap_page_prune_opt                     // 3.3 碎片整理
  |                   heap_page_prune
  |                     heap_page_prune_execute
  |                       PageRepairFragmentation
  |                         compactify_tuples
  ExecUpdate
    ExecUpdateAct
      table_tuple_update
        heapam_tuple_update
          heap_update                                       // 3.4 更新

1 分析用例

照常先给出分析用例。

-- 测试表,单页可以放136条数据
drop table testbl;
create table testbl(i int primary key not null, id int not null, info varchar(200) not null);
alter table testbl set (autovacuum_enabled = false);
insert into testbl select generate_series(1,130), (random()*100)::integer, repeat('DUfw',(5)::integer);


select * from testbl limit 10;
 i  | id |         info         
----+----+----------------------
  1 | 57 | DUfwDUfwDUfwDUfwDUfw
  2 |  2 | DUfwDUfwDUfwDUfwDUfw
  3 | 29 | DUfwDUfwDUfwDUfwDUfw
  4 | 37 | DUfwDUfwDUfwDUfwDUfw
  5 |  2 | DUfwDUfwDUfwDUfwDUfw
  6 | 44 | DUfwDUfwDUfwDUfwDUfw
  7 | 53 | DUfwDUfwDUfwDUfwDUfw
  8 | 24 | DUfwDUfwDUfwDUfwDUfw
  9 | 49 | DUfwDUfwDUfwDUfwDUfw
 10 | 17 | DUfwDUfwDUfwDUfwDUfw

2 HOT更新实验

2.1 总结:等宽更新

  • HOT多次更新后,发现HOT链会复用元组,并不会一直延长。
  • HOT根节点(本例中的lp=130)不变,复用后续的节点。
  • 复用节点带HEAP_ONLY_TUPLE | HEAP_HOT_UPDATED标记。
UPDATElp=130lp=131lp=132
第一次原tuple:HEAP_HOT_UPDATED新tuple:HEAP_ONLY_TUPLE
第二次原tuple:HEAP_ONLY_TUPLE HEAP_HOT_UPDATED新tuple:HEAP_ONLY_TUPLE
第三次新tuple:HEAP_ONLY_TUPLE原tuple:HEAP_ONLY_TUPLE HEAP_HOT_UPDATED
第四次原tuple:HEAP_ONLY_TUPLE HEAP_HOT_UPDATED新tuple:HEAP_ONLY_TUPLE
第五次新tuple:HEAP_ONLY_TUPLE原tuple:HEAP_ONLY_TUPLE HEAP_HOT_UPDATED

数组区域状态(等宽更新,对应上表)

注意uppdate都是使用lp有效的位置,用之前先做碎片整理,把有效的向下移动,填充到删除的地方。然后再insert。
在这里插入图片描述

2.1 总结:不等宽更新

数组区域状态(不等宽更新)

注意第四次更新和第五次更新,新数据更宽了,可以明显看到碎片整理的过程:

  • 第五次更新时,先把132的数据向下移动到888-967;然后再对132的数据进行更新;更新后132被删除;131被复用,放在了页面的upper指针+数据大小的位置。
    在这里插入图片描述

实验:更新前xid=8169

SELECT lp, lp_off, t_xmin, t_xmax, t_field3, t_ctid, t_infomask2, t_infomask FROM heap_page_items(get_raw_page('testbl', 0));

 lp  | lp_off | t_xmin | t_xmax | t_field3 | t_ctid  | t_infomask2 | t_infomask 
-----+--------+--------+--------+----------+---------+-------------+------------
 130 |    912 |   8169 |      0 |        0 | (0,130) |           3 |       2050

============
t_infomask
============
2050(0x802) = HEAP_XMAX_INVALID | HEAP_HASVARWIDTH

============
t_infomask2(11 bits for number of attributes)
============
number_of_ = 3

实验:第一次更新xid=8170

update testbl set info = 'DDDDDDDDDDDDDDDDDDD1' where i = 130;

SELECT lp, lp_off, t_xmin, t_xmax, t_field3, t_ctid, t_infomask2, t_infomask FROM heap_page_items(get_raw_page('testbl', 0));
 lp  | lp_off | t_xmin | t_xmax | t_field3 | t_ctid  | t_infomask2 | t_infomask 
-----+--------+--------+--------+----------+---------+-------------+------------
 130 |    912 |   8169 |   8170 |        0 | (0,131) |       16387 |        258
 131 |    856 |   8170 |      0 |        0 | (0,131) |       32771 |      10242
 
============
t_infomask
============
258(0x102) = HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
10242(0x2802) = HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH

============
t_infomask2
============
16387(0x4003) = HEAP_HOT_UPDATED | 3attributes
32771(0x8003) = HEAP_ONLY_TUPLE | 3attributes

实验:第二次更新xid=8171

update testbl set info = 'DDDDDDDDDDDDDDDDDDD2' where i = 130;

SELECT lp, lp_off, t_xmin, t_xmax, t_field3, t_ctid, t_infomask2, t_infomask FROM heap_page_items(get_raw_page('testbl', 0));
 lp  | lp_off | t_xmin | t_xmax | t_field3 | t_ctid  | t_infomask2 | t_infomask 
-----+--------+--------+--------+----------+---------+-------------+------------
 130 |    131 |        |        |          |         |             |           
 131 |    912 |   8170 |   8171 |        0 | (0,132) |       49155 |       8450
 132 |    856 |   8171 |      0 |        0 | (0,132) |       32771 |      10242

============
t_infomask
============
8450(0x2102) = HEAP_UPDATED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
10242(0x2802) = HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH

============
t_infomask2
============
49155(0xC003) = HEAP_ONLY_TUPLE | HEAP_HOT_UPDATED | 3attributes
32771(0x8003) = HEAP_ONLY_TUPLE | 3attributes

实验:第三次更新xid=8172

update testbl set info = 'DDDDDDDDDDDDDDDDDDD3' where i = 130;

SELECT lp, lp_off, t_xmin, t_xmax, t_field3, t_ctid, t_infomask2, t_infomask FROM heap_page_items(get_raw_page('testbl', 0));
 lp  | lp_off | t_xmin | t_xmax | t_field3 | t_ctid  | t_infomask2 | t_infomask 
-----+--------+--------+--------+----------+---------+-------------+------------
 130 |    132 |        |        |          |         |             |           
 131 |    856 |   8172 |      0 |        0 | (0,131) |       32771 |      10242
 132 |    912 |   8171 |   8172 |        0 | (0,131) |       49155 |       8450
 
============
t_infomask
============
10242(0x2802) = HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH
8450(0x2102) = HEAP_UPDATED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH

============
t_infomask2
============
49155(0xC003) = HEAP_ONLY_TUPLE | HEAP_HOT_UPDATED | 3attributes
32771(0x8003) = HEAP_ONLY_TUPLE | 3attributes

实验:第四次更新xid=8173

update testbl set info = 'DDDDDDDDDDDDDDDDDDD4' where i = 130;

SELECT lp, lp_off, t_xmin, t_xmax, t_field3, t_ctid, t_infomask2, t_infomask FROM heap_page_items(get_raw_page('testbl', 0));
 lp  | lp_off | t_xmin | t_xmax | t_field3 | t_ctid  | t_infomask2 | t_infomask 
-----+--------+--------+--------+----------+---------+-------------+------------
 130 |    131 |        |        |          |         |             |           
 131 |    912 |   8172 |   8173 |        0 | (0,132) |       49155 |       8450
 132 |    856 |   8173 |      0 |        0 | (0,132) |       32771 |      10242

============
t_infomask
============
8450(0x2102) = HEAP_UPDATED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH
10242(0x2802) = HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH

============
t_infomask2
============
49155(0xC003) = HEAP_ONLY_TUPLE | HEAP_HOT_UPDATED | 3attributes
32771(0x8003) = HEAP_ONLY_TUPLE | 3attributes

实验:第五次更新xid=8174

update testbl set info = 'DDDDDDDDDDDDDDDDDDD5' where i = 130;

SELECT lp, lp_off, t_xmin, t_xmax, t_field3, t_ctid, t_infomask2, t_infomask FROM heap_page_items(get_raw_page('testbl', 0));
 lp  | lp_off | t_xmin | t_xmax | t_field3 | t_ctid  | t_infomask2 | t_infomask 
-----+--------+--------+--------+----------+---------+-------------+------------
 130 |    132 |        |        |          |         |             |           
 131 |    856 |   8174 |      0 |        0 | (0,131) |       32771 |      10242
 132 |    912 |   8173 |   8174 |        0 | (0,131) |       49155 |       8450

============
t_infomask
============
10242(0x2802) = HEAP_UPDATED | HEAP_XMAX_INVALID | HEAP_HASVARWIDTH
8450(0x2102) = HEAP_UPDATED | HEAP_XMIN_COMMITTED | HEAP_HASVARWIDTH

============
t_infomask2
============
49155(0xC003) = HEAP_ONLY_TUPLE | HEAP_HOT_UPDATED | 3attributes
32771(0x8003) = HEAP_ONLY_TUPLE | 3attributes

3 场景分析:lp=130空、lp=131删除、lp=132有效

  • 当前HOT链:130(重定向)—>132(有效)
  • 预期发生:碎片整理:132准备转移到131的位置;新增数据到132原来的位置、用131指向新增数据。
    在这里插入图片描述

3.1 索引扫描

从顶层ExecModifyTable进入索引扫描部分,因为关掉了VAUUM,索引总是返回130:index_getnext_tid

ExecModifyTable
  ExecProcNode        // ExecProcNode = 0x783005 <ExecIndexScan>
    ExecIndexScan
      ExecScan
        ExecScanFetch
          IndexNext
            index_getnext_slot
              tid = index_getnext_tid                       // 3.1 总是拿到ip_posid = 130  <<<<
              index_fetch_heap
                table_index_fetch_tuple(ItemPointer tid)    // {ip_posid = 130}
                  heapam_index_fetch_tuple
                    heap_hot_search_buffer                  // 3.2 遍历HOT链找旧元组
                    heap_page_prune_opt                     // 3.3 碎片整理

3.2 遍历HOT链找旧元组

再用130去找元组:heap_hot_search_buffer

bool
heap_hot_search_buffer(
			ItemPointer tid,  // {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 130}
			Relation relation, Buffer buffer,
			Snapshot snapshot, HeapTuple heapTuple,
			bool *all_dead, bool first_call)
{
	...
	...	
	/* Scan through possible multiple members of HOT-chain */
	for (;;)
	{
		ItemId		lp;

		/* check for bogus TID */
		if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp))
			break;
		

【第一轮循环读到HOT链130,找到132】

这里比较重要,offnum现在还是130,但是lp拿出来就直接是132了:
ItemIdData = {lp_off = 132, lp_flags = 2, lp_len = 0}
这里的lp_flags=2表示LP_REDIRECT,重定向到132。
#define LP_UNUSED 0 /* unused (should always have lp_len=0) */
#define LP_NORMAL 1 /* used (should always have lp_len>0) */
#define LP_REDIRECT 2 /* HOT redirect (should have lp_len=0) */
#define LP_DEAD 3 /* dead, may or may not have storage */

在这里插入图片描述


【第二轮循环读到HOT链132,找到数据位置】

第二次进入循环后,offnum=132拿到的lp:
ItemIdData = {lp_off = 912, lp_flags = 1, lp_len = 53}(LP_NORMAL)

现在的912就是指向数据区域了。

		lp = PageGetItemId(dp, offnum);

		/* check for unused, dead, or redirected items */
		if (!ItemIdIsNormal(lp))
		{
			/* We should only see a redirect at start of chain */
			if (ItemIdIsRedirected(lp) && at_chain_start)
			{
				/* Follow the redirect */
				offnum = ItemIdGetRedirect(lp);

使用lp=132拿到offnum=132,继续下一轮循环继续遍历HOT链。

				at_chain_start = false;
				continue;
			}
			/* else must be end of chain */
			break;
		}

遍历完HOT链出循环,开始读132的数据部分,拼接元组。

		heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
		heapTuple->t_len = ItemIdGetLength(lp);
		heapTuple->t_tableOid = RelationGetRelid(relation);
		ItemPointerSet(&heapTuple->t_self, blkno, offnum);

		if (!skip)
		{
			/* If it's visible per the snapshot, we must return it */
			valid = HeapTupleSatisfiesVisibility(heapTuple, snapshot, buffer);
			HeapCheckForSerializableConflictOut(valid, relation, heapTuple,
												buffer, snapshot);

			if (valid)
			{
				ItemPointerSetOffsetNumber(tid, offnum);
				PredicateLockTID(relation, &heapTuple->t_self, snapshot,
								 HeapTupleHeaderGetXmin(heapTuple->t_data));
				if (all_dead)
					*all_dead = false;

找到132返回。

				return true;
			}
		}
		skip = false;

3.3 碎片整理

将132移动到131的位置上,因为131删掉已经是空洞了。

这里就不展开分析了。记录下函数堆栈。

ExecModifyTable
  ExecProcNode        // ExecProcNode = 0x783005 <ExecIndexScan>
    ExecIndexScan
      ExecScan
        ExecScanFetch
          IndexNext
            index_getnext_slot
              tid = index_getnext_tid                       // 总是拿到ip_posid = 130  <<<<
              index_fetch_heap
                table_index_fetch_tuple(ItemPointer tid)    // {ip_posid = 130}
                  heapam_index_fetch_tuple
                    heap_hot_search_buffer                  // 3.2 遍历HOT链找旧元组
                    heap_page_prune_opt                     // 3.3 碎片整理
                      heap_page_prune
                        heap_page_prune_execute
                          PageRepairFragmentation
                            compactify_tuples

3.4 开始更新heap_update

ExecModifyTable
  ExecProcNode        // ExecProcNode = 0x783005 <ExecIndexScan>
  | ExecIndexScan
  |   ExecScan
  |     ExecScanFetch
  |       IndexNext
  |         index_getnext_slot
  |           tid = index_getnext_tid                       // 总是拿到ip_posid = 130  <<<<
  |           index_fetch_heap
  |             table_index_fetch_tuple(ItemPointer tid)    // {ip_posid = 130}
  |               heapam_index_fetch_tuple
  |                 heap_hot_search_buffer                  // 3.2 遍历HOT链找旧元组
  |                 heap_page_prune_opt                     // 3.3 碎片整理
  |                   heap_page_prune
  |                     heap_page_prune_execute
  |                       PageRepairFragmentation
  |                         compactify_tuples
  ExecUpdate
    ExecUpdateAct
      table_tuple_update
        heapam_tuple_update
          heap_update

1 拿到数据地址

ItemId = {lp_off = 912, lp_flags = 1, lp_len = 53}

lp = PageGetItemId(page, ItemPointerGetOffsetNumber(otid));

2 拼接旧元组:

HeapTupleData = {t_len = 53, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 132}, t_tableOid = 32946, t_data = 0x2aaaab4ae610}

3 新元组位置未确定:

HeapTupleData = {t_len = 53, t_self = {ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, t_tableOid = 32946, t_data = 0x26fe440}

	oldtup.t_tableOid = RelationGetRelid(relation);
	oldtup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
	oldtup.t_len = ItemIdGetLength(lp);
	oldtup.t_self = *otid;
	
	newtup->t_tableOid = RelationGetRelid(relation);

4 判断旧元组可见性

result = TM_Ok

result = HeapTupleSatisfiesUpdate(&oldtup, cid, buffer);

5 同页更新使用use_hot_update

旧的加:HEAP_HOT_UPDATED
新的加:HEAP_ONLY_TUPLE

1: *newtup = {t_len = 53, t_self = {ip_blkid = {bi_hi = 65535, bi_lo = 65535}, ip_posid = 0}, t_tableOid = 32946, t_data = 0x26fe440}
3: oldtup = {t_len = 53, t_self = {ip_blkid = {bi_hi = 0, bi_lo = 0}, ip_posid = 132}, t_tableOid = 32946, t_data = 0x2aaaab4ae610}

6 insert插入新tuple

RelationPutHeapTuple执行insert操作,需要先找到插入位置。

RelationPutHeapTuple
	PageAddItem

核心逻辑(重要)

  • 正向遍历itemid,所以,数据区域是反向遍历的。
  • 找到空闲的lp131,然后132经过碎片整理下移到131的位置上了,原来132的位置在最上面空出来了,申请这个位置使用。
			for (offsetNumber = FirstOffsetNumber;
				 offsetNumber < limit;	/* limit is maxoff+1 */
				 offsetNumber++)
			{
				itemId = PageGetItemId(phdr, offsetNumber);

				/*
				 * We check for no storage as well, just to be paranoid;
				 * unused items should never have storage.  Assert() that the
				 * invariant is respected too.
				 */
				Assert(ItemIdIsUsed(itemId) || !ItemIdHasStorage(itemId));

				if (!ItemIdIsUsed(itemId) && !ItemIdHasStorage(itemId))
					break;
			}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/54424.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[附源码]计算机毕业设计JAVA鑫地酒店酒水库存管理系统论文

[附源码]计算机毕业设计JAVA鑫地酒店酒水库存管理系统论文 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; …

搜索技术——群智能

如果有兴趣了解更多相关内容&#xff0c;欢迎来我的个人网站看看&#xff1a;瞳孔空间 一&#xff1a;初识群智能 1.1&#xff1a;粒子群算法 粒子群算法&#xff0c;也称粒子群优化算法或鸟群觅食算法&#xff08;Particle Swarm Optimization&#xff09;&#xff0c;缩写…

语音特征:spectrogram、Fbank(fiterbank)、MFCC

1.各种语音特征 语音特征用于语音识别和语音合成等。 语音特征有声谱图spectrogram、Fbank(fiterbank)、MFCC(Mel-frequency cepstral coefficients)等。 Fbank 特征提取方法就是相当 于 MFCC 去掉最后一步的离散余弦变换&#xff08;有损变换&#xff09;. 在深度学习之前…

git学习笔记

1、安装及配置git 1、到官网下载git安装包&#xff1a;https://git-scm.com/download/win 2、安装完成后&#xff0c;菜单栏有如下工具 3、配置账户和邮件信息 $ git config --global user.name "xxx"$ git config --global user.email "xxxmegvii.com"4…

十大排序算法(C++)

十大排序算法Sorting algorithm(C) 百度百科&#xff1a; 所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。排序算法&#xff0c;就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地…

高通平台开发系列讲解(AI篇)如何让yolov5运行在SNPE

文章目录 一、模型下载二、模型转换三、模型量化四、后处理加速沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要介绍高通平台SNPE SDK运行yolov5。 一、模型下载 首先去git上下载yolov5s的代码和模型https://github.com/ultralytics/yolov5 二、模型转换 采…

Python爬虫实战,requests+xlwings模块,Python实现制作天气预报表!

前言 今天为大家介绍PythonExcel的实战项目&#xff0c;非常有趣&#xff0c;废话不多说。 Let’s start happily 开发工具 Python版本&#xff1a; 3.6.4 相关模块&#xff1a; xlwings模块 requests模块 pathlib模块 xlwings模块 json模块 环境搭建 安装Python并…

RAR压缩包,去除密码?

压缩包设置了加密&#xff0c;需要输入压缩包密码才能够顺利解压文件出来。但是有些时候&#xff0c;一些文件只需要一段时间内要加密&#xff0c;之后文件不需要加密了&#xff0c;每次解压文件的时候还是需要输入压缩包密码才行&#xff0c;就很麻烦&#xff0c;那么RAR压缩包…

SAP 接口主动推送企业微信异常消息

"推送企业微信格式lv_json { "msgtype": "markdown", "markdown": &&{ "content": "### 异常JOB通知\n >JOB名称&#xff1a; && gt_alv-jobname && \n 程序名称&#xff1a; && gt_…

戟星安全实验室|五分钟教你挖掘小程序漏洞

戟星安全实验室 忆享科技旗下高端的网络安全攻防服务团队.安服内容包括渗透测试、代码审计、应急响应、漏洞研究、威胁情报、安全运维、攻防演练等。 本文约1252字&#xff0c;阅读约需5分钟。 前言 现在大多小程序反编译教程所使用的都是node.js&#xff0c;操作过程较为麻烦…

第一周练习——认识复杂度和简单排序算法

前言&#xff1a; &#x1f44f;作者简介&#xff1a;我是笑霸final&#xff0c;一名热爱技术的在校学生。 &#x1f4dd;个人主页&#xff1a;个人主页1 || 笑霸final的主页2 &#x1f4d5;系列专栏&#xff1a;《数据结构与算法》 &#x1f4e7;如果文章知识点有错误的地方&a…

Mediapipe学习记录

学习文档 1、Google MediaPipe&#xff1a;设备端机器学习【完整解决方案】背后的技术实现 - 极术社区 - 连接开发者与智能计算生态 2、【转载】Google MediaPipe&#xff1a;设备端机器学习【完整解决方案】背后的技术实现 3、MediaPipe框架结构 - 走看看 Handtracking封装…

qt creator 设置 项目依赖关系

qt creator中有两种设置项目依赖关系的方式。 1、对于有依赖的项目&#xff0c;如果工程比较简单&#xff0c;可以将所有项目放到一个空的项目下&#xff0c;然后显示地指定从属关系&#xff0c;参考&#xff1a;qmake TEMPLATE subdirs_丘上人的博客-CSDN博客 2、通过qt cre…

leecode#Excel表列序号#组合两个表

题目描述&#xff1a; 给你一个字符串 columnTitle &#xff0c;表示 Excel 表格中的列名称。返回 该列名称对应的列序号 。 分析&#xff1a; 法1&#xff0c;进制转换 这道题要求将Excel 表中的列名称转换成相对应的列序号。由于Excel 表的列名称由大写字母组成&#xff…

vue学习53~60(Vue组件化编程)

2 Vue组件化编程 2.1 模块与组件、模块化与组件化 2.1.1 模块 理解:向外提供特定功能的js程序,一般就是一 个js文件为什么: js 文件很多很复杂作用:复用js,简化js的编写,提高js运行效率 2.1.2 组件 理解:用来实现局部(特定)功能效果的代码集合(html/css/js/image…)为什么…

正大国际期货:外盘期货交易中常见的五类技术分析方法

外盘期货交易中&#xff0c;技术分析是必不可少的&#xff0c;技术分析是指以市场行为为研究对象&#xff0c;以判断市场趋势并跟随趋势的周期性变化来进行一切金融衍生物交易决策的方法的总和。常用的技术方法大体上可以分五大类&#xff1a;指标类、切线类、形态类、波浪类、…

安卓程序逆向与防护

安卓程序逆向与防护实际用处代码打包生成apk的步骤程序基础逆向步骤程序基础防御代码混淆针对逆向工具的防御手段ProguardSO文件防止二次打包程序进阶逆向攻击实战练习逆向简易安卓程序实际用处 1.当想要获得一款前后端分离的安卓程序的数据时&#xff0c;一般会考虑抓包后端传…

Kanzi:关于kanzi的一点杂谈

概述&#xff1a; 做过开发的小伙伴在初次接触Kanzi Studio 这个开发工具时&#xff0c;可能有点摸不着头脑&#xff0c;由于 它不像Android studio 也 不像QT &#xff0c;而且最大的一个区别是&#xff1a;kanzi基本不用写代码逻辑来控制程序显示UI&#xff0c;但是Android …

做外贸一定要知道的20条经验教训

做外贸你必须知道的20课&#xff0c;由米贸搜为你整理如下: 1.在工厂里&#xff0c;客人抱怨价格太高的时候&#xff0c;我总是说一分钱一分货&#xff0c;质量好的回复。进入一家贸易公司后&#xff0c;我知道价格才是硬道理&#xff0c;尤其是大客人。对价格的考虑肯定高于对…

1776年美国才建国,那一年中国在干什么?

时代淘汰你&#xff0c;都不会跟你打一声招呼。时代的残酷性&#xff0c;对人如此&#xff0c;对国家也是这般。美国的历史是太嫩了&#xff0c;在1776年才得以建国。但在这年&#xff0c;美国踏上了工业化革命的节奏&#xff0c;走上了扬帆起航之旅&#xff0c;逐步成为了超级…