PostgreSQL的学习心得和知识总结(一百四十六)|深入理解PostgreSQL数据库之客户端侧auto savepoint的使用和实现

news2025/1/20 5:51:37

注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

1、参考书籍:《PostgreSQL数据库内核分析》
2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
3、PostgreSQL数据库仓库链接,点击前往
4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
5、参考书籍:《PostgreSQL中文手册》
6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往


1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
2、本文目的:开源共享 抛砖引玉 一起学习
3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
5、本文内容基于PostgreSQL master源码开发而成


深入理解PostgreSQL数据库之客户端侧auto savepoint的使用和实现

  • 文章快速说明索引
  • 功能使用背景说明
  • 功能实现源码解析
    • pgJDBC autosave属性
    • psql ON_ERROR_ROLLBACK



文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


学习内容:(详见目录)

1、深入理解PostgreSQL数据库之客户端侧auto savepoint的使用和实现


学习时间:

2024年06月20日 22:41:20


学习产出:

1、PostgreSQL数据库基础知识回顾 1个
2、CSDN 技术博客 1篇
3、PostgreSQL数据库内核深入学习


注:下面我们所有的学习环境是Centos8+PostgreSQL master+Oracle19C+MySQL8.0

postgres=# select version();
                                                  version                                                   
------------------------------------------------------------------------------------------------------------
 PostgreSQL 17devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
(1 row)

postgres=#

#-----------------------------------------------------------------------------#

SQL> select * from v$version;          

BANNER        Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
BANNER_FULL	  Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0	
BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
CON_ID 0


#-----------------------------------------------------------------------------#

mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.06 sec)

mysql>

功能使用背景说明

我们先看一个例子,示例一 如下:

postgres=# select version();
                                                  version                                                   
------------------------------------------------------------------------------------------------------------
 PostgreSQL 17beta1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
(1 row)

postgres=#
postgres=# create table t1 (id int primary key);
CREATE TABLE
postgres=# begin;
BEGIN
postgres=*# insert into t1 values(1);
INSERT 0 1
postgres=*# savepoint somename;
SAVEPOINT
postgres=*# insert into t1 values(1);
2024-06-20 07:49:46.746 PDT [180224] ERROR:  duplicate key value violates unique constraint "t1_pkey"
2024-06-20 07:49:46.746 PDT [180224] DETAIL:  Key (id)=(1) already exists.
2024-06-20 07:49:46.746 PDT [180224] STATEMENT:  insert into t1 values(1);
ERROR:  duplicate key value violates unique constraint "t1_pkey"
DETAIL:  Key (id)=(1) already exists.
postgres=!# rollback to somename;
ROLLBACK
postgres=*# commit;
COMMIT
postgres=# table t1;
 id 
----
  1
(1 row)

postgres=#

如上,非常便于理解,因为savepoint的存在 而不会导致整个事务的回滚!


继续看一个例子,示例二 如下:

postgres=# \d+ t1
                                           Table "public.t1"
 Column |  Type   | Collation | Nullable | Default | Storage | Compression | Stats target | Description 
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
 id     | integer |           | not null |         | plain   |             |              | 
Indexes:
    "t1_pkey" PRIMARY KEY, btree (id)
Access method: heap

postgres=# table t1;
 id 
----
  1
(1 row)

postgres=# \echo :ON_ERROR_ROLLBACK
off
postgres=# \set ON_ERROR_ROLLBACK on
postgres=# 
postgres=# begin ;
BEGIN
postgres=*# insert into t1 values(2);
INSERT 0 1
postgres=*# insert into t1 values(2);
2024-06-20 07:54:10.813 PDT [181022] ERROR:  duplicate key value violates unique constraint "t1_pkey"
2024-06-20 07:54:10.813 PDT [181022] DETAIL:  Key (id)=(2) already exists.
2024-06-20 07:54:10.813 PDT [181022] STATEMENT:  insert into t1 values(2);
ERROR:  duplicate key value violates unique constraint "t1_pkey"
DETAIL:  Key (id)=(2) already exists.
postgres=*# 
postgres=*# commit;
COMMIT
postgres=# table t1;
 id 
----
  1
  2
(2 rows)

postgres=#

这是psql变量ON_ERROR_ROLLBACK的使用!PostgreSQL官方文档解释,如下:

