技术贴 | 深度解析 PostgreSQL Protocol v3.0(二)— 扩展查询

引言
PostgreSQL 使用基于消息的协议在前端(客户端)和后端(服务器)之间进行通信。该协议通过 TCP/IP 和 Unix 域套接字支持。
《深度解析 PostgreSQL Protocol v3.0》系列技术贴,将带大家深度了解 PostgreSQL Protocol v3.0(在 PostgreSQL 7.4 及更高版本中实现,有关早期协议版本的描述请参考 PostgreSQL 文档的早期版本,该系列文章不予赘述)相关的消息传输格式和格式码、消息支持的数据类型、消息的格式、协议交互流程、错误消息和通知消息、支持的子协议等,相关的代码解读基于 PostgreSQL 代码仓库的 REL_14_STABLE 分支。
本期是《深度解析 PostgreSQL Protocol v3.0》系列技术贴的第二期文章,在第一期文章中带大家解读了 PostgreSQL Protocol v3.0(一)—概述,本期将为大家分享 PostgreSQL Protocol v3.0(二) — 扩展查询功能的内容。
一、扩展查询介绍
扩展查询(Extended Query)协议将简单查询协议分解为多个步骤,为提高效率,可多次重复使用准备(Prepare)步骤的结果。此外,扩展查询协议还提供了其他功能,例如可以将数据值作为单独的参数提供,而不必将它们直接插入到查询字符串中。
在扩展查询协议中,客户端首先发送一条 Parse 消息,其中包含文本查询字符串、可选的参数占位符的数据类型信息以及目标准备语句对象的名称(目标准备语句对象名称为空字符串,则选择未命名的准备语句)。响应为 ParseComplete 或 ErrorResponse 消息。参数数据类型可以由 OID 指定;如果没有指定参数数据类型,解析器将尝试以与无类型的文本字符串常量相同的方式推断数据类型。
Parse 过程需要注意:
(1)参数的数据类型可以不指定,此时设置参数数据类型的 OID 为 0,或者使参数数据类型 OID 的数组比查询字符串中使用的参数符号的数量($n)短。另一种特殊情况是参数的数据类型可以指定为 void(即 void 伪类型的 OID)。
这意味着允许实际上是输出 OUT 使用的参数符号,作为函数的入参使用。通常情况下,没有可以使用 void 参数的上下文,但如果函数的参数列表中出现了这样的参数符号,则实际上会忽略它。例如,如果将 $3 和 $4 指定为具有 void 类型,则诸如 foo($1, $2, $3, $4) 之类的函数调用可以匹配具有两个 IN 和两个 OUT 参数的函数。
(2)Parse 消息中包含的查询字符串不能包含多个 SQL 语句,否则报告语法错误。这种限制在简单查询协议中不存在,但在扩展查询协议中确实存在,因为允许准备语句或门户包含多个 SQL 命令会使协议过度复杂化。
如果成功创建命名的准备语句对象,除非明确销毁它,否则它将持续到当前会话结束。未命名的准备语句只持续到处理下一个指定未命名语句为目标的 Parse 语句为止。
需要特别注意的是,简单查询消息 Query 也会销毁未命名的准备语句。命名的准备语句必须显式关闭,然后才能被另一个 Parse 消息重新定义,但这对于未命名语句来说不是必需的。还可以使用 PREPARE 和 EXECUTE 在 SQL 命令级别创建和访问命名的准备语句。
一旦准备语句存在,就可以使用 Bind 消息为执行做好准备。Bind 消息提供源准备语句的名称(空字符串表示未命名的准备语句)、目标门户的名称(空字符串表示未命名的门户)以及准备语句中每个参数占位符的值。提供的参数集必须与准备语句所需的参数集匹配。
如果在 Parse 消息中声明了任何 void 参数,在 Bind 消息中为它们传递 NULL 值。Bind 还指定用于查询返回数据的格式;返回数据格式可以整体指定,也可以按列指定。Bind 消息的响应为 BindComplete 或 ErrorResponse 消息。
Bind 过程需要注意:文本和二进制输出之间的选择取决于 Bind 中给出的格式代码,而不考虑所涉及的 SQL 命令。当使用扩展查询协议时,游标(CURSOR)声明中的 BINARY 属性无关紧要。
查询的计划生成通常在处理 Bind 消息时进行。如果准备语句没有参数,或者被重复执行,服务器可能会缓存创建的计划,并在同一准备语句的后续 Bind 消息中重用它。但是,只有当服务器发现可以创建的通用计划的效率比依赖于提供的特定参数值的计划高得多时,它才会这样做。但是就扩展协议而言,这个过程是透明的。
如果成功创建命名门户对象,除非明确销毁它,否则它将持续到当前事务结束。在事务结束时,或在发出指定未命名门户为目标的下一个 Bind 语句时,将销毁未命名门户。需要特别注意的,简单查询的消息 Query 也会销毁未命名的门户。在可以通过另一个 Bind 消息重新定义命名门户之前,必须显式关闭命名门户,但这对于未命名门户不是必需的。命名门户也可以使用 DECLARE CURSOR 和 FETCH 在 SQL 命令级别创建和访问。
一旦门户存在,就可以使用 Execute 消息执行它。Execute 消息指定门户名称(空字符串表示未命名的门户)和最大结果行计数(0 表示“获取所有行”)。结果行计数仅对包含返回行数据集的命令的门户有意义;在其他情况下,命令始终执行直至完成,并且忽略行计数。对 Execute 消息的可能响应与通过简单查询协议发出的 Query 消息的响应相同,但 Execute 不会导致 Server 端发出 ReadyForQuery 和 RowDescription 消息。
如果 Execute 在门户执行完成之前终止(由于达到非 0 结果行计数),服务器端将发送 PortalSuspended 消息。PortalSuspended 消息的出现告诉客户端,应针对同一门户发出另一个 Execute 消息以完成操作。在门户执行完成之前,不会发送指示源 SQL 命令执行完成的 CommandComplete 消息。因此,Execute 阶段总是由以下消息之一的出现而终止:CommandComplete、EmptyQueryResponse(如果门户是从空查询字符串创建的)、ErrorResponse 或 PortalSuspended。
在完成每个扩展查询消息系列时,客户端应发出一条 Sync 消息。如果当前事务不在 BEGIN/COMMIT 显式事务块内,则无参数 Sync 消息会导致服务器端关闭当前事务(这里的“关闭”表示如果没有错误则提交,如果错误则回滚)。然后发出 ReadyForQuery 响应。Sync 消息的目的是为错误恢复提供新的同步点。
当在处理任何扩展查询消息时检测到错误,服务器端会发出 ErrorResponse 消息,然后读取并丢弃消息,直至收到 Sync 消息,然后发出 ReadyForQuery 消息,返回正常消息处理状态。但是需要注意,如果在处理 Sync 消息时检测到错误,则不会跳过该消息处理,这确保每个 Sync 消息都有且只有一个 ReadyForQuery 响应消息发送到客户端。
另外,Sync 消息不会导致用 BEGIN 打开的事务块关闭。ReadyForQuery 消息包含事务状态信息,因此可以检测到这种情况。
除了上述必备的基本操作之外,还有几个可选操作可用于扩展查询协议:
Describe 消息(门户描述变体)指定现有门户的名称(或空字符串指示未命名门户)。响应是一条 RowDescription 消息,描述门户执行将返回的数据行;如果门户不包含将返回数据行的查询,则返回 NoData 消息;如果指定的门户不存在,则返回 ErrorResponse 消息。
Describe 消息(语句描述变体)指定现有准备语句的名称(或空字符串指示未命名准备语句)。响应是一条 ParameterDescription 消息,描述指定语句所需的参数,紧跟着是一条 RowDescription 消息,描述最终执行语句时将返回的数据行(如果该语句不返回数据行,则返回 NoData 消息)。
如果指定的准备语句不存在,则会响应 ErrorResponse 消息。需要注意的是,由于尚未收到 Bind 消息,因此服务器端还不知道用于数据返回的列格式;在这种情况下,RowDescription 消息中的格式代码字段将为 0。
在大多数情况下,客户端应该在发送 Execute 消息之前发送一个门户或语句 Describe 变体消息,以确保它知道如何解释将返回的结果。
Close 消息关闭现有的准备语句或门户,并释放资源。对不存在的语句或门户名称发出 Close 消息不会引起错误。Close 消息的响应通常是 CloseComplete,但如果在释放资源时遇到一些困难,则可能是 ErrorResponse 消息。需要注意的是,关闭准备语句会隐式关闭由该语句构建的所有打开的门户。
Flush 消息不会导致生成任何特定的输出,但会强制服务器端发送其输出缓冲区中已经存在的任何数据。如果客户端希望在发出更多命令之前检查该命令的结果,则必须在除 Sync 之外的任何扩展查询命令之后发送 Flush。如果没有 Flush 消息,服务器端返回的消息将被组合成尽可能少的数据包数量,以最大限度地减少网络开销。
因此,简单查询消息 Query 大致相当于 Parse、Bind、portal-Description、Execute、Close、Sync 系列命令,使用未命名的准备语句和门户的对象,不使用参数。
不同之处在于:
(1)简单查询的 Query 消息将接受查询字符串中的多个 SQL 语句,自动为每个语句连续执行 bind/describe/execute 命令序列;
(2)简单查询的 Query 消息的响应不会是 ParseComplete、BindComplete、CloseComplete 和 NoData 消息。
二、扩展查询支持流水线操作
扩展查询协议允许流水线(Pipelining)操作,这意味着可以发送一系列查询语句,而无需等待较早的查询完成。这减少了完成一系列给定操作所需的网络往返次数。但是,如果其中一个步骤失败,用户必须仔细考虑处理错误所需的行为,因为后续的查询已经在向服务器发送。
处理这一问题的一种方法是使整个查询系列成为一个单独的事务,即将查询序列封装在 BEGIN…COMMIT 中。然而,如果希望某些命令独立于其他命令进行单独提交,该方法并没有帮助。
扩展查询协议提供了另一种管理此问题的方法,即省略在依赖步骤之间发送同步消息。由于在发生错误后,服务器端将跳过命令消息,直至找到 Sync 消息,这允许在前一个命令失败时自动跳过流水线中的后一个命令,而客户端不必使用 BEGIN 和 COMMIT 显式管理命令序列。流水线中可独立提交的段可以通过 Sync 消息来分隔。
如果客户端没有发出显式的 BEGIN,那么如果前面的步骤成功,则每个 Sync 通常会导致事务隐式的 COMMIT,如果失败,则会引起事务隐式的 ROLLBACK。但是,有一些 DDL 命令(如 CREATE DATABASE)无法在事务块内执行。如果一个这种 DDL 命令在流水线中执行,除非它是流水线中的第一个命令,否则都将失败。
此外,这种 DDL 命令一旦执行成功,它将强制立即提交以保持数据库一致性。因此,在其中一个命令之后立即发送 Sync 消息除了使用 ReadyForQuery 进行响应之外没有任何效果。使用此方法时,必须通过 ReadyForQuery 消息计数并等待达到发送的 Sync 消息数来确定流水线的完成情况。计算命令完成响应数量是不可靠的,因为一些命令可能会被跳过,因此不会产生完成消息。
三、扩展查询可能出现的消息

