【MySQL事务】深刻理解事务隔离以及MVCC

news2024/10/7 16:22:36

文章目录

  • 什么叫事务
  • 事务的提交方式
  • 常见的事务操作方式
    • 事务的开始与回滚
    • 总结
  • 事务的隔离
    • 设置隔离级别
    • 解释脏读
    • 解释幻读
    • 解释不可重复读
    • 为什么可重复读不能解决幻读问题?
    • 总结
  • 数据库并发的场景
    • MVCC
      • 隐藏列字段
      • undo日志
      • Read view
    • RR和RC的本质区别
      • 总结

什么叫事务

在MySQL中,事务(Transaction)是一组为达成某种目的的sql操作。事务可以是单条sql语句,也可以是多条语句的集合。例如银行的一次转账,主要包含两个sql操作:一是某某用户的资产减少,二是另一个用户的资产增加。将这两条sql语句打包起来就可以称之为一个简单的转账事务。对于用户而言,他并不需要关注事务的过程,他只关心这一件事务有没有完成。即使在底层事务有执行过程,但是在概念上我们认为事务就是原子性的,即忽略中间的执行过程

事务除了是sql操作的集合之外,还满足以下四个属性:

  • 原子性(Atomicity):一个事务中的所有操作必须要么全部完成,要么全部不完成,不会结束在中间的某个环节。即使在事务的执行过程中发生了某种错误,mysql也会回滚到事务开始前的状态。
  • 一致性(Consistency):一致性确保事务将数据库从一个一致状态转变为另一个一致状态。在事务开始和完成时,数据库必须遵守所有定义的规则,包括数据的完整性约束。
  • 隔离性( I solation):数据库允许多个并发事务同时对同一数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于sql交叉执行而导致数据不一致。具体来说,事务隔离又分为不同级别,包括:读未提交Read uncommitted)、读提交read committed )、可重复读 repeatable read )和串行化Serializable )。不同的隔离级别会影响并发控制的严格程度,隔离级别越低越危险,效率越高,隔离级别越高越安全,效率越低。不同的场景需要选择合适的隔离级别。mysql默认的隔离级别是可重复读
  • 持久性(Durability):事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

上面四个属性可以简称为AICD
值得注意的是,在MySQL中并不是所有存储引擎都支持事务,比如MyISAM就不支持,而Innodb是支持的。 这一点可以在mysql中使用sqlshow engines;查看
在这里插入图片描述

事务的提交方式

事务的提交方式常见的有两种:

  • 自动提交
  • 手动提交

可以使用sql查看事务提交方式如:
在这里插入图片描述
ON表示自动提交,OFF表示关闭自动提交,默认是ON
可以使用set来改变mysql的自动提交模式,0表示关闭,1表示打开

在这里插入图片描述

常见的事务操作方式

下面用具体的sql样例来展示常见的事务操作,为了方便观察,以下事务操作是在读未提交的隔离级别下进行的,关于修改数据库的隔离级别操作后面会讲,目前不用在意。

现创建测试表