ON_ERROR_ROLLBACK:

  • 当被设置为on时,如果事务块中的一个语句产生一个错误,该错误会被忽略并且该事务会继续。
  • 当被设置为interactive时,只在交互式会话中忽略这类错误,而读取脚本文件时则不会忽略错误。
  • 当被重置或者设置为off(默认值)时,事务块中产生错误的一个语句会中止整个事务。
  • 错误回滚模式的工作原理是:在事务块的每个命令之前都为你发出一个隐式的SAVEPOINT,然后在该命令失败时回滚到该保存点。

类似的 pgJDBC 中也有一个类似的属性,如下:

  • pgJDBC官方文档,点击前往

在这里插入图片描述

  • autosave(字符串)默认 never
    指定查询失败时驱动程序应执行的操作。在 autosave=always 模式下,JDBC 驱动程序在每次查询前设置一个保存点,并在失败时回滚到该保存点。在 autosave=never 模式(默认)下,永远不会进行保存点切换。在 autosave=conservative 模式下,为每个查询设置保存点,但仅在极少数情况下才会回滚,例如cached statement cannot change return typestatement XXX is not valid,因此 JDBC 驱动程序会回滚并重试

  • cleanupSavepoints(布尔值)默认 false
    确定在自动保存模式下创建的 SAVEPOINT 是否在语句之前释放。这样做是为了避免在执行 1000 次查询的情况下耗尽服务器上的共享缓冲区。


功能实现源码解析

pgJDBC autosave属性

// pgjdbc\src\main\java\org\postgresql\core\v3\QueryExecutorImpl.java

  @Override
  public void execute(Query query, @Nullable ParameterList parameters,
      ResultHandler handler,
      int maxRows, int fetchSize, int flags, boolean adaptiveFetch) throws SQLException {
    try (ResourceLock ignore = lock.obtain()) {
      waitOnLock();
      if (LOGGER.isLoggable(Level.FINEST)) {
        LOGGER.log(Level.FINEST, "  simple execute, handler={0}, maxRows={1}, fetchSize={2}, flags={3}",
            new Object[]{handler, maxRows, fetchSize, flags});
      }

      if (parameters == null) {
        parameters = SimpleQuery.NO_PARAMETERS;
      }

      flags = updateQueryMode(flags);

      boolean describeOnly = (QUERY_DESCRIBE_ONLY & flags) != 0;

      ((V3ParameterList) parameters).convertFunctionOutParameters();

      // Check parameters are all set..
      if (!describeOnly) {
        ((V3ParameterList) parameters).checkAllParametersSet();
      }

      boolean autosave = false;
      try {
        try {
          handler = sendQueryPreamble(handler, flags);
          autosave = sendAutomaticSavepoint(query, flags);
          sendQuery(query, (V3ParameterList) parameters, maxRows, fetchSize, flags,
              handler, null, adaptiveFetch);
          if ((flags & QueryExecutor.QUERY_EXECUTE_AS_SIMPLE) != 0) {
            // Sync message is not required for 'Q' execution as 'Q' ends with ReadyForQuery message
            // on its own
          } else {
            sendSync();
          }
          processResults(handler, flags, adaptiveFetch);
          estimatedReceiveBufferBytes = 0;
        } catch (PGBindException se) {
          // There are three causes of this error, an
          // invalid total Bind message length, a
          // BinaryStream that cannot provide the amount
          // of data claimed by the length argument, and
          // a BinaryStream that throws an Exception
          // when reading.
          //
          // We simply do not send the Execute message
          // so we can just continue on as if nothing
          // has happened. Perhaps we need to
          // introduce an error here to force the
          // caller to rollback if there is a
          // transaction in progress?
          //
          sendSync();
          processResults(handler, flags, adaptiveFetch);
          estimatedReceiveBufferBytes = 0;
          handler
              .handleError(new PSQLException(GT.tr("Unable to bind parameter values for statement."),
                  PSQLState.INVALID_PARAMETER_VALUE, se.getIOException()));
        }
      } catch (IOException e) {
        abort();
        handler.handleError(
            new PSQLException(GT.tr("An I/O error occurred while sending to the backend."),
                PSQLState.CONNECTION_FAILURE, e));
      }

      try {
        handler.handleCompletion();
        if (cleanupSavePoints) {
          releaseSavePoint(autosave, flags);
        }
      } catch (SQLException e) {
        rollbackIfRequired(autosave, e);
      }
    }
  }
  @Override
  public void execute(Query[] queries, @Nullable ParameterList[] parameterLists,
      BatchResultHandler batchHandler, int maxRows, int fetchSize, int flags, boolean adaptiveFetch)
      throws SQLException {
    try (ResourceLock ignore = lock.obtain()) {
      waitOnLock();
      if (LOGGER.isLoggable(Level.FINEST)) {
        LOGGER.log(Level.FINEST, "  batch execute {0} queries, handler={1}, maxRows={2}, fetchSize={3}, flags={4}",
            new Object[]{queries.length, batchHandler, maxRows, fetchSize, flags});
      }

      flags = updateQueryMode(flags);

      boolean describeOnly = (QUERY_DESCRIBE_ONLY & flags) != 0;
      // Check parameters and resolve OIDs.
      if (!describeOnly) {
        for (ParameterList parameterList : parameterLists) {
          if (parameterList != null) {
            ((V3ParameterList) parameterList).checkAllParametersSet();
          }
        }
      }

      boolean autosave = false;
      ResultHandler handler = batchHandler;
      try {
        handler = sendQueryPreamble(batchHandler, flags);
        autosave = sendAutomaticSavepoint(queries[0], flags);
        estimatedReceiveBufferBytes = 0;

        for (int i = 0; i < queries.length; i++) {
          Query query = queries[i];
          V3ParameterList parameters = (V3ParameterList) parameterLists[i];
          if (parameters == null) {
            parameters = SimpleQuery.NO_PARAMETERS;
          }

          sendQuery(query, parameters, maxRows, fetchSize, flags, handler, batchHandler, adaptiveFetch);

          if (handler.getException() != null) {
            break;
          }
        }

        if (handler.getException() == null) {
          if ((flags & QueryExecutor.QUERY_EXECUTE_AS_SIMPLE) != 0) {
            // Sync message is not required for 'Q' execution as 'Q' ends with ReadyForQuery message
            // on its own
          } else {
            sendSync();
          }
          processResults(handler, flags, adaptiveFetch);
          estimatedReceiveBufferBytes = 0;
        }
      } catch (IOException e) {
        abort();
        handler.handleError(
            new PSQLException(GT.tr("An I/O error occurred while sending to the backend."),
                PSQLState.CONNECTION_FAILURE, e));
      }

      try {
        handler.handleCompletion();
        if (cleanupSavePoints) {
          releaseSavePoint(autosave, flags);
        }
      } catch (SQLException e) {
        rollbackIfRequired(autosave, e);
      }
    }
  }