四、扩展查询涉及消息的格式
以下是在扩展查询过程中可能出现的消息,下面进行详细介绍。
1.Parse(F:‘P’)
Parse 消息格式如下:

Byte1(‘P’)
将消息标识为 Parse 命令。
Int32
消息内容的字节长度,包括自身 4 个字节。
String
目标准备语句的名称(空字符串选择未命名的准备语句)。
String
要解析的查询字符串。
int16
指定的参数数据类型的数目(可以为 0)。请注意,这并不是可能出现在查询字符串中的参数数量的指示,只是前端希望为其预先指定数据类型的数量。
然后,对于每一个参数都有以下数据类型 OID:
Int32
指定参数数据类型的 OID。在此处放置 0 相当于未指定类型。
2. ParseComplete(B:‘1’)
ParseComplete 消息格式如下:

Byte1(‘1’)
将消息标识为 ParseComplete 消息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。始终为 4。
3. Bind(F:‘B’)
Bind 消息格式如下:

Byte1(‘B’)
将消息标识为 Bind 消息。
Int32
消息内容的字节长度,包括自身 4 个字节。
String
目标门户的名称(空字符串选择未命名的门户)。
String
源准备语句的名称(空字符串选择未命名的准备语句)。
Int16
后续的参数格式代码的数量(下面用C表示)。值可以是:
(1)0,表示没有参数或参数都使用默认格式(text);
(2)1,在这种情况下,指定的格式代码被应用于所有参数;
(3)值等于参数的实际数量。
Int16[C]
参数格式代码。目前数组每个元素的值必须为 0(text)或 1(binary)。
Int16
后面的参数值的数目(可能为 0)。值必须与查询所需的参数数量相匹配。
接下来,将为每个参数构建以下字段对:
Int32
参数值的长度,以字节为单位(此计数不包括其自身)。值可以为零。作为一种特殊情况,-1 表示 NULL 参数值。值为 NULL 情况下,后面没有值字节。
Byten
参数的值,格式由关联的格式代码指示。n 是上述参数值的长度。
在最后一个参数之后,将显示以下两个字段:
Int16
后面的结果列格式代码的数量(下面用 R 表示)。值可以是:
(1)0,表示没有结果列,或者结果列都应该使用默认格式(text);
(2)1,在这种情况下,指定的格式代码被应用于所有结果列(如果有结果列的话);
(3)等于查询的结果列的实际数量。
Int16[R]
结果列格式代码。目前数组每个元素的值必须为0(text)或1(binary)。
4. BindComplete(B:‘2’)
BindComplete 消息格式如下:

Byte1(‘2’)
将消息标识为 BindComplete 消息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。始终为 4。
5. Describe(F:‘D’)
Describe 消息格式如下:

Byte1(‘D’)
将消息标识为 Describe 消息。
Int32
消息内容的字节长度,包括自身 4 个字节。
Byte1
指示 Describe 描述的对象类型。可能的值为:‘S’,表示描述准备语句;‘P’,表示描述门户。
String
要描述的准备语句或门户的名称(空字符串选择未命名的准备语句或门户)。
描述门户 Portal 的消息格式如下:

描述准备语句的消息格式如下:

6. RowDescription(B:‘T’)
RowDescription 消息格式如下:

Byte1(‘T’)
将消息标识为行描述。
Int32
消息内容的字节长度,包括自身 4 个字节。
Int16
指定一行中的字段数(可以为 0)。
对于行描述中的每个字段,都有以下 7 部分内容:
String
字段名称
Int32
如果字段可以被标识为特定表的列,则值为该表的对象 ID;否则为 0。
Int16
如果该字段可以标识为特定表的列,则值为该列的属性编号;否则为 0。
Int32
字段数据类型的对象 ID。
Int16
数据类型大小(可以参考 pg_type.typlen)。需要注意的是,负值表示可变宽度类型。
Int32
类型修饰符(可以参考 pg_attribute.atttypmod)。修饰符的含义是特定于数据类型的。
Int16
字段的格式代码。目前,只能是 0(文本)或 1(二进制)。在从 Describe 语句请求返回的 RowDescription 中,格式代码还未知,并且始终为零。
蓝色背景的部分,是每个列的详细描述。浅灰色背景的部分是可选的,格式是蓝色部分的重复。
7. NoData(B:‘n’)
NoData 消息格式如下:

Byte1(‘n’)
将消息标识为 NoData 消息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。值始终为 4。
8. ParameterDescription(B:‘t’)
ParameterDescription 消息格式如下:

Byte1(‘t’)
将消息标识为 ParameterDescription 消息。
Int32
消息内容的字节长度,包括自身 4 个字节。
Int16
语句使用的参数数量(可以为 0)。
然后,对于每个参数,都有以下内容:
Int32
指定参数数据类型的对象 ID。
如果语句使用的参数数量为 0,则没有后续的参数数据类型的内容。蓝色背景的部分,是每个参数的数据类型对象 ID,是可选的。浅灰色背景的部分是可选的,格式是蓝色部分的重复。
9. Execute(F:‘E’)
Execute 消息格式如下:

Byte1(‘E’)
将消息标识为 Execute 消息。
Int32
消息内容的字节长度,包括自身 4 个字节。
String
待执行门户的名称(空字符串选择未命名的门户)。
Int32
如果门户包含返回行的查询,值为返回的最大行数;否则忽略该值。0 表示不限制返回行数。
10. DataRow(B:‘D’)
DataRow 消息格式如下:

Byte1(‘D’)
将消息标识为数据行。
Int32
消息内容的字节长度,包括自身 4 个字节。
Int16
后续列值的数量(可能为 0)。
接下来,为每列的值显示以下一对字段:
Int32
列值的长度,以字节为单位(此计数不包括长度字段自身的 4 个字节)。该字段值可以为零。作为一种特殊情况,-1 表示列值为 NULL。在列值为 NULL 的情况下,后面没有值字节的字段。
Byten
列的值,格式由关联的格式代码指示。n 是上述字段的长度值。
蓝色背景的部分,是每个列值的字节长度和列值,是可选的。浅灰色背景的部分是可选的,格式是蓝色部分的重复。
11. CommandComplete(B:‘C’)
CommandComplete 消息格式如下:

Byte1(‘C’)
将消息标识为命令完成。
Int32
消息内容的字节长度,包括自身 4 个字节。
String
命令标记。这通常是一个单词,用于标识完成了哪个 SQL 命令。
(1)对于 INSERT 命令,标记是 INSERT oid rows,其中 rows 是插入的行数。如果 rows 为 1 并且目标表具有 oid,则 oid 表示插入行的对象 ID。但 OIDs 系统列不再受支持;因此 oid 总是 0;
(2)对于 DELETE 命令,标记为 DELETE rows,其中 rows 是删除的行数;
(3)对于 UPDATE 命令,标记是 UPDATE rows,其中 rows 是更新的行数;
(4)对于 SELECT 或 CREATE TABLE AS 命令,标记是 SELECT rows,其中 rows 是检索的行数;
(5)对于 MOVE 命令,标记为 MOVE rows,其中 rows 是游标位置已更改的行数;
(6)对于 FETCH 命令,标记是 FETCH rows,其中 rows 是从游标检索的行数;
(7)对于 COPY 命令,标记是 COPY rows,其中 rows 是复制的行数。(注意:行计数仅出现在 PostgreSQL 8.2 及更高版本中。)
12. PortalSuspended(B:‘s’)
PortalSuspended 消息格式为:

Byte1(‘s’)
将消息标识为门户已挂起消息。请注意,只有当达到 Execute 消息的行计数限制时,才会出现此信息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。值始终为 4。
13. EmptyQueryResponse(B:‘I’)
EmptyQueryResponse 消息格式如下:

Byte1(‘I’)
将消息标识为对空查询字符串的响应。(EmptyQueryResponse 消息将替代 CommandComplete 消息)。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。值始终为 4。
- Close(F:‘C’)
Close 消息格式如下:

Byte1(‘C’)
将消息标识为 Close 消息。
Int32
消息内容的字节长度,包括自身 4 个字节。
Byte1
指示 Close 的对象类型。可能的值为:‘S’,表示关闭准备语句;‘P’,表示关闭门户。
String
要关闭的准备语句或门户的名称(空字符串选择未命名的准备语句或门户)。
关闭门户 Portal 的消息格式如下:

关闭准备语句的消息格式如下:

15. CloseComplete(B:‘3’)
CloseComplete 消息格式如下:

Byte1(‘3’)
将消息标识为 CloseComplete 消息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。始终为 4。
16.Flush(F:‘H’)
Flush 消息格式如下:

Byte1(‘H’)
将消息标识为 Flush 消息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。始终为 4。
17.Sync(F:‘S’)
Sync 消息格式如下:

Byte1(‘S’)
将消息标识为 Sync 消息。
Int32(4)
消息内容的字节长度,包括自身 4 个字节。值始终为 4。
18.ErrorResponse(B:‘E’)
ErrorResponse 消息格式如下:

Byte1(‘E’)
将消息类型标识为错误。
Int32
消息内容的字节长度,包括自身 4 个字节。
消息体
消息体由一个或多个标识字段组成,后跟一个’\0’字节作为终止符。字段可以按任何顺序出现。对于每个字段,都有以下内容:
(1)Byte1
标识字段类型的代码。如果为零,则这是消息终止符,后面没有字符串。由于将来可能会添加更多的字段类型,客户端应默默忽略无法识别类型的字段;
(2)String
字段值。
蓝色背景的部分,是消息体的一个字段类型及其值以及结束符’\0’。一个 ErrorResponse 消息至少包含一个消息体,浅灰色背景的部分是可选的,格式是蓝色部分的重复。消息体的最后,有一个’\0’作为消息的结束。
19.ReadyForQuery(B:‘Z’)
ReadyForQuery 消息格式如下:

Byte1(‘Z’)
将消息标识为服务器端准备好接收客户端的查询请求类型。每当服务器为新的查询周期做好准备时,就会发送 ReadyForQuery。
Int32(5)
消息内容的字节长度,包括自身 4 个字节。值总是 5。
Byte1
当前服务器端事务状态指示器。可能的值为:
(1)‘I’: 处于空闲状态(不在事务块中);
(2)‘T’: 在事务块中;
(3)‘E’: 在失败的事务块中(查询将被拒绝,直到事务块结束)。
五、扩展查询交互流程
扩展查询协议将简单查询协议分解为多个步骤,可以多次重复使用准备步骤的结果,以此提高效率。此外,扩展查询还提供了其他功能,例如可以将数据值作为单独的参数提供,而不必将其直接插入查询字符串中。
扩展查询交互流程,主要有两种场景:
(1)扩展查询的必要交互流程;
(2)扩展查询带描述准备语句的交互流程。
1. 扩展查询的必要交互流程
扩展查询必要的交互流程主要是 Parse、Bind、portal-Describe、Execute、Sync 等。扩展查询的主要流程如下:
(1)先发送包含单条 SQL 语句的 Parse 消息,创建准备语句和 Portal,方便后续步骤重复利用;
(2)通过 Bind 消息提供准备语句中参数占位符需要的参数值;
(3)通过 Describe 消息,获取 Portal 执行将要返回的数据行的信息。该步骤是可选的;
(4)发送 Execute 消息,执行当前的 Portal,并返回执行结果的数据;
(5)执行完上述流程,发送 Sync 消息提交数据;
(6)可以继续重复执行步骤 (2)~(5)的 Bind、Describe、Execute、Sync 流程,进行数据的读写。
具体的交互消息和交互流程,如下图所示:

2. 扩展查询带描述准备语句的交互流程
扩展查询带描述准备语句的交互流程主要是 Parse、准备语句 Describe、Bind、Execute、Sync 等,主要是在 Parse 和 Bind 之间,发送准备语句的描述请求消息。主要流程如下:
(1) 先发送包含单条 SQL 语句的 Parse 消息,创建准备语句和 Portal,方便后续步骤重复利用;
(2)发送准备语句 Describe,服务器端响应 ParameterDescription 消息和 RowDescription 消息。ParameterDescription 消息描述准备语句需要的参数信息,RowDescription 消息描述返回数据行的列布局信息;
(3)通过 Bind 消息提供准备语句中参数占位符需要的参数值;
(4)通过 Describe 消息,获取 Portal 执行将要返回的数据行的信息。该步骤是可选的;
(5)发送 Execute 消息,执行当前的 Portal,并返回执行结果的数据;
(6)执行完上述流程,发送 Sync 消息提交数据;
(7)可以继续重复执行步骤(3)~(6)的 Bind、portal-Describe、Execute、Sync 流程,进行数据的读写。
具体的交互消息和交互流程,如下图所示:

END
关于技术流系列博客
开物成务,厚积薄发。技术流系列博客-以“短小精悍”的形式普及数据库硬核技术。相信大家的每一次阅读,都会距离数据库内核更近一步。每一份来自你们的关注,都是我们坚持输出的满满能量!
相关文章:
技术贴 | 深度解析 PostgreSQL Protocol v3.0(二)— 扩展查询
引言 PostgreSQL 使用基于消息的协议在前端(客户端)和后端(服务器)之间进行通信。该协议通过 TCP/IP 和 Unix 域套接字支持。 《深度解析 PostgreSQL Protocol v3.0》系列技术贴,将带大家深度了解 PostgreSQL Protoc…...
HDFS编程实践-从HDFS中下载指定文件到本地
前言:Hadoop采用java语言开发,提供了Java Api与HDFS进行交互 先要把hadoop的jar包导入到idea中去 为了能编写一个与hdfs交互的java应用程序,一般需要向java工程中添加以下jar包 1)/usr/local/hadoop/share/hadoop/common目录下…...
安防监控视频AI智能分析网关:人流量统计算法的应用场景汇总
TSINGSEE青犀人流量检测算法是内置在智能分析网关中的一种能够通过AI分析和计算人群数量以及密度的算法技术,在提升城市管理效率、改善用户体验和增加安全性方面发挥着重要作用。人流量检测算法在许多领域都有广泛的应用,如智慧城市、智慧交通、智慧景区…...
第一百五十二回 自定义组件综合实例:游戏摇杆三
文章目录 内容回顾优化性能示例代码我们在上一章回中介绍了 如何实现游戏摇杆相关的内容,本章回中将继续介绍这方面的知识.闲话休提,让我们一起Talk Flutter吧。 内容回顾 我们在前面章回中介绍了游戏摇杆的概念以及实现方法,并且通过示例代码演示了实现游戏摇杆的整个过程…...
多线程的学习中篇上
终其一生,满是遗憾 知足且坚定,温柔且上进 总之岁月漫长,然而值得等待 获取当前线程引用 方法说明public static Thread currentThread();返回当前线程对象的引用 currentThread() > 在那个线程中, 就能获取到那个线程的实例. static关键…...
非标准化套利
交易对象:目前使用非标准化组合进行交易。(即黄金远近月,焦煤焦炭等等) 交易平台:易盛极星极星产品网 手续费研究:白糖期货手续费和保证金2023年09月更新 - 九期网 本人使用的期货交易公司:中信期货&…...
从CNN(卷积神经网络),又名CAM获取热图
一、说明 卷积神经网络(CNN)令人难以置信。如果你想知道它如何看待世界(图像),有一种方法是可视化它。 这个想法是,我们从最后的密集层中得到权重,然后乘以最终的CNN层。这需要全局平均…...
kafka消费者多线程开发
目录 前言 kafka consumer 设计原理 多线程的方案 参考资料 前言 目前,计算机的硬件条件已经大大改善,即使是在普通的笔记本电脑上,多核都已经是标配了,更不用说专业的服务器了。如果跑在强劲服务器机器上的应用程序依然是单…...
布局设计和实现:计算器UI【TableLayout、GridLayout】
一、使用TableLayout实现计算器UI 1.新建一个空白项目布局 根据自己的需求输入其他信息 填写完成后,点击Finish即可 2. 设计UI界面 在res/layout文件夹中的XML文件中创建UI界面。在这个XML文件中,您可以使用TableLayout来设计计算器界面。 2.1 创建l…...
stack与queue的简单封装
前言: stack与queue即栈和队列,先进后出/先进先出的特性我们早已了然于心, 在学习数据结构时,我们利用c语言实现栈与队列,从结构体写起,利用数组或指针表示他们的数据成员,之后再一个个实现他们…...
ChatGPT使用技巧整理
目录 1. 让ChatGPT扮演专家角色2. 告诉ChatGPT你的身份3. 限制ChatGPT的回答长度4. 让ChatGPT一步步思考5. 明确你的要求和目的6. 提供充分的背景信息7. 始终结构化思考你的prompt1. 让ChatGPT扮演专家角色 当你们讨论的是市场营销问题时,你可以要求ChatGPT扮演一个具有20年从…...
机器学习笔记 - 维度诅咒的数学表达
1、点之间的距离 kNN分类器假设相似的点也可能有相同的标签。但是,在高维空间中,从概率分布中得出的点往往不会始终靠近在一起。 我们可以用一个简单的例子来说明这一点。 我们将在单位立方体内均匀地随机绘制点(如图所示),并研究该立方体内测试点的 k 个最近邻将占用多少…...
组合计数训练题解
CF40E 题目链接 点击打开链接 题目解法 首先,如果 n , m n,m n,m 一奇一偶,那么答案为 0 0 0 原因是从行和列的角度分析, − 1 -1 −1 个数的奇偶性不同 可以发现 k < max { n , m } k<\max\{n,m\} k<max{n,m} 的性质很微…...
P1095 [NOIP2007 普及组] 守望者的逃离
[NOIP2007 普及组] 守望者的逃离 - 洛谷 首先DP的套路就是先找状态 这题也找不出其他的状态了,只有时间一个 所以用f[i]表示时刻i能走多远 而仔细一想实际上决策只有跑、闪现、停三种决策 然而闪现的耗蓝要和跑步一同计算十分麻烦 于是把它们分开算࿱…...
Python函数绘图与高等代数互融实例(八):箱线图|误差棒图|堆积图
Python函数绘图与高等代数互融实例(一):正弦函数与余弦函数 Python函数绘图与高等代数互融实例(二):闪点函数 Python函数绘图与高等代数互融实例(三):设置X|Y轴|网格线 Python函数绘图与高等代数互融实例(四):设置X|Y轴参考线|参考区域 Python函数绘图与高等代数互融实例(五…...
联想y7000 y7000p 2018/2019 不插电源 不插充电器, 直接关机 ,电量一直89%/87%/86%,V0005如何解决?
这种问题,没有外力破坏的话,电池不可能突然出事。这种一般是联想的固件问题,有可能发生在系统更新,或者突然的不正常关机或长时间电池过热,原因我不是很清楚。 既然发生了,根据我收集的解决方法,…...
stm32与esp8266通信
esp8266 #include <ESP8266WiFi.h> #include <ESP8266HTTPClient.h>// 测试HTTP请求用的URL // #define URL "http://162.14.107.118:8086/PC/modifyFoodPrice/0/6"// 测试HTTP请求用的URL // 设置wifi接入信息(请根据您的WiFi信息进行修改) const char…...
组合数 2.1 2.2
O(nlogn)预处理, O(1)查询 #include<bits/stdc.h> #define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define endl \nusing namespace std;typedef pair<int, int> PII; typedef long long ll; typedef long double ld;const int N 1000…...
【数组的中心位置】python实现-附ChatGPT解析
1.题目 数组的中心位置 题目 给你一个整数数组 nums,请计算数组的中心位置。 数组中心位置是数组的一个下标,其左侧所有元素相乘的积等于右侧所有元素相乘的积。 数组第一个元素的左侧积为 1,最后一个元素的右侧积为 1。 如果数组有多个中心位置,应该返回最靠近左边的那一个…...
黑马JVM总结(二十三)
(1)字节码指令-init 方法体内有一些字节,对应着将来要由java虚拟机执行方法内的代码,构造方法里5个字节代码,main方法里有9个字节的代码 java虚拟机呢内部有一个解释器,这个解释器呢可以识别平台无关的字…...
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上,开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识,在 vs 2017 平台上,进行 ASP.NET 应用程序和简易网站的开发;初步熟悉开发一…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
淘宝扭蛋机小程序系统开发:打造互动性强的购物平台
淘宝扭蛋机小程序系统的开发,旨在打造一个互动性强的购物平台,让用户在购物的同时,能够享受到更多的乐趣和惊喜。 淘宝扭蛋机小程序系统拥有丰富的互动功能。用户可以通过虚拟摇杆操作扭蛋机,实现旋转、抽拉等动作,增…...
解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散
前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说,在叠衣服的过程中,我会带着团队对比各种模型、方法、策略,毕竟针对各个场景始终寻找更优的解决方案,是我个人和我司「七月在线」的职责之一 且个人认为,…...
macOS 终端智能代理检测
🧠 终端智能代理检测:自动判断是否需要设置代理访问 GitHub 在开发中,使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新,例如: fatal: unable to access https://github.com/ohmyzsh/oh…...
篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