create table if not exists account(
   id int primary key, 
   name varchar(50) not null default '', 
   blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

上述sql初建了一个简单的银行用户表

事务的开始与回滚

  • begin表示开启一个事务
  • commit表示提交事务
  • savepoint 设置保存点,将来可以回滚到保存点之前
  • rollback 回滚操作,可以回滚到某一个保存点,如果一个事务被提交了则不能回退

在这里插入图片描述
在这里插入图片描述
我们可以看到在一个事务中,使用rollback可以将当前状态回滚到某一个保存点之前
如果直接在事务中使用rollback则表示回滚到事务的最开始,例如:

  • 值得注意的是,如果我们开启一个事物但是没有commit提交,发生异常中断后,MySQL会自动回滚
    在这里插入图片描述
    一旦我们conmmit提交了事务,之后发生中断mysql崩溃,数据也不会受到影响,此时数据已经持久化保存在数据库表文件中,这体现了事务的数据持久性。

  • begin操作会自动更改提交方式,不会受MySQL是否自动提交影响
    一旦开启事务,就必须要使用commit提交,即使我们设置了自动提交,在遇到异常时,一样会回滚。这一点在上面的实验中已经可以证明。

  • 自动提交只会影响单sql事务
    实验一:终端A关闭自动提交,执行一条单sql事务后发生中断,观察现象
    在这里插入图片描述

实验二:终端A打开自动提交,执行一条单sql事务后发生中断,观察其现象

在这里插入图片描述

总结

  • 只要使用begin或者start transaction开启事务,就必须要通过commit提交,否则即使设置了autocommit也不会自动提交。
  • 事务可以手动回滚,同时,当操作异常时,MySQL会自动回滚
  • 对于InnoDB每一条SQL语句都默认封装成一个事务,自动提交。(除select外)

事务的隔离

既然MySQL支持并发访问,那多个事务一起访问同一个数据库中同一张表的场景就会经常出现。不同事物之间可能会有sql交叉执行的情况。 例如一个事物执行updata另一个事务执行select,应该先执行谁呢? 为了避免多个事务相互影响,MySQL提供了一系列方案来维持事物并发时数据的一致性,这就叫做事务的隔离。当然,事务的隔离并不是完全将两个事务隔离开互不干扰,根据不同的场景,我们需要选择不同的隔离程度。隔离程度的不同也决定了隔离级别的不同。下面我们将谈谈事物的隔离级别有哪些:

  1. 读未提交(Read Uncommitted):该隔离级别下的事务可以看到其他事务没有提交的执行结果,相当于没有隔离,会产生许多并发问题,如脏读,幻读,不可重复读等(后面会讲解什么是脏读等)。
  2. 读提交(Read Committed):该隔离级别下,一个正在执行的事务能看到另一个已经提交的事务的执行结果,这种隔离级别不会导致脏读,但是不可重复读以及幻读
  3. 可重复读(Repeatable Read):MySQL默认的隔离级别。该隔离级别的事务看到的数据是一致的,不会受到其他事务的影响,即使其他事物提交了。InnoDB存储引擎默认使用这个隔离级别。注意该隔离级别依旧存在幻读问题。
  4. 串行化(Serializable):事物的最高隔离级别。通过强制对事物进行排序,使得每一个事物的开始必然是在另一个事物结束之后,也就不会有冲突问题,从而解决了幻读问题。它在每行数据上面加上共享锁。可能这种隔离级别的效率太低了。

下面解释什么是脏读、幻读、不可重复读。在此之前先介绍如何在MySQL中设置隔离级别。

设置隔离级别

  • 查看全局的隔离级别
    可以使用sqlselect transaction_isolation;(mysql8.0以前版本是tx_isolation
    在这里插入图片描述
  • 查看当前会话的隔离级
    使用sql select @@session.transaction_isolation;
    在这里插入图片描述

全局隔离级别是对整个MySQL服务器实例的默认设置,影响所有新创建的会话(连接)。当你设置全局隔离级别时,任何新的连接都会使用这个隔离级别,除非在会话中另行指定。当前会话隔离级别只影响当前连接的事务隔离级别,当你设置会话隔离级别时,该设置仅对当前连接有效,不会影响其他会话。会话隔离级别的优先级要高于全局隔离级别

  • 设置全局隔离级别
    以设置为RR级别(重复读)为例,使用sql set global transaction isolation level repeatable read;

在这里插入图片描述
值得注意的是,如果我们只修改全局隔离级别,那么需要重启更新会话隔离级别

  • 设置会话隔离级别
    set session transaction isolation level repeatable read;
    在这里插入图片描述

解释脏读

脏读是指一个事务能读取到另一个事务尚未提交的修改数据

例如:
事务A开始并更新了一行数据,但是未提交
事务B读取了事务A未提交的这行数据
事务A回滚了此次修改数据
此时事务B读到的就是无效数据
在这里插入图片描述
解决方法:将隔离级别设置为RC(读提交),或者更高。

解释幻读

幻读是指在一个事务中,两次查询到的数据结果集不一致,因为其他事务在两次查询之间插入或者删除了数据。注意幻读与脏读的区别,脏读主要是指读取到未提交的事务,且被回滚导致另一事务读取到的数据无效,它的问题在于数据不可靠。而幻读的问题在于查询结果不一致,结果集中出现新的行或消失的行,导致数据一致性问题。主要在RR及以下的隔离级别发生。

解释不可重复读

不可重复读是指在一个事务中,连续读取同一行数据时,因为其他事务的提交而导致结果不一致

例子:
事务A读取了一行数据。
事务B更新了这行数据并提交。
事务A再次读取同一行数据,发现数据已经发生了变化。

为什么可重复读不能解决幻读问题?

隔离性的实现是通过对数据加锁来实现的,而insert待插入的数据本来是不存在的,那么一般加锁无法屏蔽这类问题。MySQL在RR级别解决了幻读问题(其它数据库不一定),解决的方式是通过用next-key间隙锁(GAP+行锁)。

间隙锁(next-key locking):间隙锁是一种锁定策略,不仅锁定表中的行,也锁定行之间的间隙,防止其他事务在间隙中插入新的行,从而避免幻读。

间隙(GAP): 当我们使用范围查询时,InnoDB首先会给复合条件的行加锁,但是对于不在查询范围的行,我们就称为间隙。

例如:
假设有一张employees,其结构如下:

CREATE TABLE employees (
    id INT PRIMARY KEY,
    name VARCHAR(100)
);

初始表数据如下:

idname
1Alice
3Bob
5Charlie

事务A执行以下查询:

START TRANSACTION;
SELECT * FROM employees WHERE id >= 3 AND id <= 5;

在这个查询中,InnoDB会使用间隙锁来锁定id为3和5的行,同时锁定以下间隙:

(-, 1)
(1, 3)
(3, 5)
(5, +)

所以当事务A未提交,但是事务B想在间隙中插入数据,由于间隙锁的存在,事务B就会被阻塞,直到事务A提交或者回滚。

总结

  • 隔离级别越高,安全性越高,并发性能越低
  • 不可重复读的重点在于修改,幻读的重点在于新增和删除
  • mysql的默认隔离级别是RR
    在这里插入图片描述

数据库并发的场景

数据库并发的场景有三种:

  • 读-读:不存在任何问题,也不需要并发控制
  • 读-写: 有线程安全问题,可能会造成事务隔离性问题如不可重复读、脏读、幻读等问题
  • 写-写:有线程安全问题,可能会导致数据更新丢失

下面主要讲读写并发。

MVCC

多版本并发控制Multi-Version Concurrency Control,MVCC)是一种用于提高数据库并发性能的机制。具体来说,MVCC机制通过维护数据的多个版本来实现,这些版本包含了不同事务在不同时间点的修改,为了区分不同事务,MVCC为事务提供了单向增长的事务ID。事务ID与版本相关联,读操作只读该事务开始前的数据库的快照。总的来说,MVCC机制规范了事务应该读到数据版本的范围,这也叫做事务的可见性。于是,通过控制事务的可见性范围,MVCC也就实现了无锁并发控制

  • 快照:快照是是数据库某一时刻的数据副本,记录了创建快照时数据库的状态。
  • 事务的可见性:当一个事务读取数据时,MVCC会根据事务ID的大小来决定哪个版本对该事务可见。(事务ID通常就是时间戳

综上,MVCC可以为数据库解决以下问题:

  • 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能。

    • 读取操作:事务在读取数据时,MVCC会查看数据的所有版本,并选择在事务开始之前以及存在和事务期间没有被删除的数据版本
    • 写操作:写操作不会覆盖原有数据,而是创建一个新的版本,新版本的事务ID就是当前事务ID.旧版本依旧保留以供其它事务读取。
      为什么这样基于版本迭代的读写操作不会相互阻塞呢?本质还是因为写操作不会覆盖原版本数据,而是重新生成一个版本,旧版本还在。
  • 同时可以解决脏读、幻读、不可重复读等事务隔离问题,但不能解决更新丢失问题。

隐藏列字段

在MySQL的多版本并发控制(MVCC)中,每个行记录都会包含三个隐藏字段,以支持事务和并发控制。具体来说,这三个隐藏字段是:

  • DB_TRX_ID:6 byte,最近修改(修改或者插入)事务ID。
  • DB_ROLL_PTR :7 byte,回滚指针,指向这条记录的上一个版本(形成版本链,每个节点都对应一个版本),该指针用于支持回滚操作和一致性读。通过这个指针MVCC能够找到该行的历史版本数据。
  • DB_ROW_ID:6 byte,隐含的自增ID(隐藏主键),如果表没有指定主键,InnoDB会自动以DB_ROW_ID为键值产生一个聚簇索引
  • 补充: flag字段用于存储行的内部状态或属性,比如当行被删除时,该标记就会记录该行被删除了。

比如执行下面sql:

insert into student (name, age) values ('张三', 28);

新插入的这一行数据还会包含隐藏字段,展开就是(不考虑flag):
在这里插入图片描述
目前并不知道创建该记录的事务ID,隐式主键,我们就默认设置成null,1。第一条记录也没有其他版本,我们设置回滚指针为null。

undo日志

Undo 日志记录了事务在修改数据之前的数据状态,这样在事务回滚时,可以将数据恢复到原来的状态。这也是事务能回滚的关键原因。并且,为了实现读写并发,MySQL 使用 Undo 日志来保存数据的旧版本,从而实现快照读

下面基于版本链给出相关的两个概念

  • 快照读
    快照读其实就是MVCC的一种读模式,快照读查询到的数据是事务开始时的数据,而不是最新版本的数据。这样就能保证不受其它事务修改的影响。 简单来说,快照读读取的是历史版本。
  • 当前读:当前读读取的是最新记录

那么如何确定当前事务是快照读还是当前读呢?这就是靠事务的隔离级别来决定了。如果是隔离级别为RU(读未提交)就是当前读,如果是RR(重复读)就是快照读。

假设现在给一个表中插入诺干条数据,其undo日志记录的版本链大致如下:
在这里插入图片描述

Read view

Read view就是快照读操作产生的一个读视图(也称为快照),本质就是生成一个类对象,该对象包含了快照读时的数据库状态,本质是为进行可见性判断服务的机制。即通过对比读试图中的属性,我们能知道当前事务能看到数据版本的范围。更为详细的,ReadView类包含的信息可以通过以下类结构观察:

class ReadView {
  // 省略...
 private:
  /** 高水位,大于等于这个ID的事务均不可见*/
  trx_id_t m_low_limit_id;
  /** 低水位:小于这个ID的事务均可见 */
  trx_id_t m_up_limit_id;
  
  /** 创建该 Read View 的事务ID*/
  trx_id_t m_creator_trx_id;
  
  /** 创建视图时的活跃事务id列表*/
  ids_t m_ids;
  
  /** 配合purge,标识该视图不需要小于m_low_limit_no的UNDO LOG,
   * 如果其他视图也不需要,则可以删除小于m_low_limit_no的UNDO LOG*/
  trx_id_t m_low_limit_no;
  
  /** 标记视图是否被关闭*/
  bool m_closed;
  // 省略...
};

从以上代码提取中重要的几个字段:

  • m_ids: 当前活跃的事务ID列表。生成ReadView时,系统中所有正在运行的事务ID都会被记录在这个列表中。
  • m_up_limit_id:生成ReadView时刻,当前活跃事务中最小的事务ID。所有事务ID小于该值的事务都已经结束了
  • m_low_limit_id:生成ReadView时刻,下一个还未分配的事务ID,也就是当前所有出现过的事务的最大事务ID+1。
  • m_creator_trx_id :创建该ReadView的事务ID

有了读视图,我们就能确定版本链中哪些版本可以被该事务读到。具体的比较函数实现如下:
在这里插入图片描述
将上述代码拆分成三个部分:

  • 第一部分
    在这里插入图片描述
    这个代码片段的意思是,如果当前版本事务ID小于m_up_limit_id表示该版本在当前所有活跃事务开始之前生成,因此该版本是可见的。或者当前版本事务ID等于m_creactor_trx_id,表示该版本就是当前事务生成的,因此也是可见的。

  • 第二部分
    在这里插入图片描述
    改代码片段的意思是,如果当前版本事务ID大于m_low_limit_id表示这个版本是在生成read view之后开始的事务生成的版本,因此是不可见的。之后再判断m_ids是否为空,为空表示当前事务开始时没有其他事务正在运行,因此是可见的。

  • 第三部分
    在这里插入图片描述

该代码表示,在m_ids列表中去找id,如果找到了,说明产生该版本的事务在活跃列表中,且还未结束,所以不可见。如果没找到,说明生成该版本的事务原来在活跃列表中,但是当前结束了,所以是可见的

于是通过遍历版本链中的版本,选择可见的数据版本,并依次遍历。这个筛选动作在select的时候就会自动执行。

RR和RC的本质区别

现在给出一张表,表结构如下:

create table if not exists account(
   id int primary key, 
   name varchar(50) not null default '', 
   blance decimal(10,2) not null default 0.0
)ENGINE=InnoDB DEFAULT CHARSET=UTF8;

操作流程如下(RR模式下):

  • 测试一
    在这里插入图片描述
    事务A和事务B同时开启,两者几乎同时select,生成readview(读视图)。之后事务A修改表中数据并马上提交,此时事务B并没有读到事务A的修改结果,使用共享锁后才能读到(通过加共享锁可以强行变成当前读,此时读到的是最新数据)。这样的结果符合我们的预期,因为事务B生成的readview对象中m_ids包含了事务A的id,于是事务A生成的版本就不可见了。同时也符合RR隔离级别的

  • 测试二
    在这里插入图片描述
    测试一和测试二的区别在于,事务B的select操作在事务A提交之后,这样一来,当事务B生成read view对象是, 事务A生成数据的版本ID都小于事务B的m_up_limit_id,表示生成该版本的事务已经结束了,于是B可以看见A的修改结果

总结

综上,我们可以从readview的角度来思考RR和RC级别的不同之处

  • 在RC级别下,每次快照读都会生成read view,也意味着该隔离级别下事务的可见性在变化,这也能解释为什么一个事务提交前另一个事务看不见,提交后就能看见了。
  • 在RR级别下,只会在首次快照读的地方生成read view,此后不会在生成新的。这也意味着可见性是固定的,这也能解释为什么该隔离级别可以解决不可重复读等问题。

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

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

相关文章

游戏AI的创造思路-技术基础-sigmoid函数详解

在前面的机器学习和深度学习的内容中&#xff0c;大量出现了sigmoid函数&#xff0c;所以本篇为大家介绍下sigmoid函数&#xff0c;希望对大家理解前面的算法和后面的Transformer有所帮助 目录 3.8. sigmoid函数 3.8.1. 定义 3.8.2. 性质 3.8.3. 应用 3.8.4. 缺点 3.8.5.…

sd卡一插上就提示格式化是怎么回事?sd卡数据如何恢复?

sd卡一插上就提示格式化是怎么回事&#xff1f;里面的数据怎么办&#xff1f;下面小编总结了SD卡提示格式化的原因和对应解决办法分享给大家&#xff01; SD卡好好的&#xff0c;为什么一插电脑上就提示需要格式化呢&#xff1f;当SD卡提示格式化时&#xff0c;可以考虑下面几类…

VS Code 配置cmake(Linux环境)

通过sudo apt install cmake在linux上安装cmake 在Vs Code中安装这两个插件 通过命令whereis cmake获取linux中cmake的路径信息 右键CMake Tools右下角齿轮标志&#xff0c;选择扩展设置&#xff08;Extension Settings&#xff09; 注意要设置的是本地&#xff0c;还是远程连接…

【UE5.3】笔记3-静态网格体,BSP

静态网格体组件 主要有两个属性 一个是静态网格体&#xff1a;对应的也就是模型&#xff0c;比如fbx&#xff0c;maya&#xff0c;obj等格式 一个是材质&#xff1a;由各种贴图、渲染设置等&#xff0c;比如unity里的shader BSP画刷&#xff1a; 打开放置Actor选项卡&#…

SpringAOP执行流程——从源码画流程图

文章目录 了解ProxyFactory入门操作添加多个Advice的执行顺序关于异常Advice关于proceed()方法指定方法才进行增强逻辑 创建代理对象的其他方式ProxyFactoryBeanBeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreator 对SpringAOP的理解TargetSourceProxyFactory选择JDK/CJL…

我对AI赋能的未来畅想

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

C# 入门—实现 Hello, World!

目录 一、.net 平台 二、.net 都能干什么&#xff1f; 三、.net 两种交互模式 四、使用 VS Code 开发 C# 程序 五、实现 Hello, World! 一、.net 平台 下载 .NET(Linux、macOS 和 Windows) (microsoft.com) .NET 简介 - .NET | Microsoft Learn C# :一种编程语言,可以开…

python-docx 设置页面边距、页眉页脚高度

本文目录 前言一、docx 页面边距在哪里二、对 <w:pgMar> 的详细说明1、上边距的说明2、右边距的说明3、下边距的说明4、左边距的说明5、页眉高度的说明6、页脚高度的说明三、设置 docx 页边距、页眉页脚高度1、完整代码2、代码执行效果图四、补充一些内容1、页面边距的两…

jenkins中执行docker命令

1. 修改docker.sock文件的所属组 命令如下&#xff1a; sudo chown root:root docker.sock 2. 对这个文件赋予权限&#xff0c;供其他用户使用&#xff0c;给定权限命令如下&#xff1a; sudo chmod orw docker.sock 3. docker容器映射 这里需要两个文件&#xff1a; 一个…

PS教程29

图层蒙版 以案例来解释蒙版的作用 将这两张图片原框背景切换将图二的背景选中使用套索工具选中区域切换图一CtrlA全选CtrlC复制编辑-选择性粘贴-贴入即可贴入如果位置不对用移动工具进行调整 这就是图层蒙版 图层蒙版本质作用&#xff1a;是临时通道&#xff0c;支持黑白灰三种…

火车头采集器Discuz采集发布模块插件

火车头采集器怎么采集发布数据到Discuz系统的论坛帖子或门户文章&#xff1f; 可按照以下步骤配置&#xff1a; 1. 火车头采集器Discuz采集发布插件下载安装&#xff1a; 火车头采集器Discuz发布模块插件下载地址-CSDN 2. 在火车头采集器工具导入Discuz采集发布模块插件&am…

Java 8 新特性:Lambda表达式让你的代码焕然一新——掌握它,让编程变得轻松又高效!

前言 Java 8 是 Java 发展史上的一次重要里程碑。作为企业级开发语言&#xff0c;它在性能和功能上做了巨大的提升。这其中&#xff0c;Lambda表达式是一个关键的新特性&#xff0c;它为 Java 语言带来了函数式编程的概念。本篇文章将深入探讨Lambda表达式&#xff0c;并结合热…

【Linux】解锁并发:多线程同步技术详解与应用实践

文章目录 前言&#xff1a;1. 同步概念2. 条件变量&#xff1a;实现线程间同步的&#xff01;2.1. 条件变量是什么&#xff1f;2.2. 认识条件变量接口 3. 写一个测试代码——验证线程的同步机制4. 生产消费模型5. 生产消费模型 条件变量6. 线程池7. 可重入 VS 线程安全7.1. 概…

使用Python进行大数据处理Dask与Apache Spark的对比

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Python进行大数据处理Dask与Apache Spark的对比 随着数据量的增加和数据处理需求的增长…

基于IM948(Low-cost IMU+蓝牙)模块的高精度PDR(Pedestrian Dead Reckoning)定位系统 — 可以提供模块和配套代码

一、背景与意义 行人PDR定位系统中的PDR&#xff08;Pedestrian Dead Reckoning&#xff0c;即行人航位推算&#xff09;背景意义在于其提供了一种在GPS信号不可用或不可靠的环境下&#xff0c;对行人进行精确定位和导航的解决方案。以下是关于PDR背景意义的详细描述&#xff1…

Go语言学习:每日一练1

Go语言学习&#xff1a;每日一练1 目录 Go语言学习&#xff1a;每日一练1变量声明函数定义流程控制 ifrange遍历switch 变量声明 package main//定义变量 var a 1 const Message “hello,world”func main() {b : 2 //短变量声明var c 3c TestMethod(a, b, c)} //定义函数…

【UE5.3】笔记2--资源导入

资源导入 方式一&#xff1a;内置资源--初学者内容包 方式二&#xff1a;虚幻商城 搜索免费资源&#xff1a; 添加到工程之后 搜素&#xff1a;虚幻学习工具包&#xff0c;需要注意的是支持的引擎版本 当然商城里包含了大量的免费的资源&#xff0c;初期学习不想投入太多可以…

node 实现导出, 在导出excel中包含图片(附件)

如果想查看 node mySql 实现数据的导入导出&#xff0c;以及导入批量插入的sql语句&#xff0c;连接如下 node mySql 实现数据的导入导出&#xff0c;以及导入批量插入的sql语句-CSDN博客https://blog.csdn.net/snows_l/article/details/139998373 一、效果如图&#xff1a; 二…

设计师进阶指南:掌握这6条版式设计要点

布局设计是设计师的必修课。优秀的排版不是强制性的“东拼西凑”&#xff0c;而是通过设计师独特的排版获得的。这不是简单的信息列表&#xff0c;而是认真思考如何分层、有节奏地组织和安排元素。今天我将给你带来它 6 文章还附带了布局设计模板资源&#xff0c;设计师朋友一定…

第三十三篇——互联网广告:为什么Google搜索的广告效果好?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 对于信息的利用&#xff0c;再广告这个维度中去洞察&#xff0c;你又能发…