我们这里对上面两个函数简化一下,如下:

  1. sendAutomaticSavepoint
  2. sendQuery(query)
  3. if(cleanupSavePoints) releaseSavePoint
  4. 异常 rollbackIfRequired

假设这里启用autosave,第一步,如下:

  private boolean sendAutomaticSavepoint(Query query, int flags) throws IOException {
    if (((flags & QueryExecutor.QUERY_SUPPRESS_BEGIN) == 0
        || getTransactionState() == TransactionState.OPEN)
        && query != restoreToAutoSave
        && !"COMMIT".equalsIgnoreCase(query.getNativeSql())
        && getAutoSave() != AutoSave.NEVER
        // If query has no resulting fields, it cannot fail with 'cached plan must not change result type'
        // thus no need to set a savepoint before such query
        && (getAutoSave() == AutoSave.ALWAYS
        // If CompositeQuery is observed, just assume it might fail and set the savepoint
        || !(query instanceof SimpleQuery)
        || ((SimpleQuery) query).getFields() != null)) {

      /*
      create a different SAVEPOINT the first time so that all subsequent SAVEPOINTS can be released
      easily. There have been reports of server resources running out if there are too many
      SAVEPOINTS.
       */
      sendOneQuery(autoSaveQuery, SimpleQuery.NO_PARAMETERS, 1, 0,
          QUERY_NO_RESULTS | QUERY_NO_METADATA
              // PostgreSQL does not support bind, exec, simple, sync message flow,
              // so we force autosavepoint to use simple if the main query is using simple
              | QUERY_EXECUTE_AS_SIMPLE);
      return true;
    }
    return false;
  }

如上autoSaveQuery是:

  private final SimpleQuery autoSaveQuery =
      new SimpleQuery(
          new NativeQuery("SAVEPOINT PGJDBC_AUTOSAVE", null, false, SqlCommand.BLANK),
          null, false);

第二步:就是执行query


第三步:如果这里cleanupSavepoints = true,那么如下:

  private void releaseSavePoint(boolean autosave, int flags) throws SQLException {
    if ( autosave
        && getAutoSave() == AutoSave.ALWAYS
        && getTransactionState() == TransactionState.OPEN) {
      try {
        sendOneQuery(releaseAutoSave, SimpleQuery.NO_PARAMETERS, 1, 0,
            QUERY_NO_RESULTS | QUERY_NO_METADATA
                | QUERY_EXECUTE_AS_SIMPLE);

      } catch (IOException ex) {
        throw  new PSQLException(GT.tr("Error releasing savepoint"), PSQLState.IO_ERROR);
      }
    }
  }

如上releaseAutoSave是:

  private final SimpleQuery releaseAutoSave =
      new SimpleQuery(
          new NativeQuery("RELEASE SAVEPOINT PGJDBC_AUTOSAVE", null, false, SqlCommand.BLANK),
          null, false);

第四步:这里通常就是上面query执行失败的情况,如下:

  private void rollbackIfRequired(boolean autosave, SQLException e) throws SQLException {
    if (autosave
        && getTransactionState() == TransactionState.FAILED
        && (getAutoSave() == AutoSave.ALWAYS || willHealOnRetry(e))) {
      try {
        // ROLLBACK and AUTOSAVE are executed as simple always to overcome "statement no longer exists S_xx"
        execute(restoreToAutoSave, SimpleQuery.NO_PARAMETERS, new ResultHandlerDelegate(null),
            1, 0, QUERY_NO_RESULTS | QUERY_NO_METADATA | QUERY_EXECUTE_AS_SIMPLE);
      } catch (SQLException e2) {
        // That's O(N), sorry
        e.setNextException(e2);
      }
    }
    throw e;
  }

如上restoreToAutoSave是:

  /*
  In autosave mode we use this query to roll back errored transactions
   */
  private final SimpleQuery restoreToAutoSave =
      new SimpleQuery(
          new NativeQuery("ROLLBACK TO SAVEPOINT PGJDBC_AUTOSAVE", null, false, SqlCommand.BLANK),
          null, false);
}

看到这里,其整个流程上 内部实现和上面的示例一 一模一样!


psql ON_ERROR_ROLLBACK

我们这里直接调试一下 示例二,如下:

在这里插入图片描述

然后开始执行这个query,如下:

在这里插入图片描述


然后看一下接下来的逻辑处理,如下:

// src/bin/psql/common.c

...
	/* If we made a temporary savepoint, possibly release/rollback */
	if (on_error_rollback_savepoint)
	{
		const char *svptcmd = NULL;

		transaction_status = PQtransactionStatus(pset.db);

		switch (transaction_status)
		{
			case PQTRANS_INERROR:
				/* We always rollback on an error */
				svptcmd = "ROLLBACK TO pg_psql_temporary_savepoint";
				break;

			case PQTRANS_IDLE:
				/* If they are no longer in a transaction, then do nothing */
				break;

			case PQTRANS_INTRANS:

				/*
				 * Release our savepoint, but do nothing if they are messing
				 * with savepoints themselves
				 */
				if (!svpt_gone)
					svptcmd = "RELEASE pg_psql_temporary_savepoint";
				break;

			case PQTRANS_ACTIVE:
			case PQTRANS_UNKNOWN:
			default:
				OK = false;
				/* PQTRANS_UNKNOWN is expected given a broken connection. */
				if (transaction_status != PQTRANS_UNKNOWN || ConnectionUp())
					pg_log_error("unexpected transaction status (%d)",
								 transaction_status);
				break;
		}

		if (svptcmd)
		{
			PGresult   *svptres;

			svptres = PQexec(pset.db, svptcmd);
			if (PQresultStatus(svptres) != PGRES_COMMAND_OK)
			{
				pg_log_info("%s", PQerrorMessage(pset.db));
				ClearOrSaveResult(svptres);
				OK = false;

				goto sendquery_cleanup;
			}
			PQclear(svptres);
		}
	}

...

在这里插入图片描述

上面是失败的情形,下面来看一个成功的例子!


在这里插入图片描述

自然这里最终就是成功的,如下:

postgres=# table t1;
 id 
----
  1
  2
(2 rows)

postgres=# begin ;
BEGIN
postgres=*# insert into t1 values(3);
INSERT 0 1
postgres=*# commit;
COMMIT
postgres=# table t1;
 id 
----
  1
  2
  3
(3 rows)

postgres=#

注:无论是驱动pgJDBC还是连接工具psql,如上的 auto savepoint的行为都可以理解为 client 一侧的行为。那么我们是否可以在 server 一端实现类似的功能呢?

这个问题 本人后面另开一篇新的博客进行探讨!

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

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

相关文章

mongosh常用命令详解及如何开启MongoDB身份验证

目录 Mongosh常用命令介绍 连接到MongoDB实例 基本命令 查看当前数据库 切换数据库 查看所有数据库 查看当前数据库中的集合 CRUD操作 插入文档 查询文档 更新文档 删除文档 替换文档 索引操作 创建索引 查看索引 删除索引 聚合操作 数据库管理 创建用户 …

社区项目-项目介绍环境搭建

文章目录 1.技术选型2.原型设计1.安装AxureRP2.进行汉化3.载入元件库4.基本设计 3.元数建模1.安装元数建模软件2.新建项目3.新增一个刷题模块主题域4.新增数据表 subject_category5.新增关系图&#xff0c;将表拖过来6.新增题目标签表7.新增题目信息表8.新增单选表、多选表、判…

姿态识别论文复现(一)安装包+下载数据

Lite-HRNet&#xff1a;轻量级高分辨率网络 简介&#xff1a;高分辨率网络Lite-HRNet&#xff0c;用于人体姿态估计 环境配置&#xff1a;该代码是在 Ubuntu 16.04 上使用 python 3.6 开发的。需要 NVIDIA GPU。使用 8 个 NVIDIA V100 GPU 卡进行开发和测试。其他平台或 GPU …

智慧园区解决方案PPT(53页)

## 1.1 智慧园区背景及需求分析 - 智慧园区的发展历程包括园区规划、经济、产业、企业、管理、理念的转变&#xff0c;强调管理模式创新&#xff0c;关注业务综合化、管理智慧化等发展。 ## 1.2 国家对智慧园区发展的政策 - 涉及多个国家部门&#xff0c;如工信部、住建部、…

uniapp公用返回组件

uniapp写一个公用的头部组件&#xff0c;包含home和返回。 页面中的引用 2.在components文件夹下&#xff0c;新建一个navBar.vue <template><view class"view-wrap"><view :style"{ height: barHeight }"></view><view cla…

EtherCAT扫盲,都是知识点

1. 什么是EtherCAT EtherCAT&#xff0c;全称Ethernet for Control Automation Technology&#xff0c;字面意思就是用于控制自动化技术的以太网。它是一种基于以太网的实时工业通信协议&#xff0c;简单说&#xff0c;就是让机器们通过网线互相聊天的高级方式。 EtherCAT 是最…

openEuler 22.03 (LTS-SP1)服务器用ntpd同步GPS时间服务器的案例

本文记录了openEuler 22.03 (LTS-SP1)的二级时间服务器用chronyd不能自动同步GPS时间服务器&#xff0c;改用ntpd同步GPS时间服务器成功的案例 一、环境简述 1、本环境中有两台GPS一级时间服务器&#xff0c;IP如下&#xff1a; 192.168.188.66 192.168.188.74 2、有一台o…

开发中遇到的错误 - @SpringBootTest 注解爆红

我在使用 SpringBootTest 注解的时候爆红了&#xff0c;ait 回车也导不了包&#xff0c;后面发现是因为没有加依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId>…

JAVA小知识28:FIle类文件对象

Java 中的 File 类是 java.io 包中的一个类&#xff0c;用于表示文件和目录路径名的抽象表示。它提供了一些方法来操作文件和目录 一、File的创建 1.1、绝对路径 绝对路径是指从文件系统的根目录开始定位文件或目录的完整路径。它通常以根目录符号开始&#xff0c;在 Window…

​Claude 3.5 最新体验:助力硕博生与科研人员高效完成论文,超越ChatGPT4o !

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 要不说AI领域的进展真的是日新月异&#xff0c;发展速度已经大大超过预期进度。娜姐本来在准备AI降重工具的测评文章&#xff08;最近好多小伙伴需要&#xff09;。 昨天晚上…

多头Attention MultiheadAttention 怎么用?详细解释

import torch import torch.nn as nn# 定义多头注意力层 embed_dim 512 # 输入嵌入维度 num_heads 8 # 注意力头的数量 multihead_attn nn.MultiheadAttention(embed_dim, num_heads)# 创建一些示例数据 batch_size 10 # 批次大小 seq_len 20 # 序列长度 query torch…

rknn转换后精度差异很大,失真算子自纠

下面是添加了详细注释的优化代码&#xff1a; import cv2 import numpy as np import onnx import onnxruntime as rt from onnx import helper, shape_inferencedef get_all_node_names(model):"""获取模型中所有节点的名称。参数:model (onnx.ModelProto): O…

【有手就会】图数据库Demo教程,实现反洗钱场景下银行转账流水数据分析

前言 星环社区版家族于近期发布了单机、30s一键启动的StellarDB图数据库&#xff0c;本篇文章将为用户介绍如何使用开发版StellarDB实现人物关系探索。 友情链接&#xff1a;白话大数据 | 关于图数据库&#xff0c;没有比这篇更通俗易懂的啦 TDH社区版本次发布StellarDB社区…

可信启动Trusted Board Boot

TBB Trusted Board Boot&#xff08;TBB&#xff09;对所有固件镜像&#xff08;包括普通世界的bootloader&#xff09;进行身份验证&#xff0c;以防止恶意固件在平台上运行。TBB使用公钥加密标准 &#xff08;PKCS&#xff09;来建立信任链&#xff08;Chain of Trust&#…

密码CTF(4)——e和phi不互素

参考 RSA中e和phi不互素 AMM算法 AMM算法简要理解 RSA系列解题研究——e与phi不互素 - 先知社区 (aliyun.com) e与phi不互素 --- 四 1 1 1道题详记-CSDN博客 总述 gcd(e,φ(n))比较小时可以考虑iroot直接开根&#xff0c;当直接开根跑不出来时&#xff0c;考虑有限域…

xargs 传参

xargs的默认命令是 echo&#xff0c;空格是默认定界符。这意味着通过管道传递给 xargs的输入将会包含换行和空白&#xff0c;不过通过 xargs 的处理&#xff0c;换行和空白将被空格取代。xargs是构建单行命令的重要组件之一。 xargs -n1 // 一次输出一个参数到一行&#xf…

【Android面试八股文】你能说一说自定义View与ViewGroup的区别

文章目录 Android UI 组件:View 和 ViewGroupViewGroup 的职责View 的职责自定义 View 和 ViewGroup 的区别1. 继承的类不同2. 主要功能不同3. 重写方法不同4. 使用场景不同5. 事件分发方面的区别6. UI 绘制方面的区别Android UI 组件:View 和 ViewGroup 在 Android 开发中,…

Python开发日记--手撸加解密小工具(3)

目录 1.xcb-cuisor0问题解决 2.AES-CBC算法实现 2.1 信号和槽机制 2.2 开始设计算法 3.小结 1.xcb-cuisor0问题解决 继续解决该问题&#xff0c;在Ubuntu下面运行会发生这个错误。 看描述&#xff0c; 这是说要运行Qt xcb平台插件&#xff0c;需要xcb-cursor0或者libxcb-c…

舔狗日记Puls微信小程序源码

源码介绍&#xff1a; 这是一款舔狗日记Puls微信小程序源码&#xff0c;提供每日一舔的功能&#xff0c;让你舔到最后&#xff0c;什么都有&#xff01; 源码通过API获取一些舔狗日记&#xff0c;内置了100多句舔狗日记&#xff0c;让你摆脱上班摸鱼的无聊时光&#xff0c; …

Python火焰锋动力学和浅水表面波浪偏微分方程

&#x1f3af;要点 &#x1f3af;流图可视化正弦余弦矢量场 | &#x1f3af;解空间变化边界条件二维拉普拉斯方程 | &#x1f3af;解圆柱坐标系标量场 | &#x1f3af;解一维泊松方程 | &#x1f3af;解二维扩散方程 | &#x1f3af;解火焰锋的动力学偏微分方程 | &#x1f3a…