Apache Paimon系列之:主键表

Apache Paimon系列之:主键表

  • 一、主键表
    • 1.Bucket
    • 2.LSM Trees
    • 3.Compaction
  • 二、数据分布
    • 1.固定Bucket
    • 2.动态Bucket
    • 3.正常动态Bucket模式
    • 4.跨分区更新插入动态存储桶模式
  • 三、Merge Engine
    • 1.Deduplicate
    • 2.部分更新
    • 3.序列组
    • 4.聚合部分更新
    • 5.聚合
    • 6.Retract
    • 7.First Row
  • 四、Changelog Producer
    • 1.None
    • 2.Input
    • 3.Lookup
    • 4.Full Compaction
  • 五、Sequence and Rowkind
    • 1.Sequence Field
    • 2.Row Kind Field

一、主键表

主键表是创建表时默认的表类型。用户可以插入、更新或删除表中的记录。

主键由一组包含每个记录的唯一值的列组成。 Paimon 通过对每个存储桶内的主键进行排序来强制数据排序,允许用户通过对主键应用过滤条件来实现高性能。

1.Bucket

未分区表或分区表中的分区被细分为存储桶,以便为可用于更有效查询的数据提供额外的结构。

每个存储桶目录都包含一个 LSM 树及其变更日志文件。

桶的范围由记录中的一列或多列的哈希值确定。用户可以通过提供bucket-key选项来指定分桶列。如果未指定bucket-key选项,则主键(如果已定义)或完整记录将用作存储桶键。

桶是读写的最小存储单元,因此桶的数量限制了最大处理并行度。不过这个数字不应该太大,因为它会导致大量小文件和低读取性能。一般来说,每个桶中建议的数据大小约为200MB - 1GB。

另外,如果您想在创建表后调整存储桶的数量,请参阅重新缩放存储桶。

2.LSM Trees

Paimon 采用 LSM 树(日志结构合并树)作为文件存储的数据结构。本文档简要介绍了LSM树的概念。

排序运行

LSM 树将文件组织成多个排序的运行。排序运行由一个或多个数据文件组成,并且每个数据文件恰好属于一个排序运行。

数据文件中的记录按其主键排序。在排序运行中,数据文件的主键范围永远不会重叠。

在这里插入图片描述
正如您所看到的,不同的排序运行可能具有重叠的主键范围,甚至可能包含相同的主键。查询LSM树时,必须合并所有排序的运行,并且必须根据用户指定的合并引擎和每条记录的时间戳来合并具有相同主键的所有记录。

写入LSM树的新记录将首先缓存在内存中。当内存缓冲区满时,内存中的所有记录将被排序并刷新到磁盘。现在已创建新的排序运行。

3.Compaction

当越来越多的记录写入LSM树时,排序游程的数量将会增加。由于查询LSM树需要将所有排序运行合并起来,太多排序运行将导致查询性能较差,甚至内存不足。

为了限制排序运行的数量,我们必须偶尔将多个排序运行合并为一个大的排序运行。这个过程称为压缩。

然而,压缩是一个资源密集型过程,会消耗一定的CPU时间和磁盘IO,因此过于频繁的压缩可能会导致写入速度变慢。这是查询和写入性能之间的权衡。 Paimon 目前采用了类似于 Rocksdb 通用压缩的压缩策略。

默认情况下,当Paimon将记录追加到LSM树时,它也会根据需要执行压缩。用户还可以选择在专用压缩作业中执行所有压缩。

二、数据分布

Bucket是读写的最小存储单元,每个Bucket目录中包含一棵LSM树。

1.固定Bucket

配置大于0的桶,采用Fixed Bucket模式,根据Math.abs(key_hashcode % numBuckets)计算记录的桶。

重新缩放存储桶只能通过离线流程完成。桶数过多会导致小文件过多,桶数过少会导致写入性能较差。

2.动态Bucket

配置“桶”=“-1”。先到达的key会落入旧的bucket,新的key会落入新的bucket,bucket和key的分布取决于数据到达的顺序。 Paimon 维护一个索引来确定哪个键对应哪个桶。

Paimon会自动扩大桶的数量。

  • Option1: ‘dynamic-bucket.target-row-num’:控制一个桶的目标行数。
  • 选项2:‘dynamic-bucket.initial-buckets’:控制初始化bucket的数量。

动态Bucket仅支持单个写入作业。请不要启动多个作业来写入同一分区(这可能会导致重复数据)。即使您启用“只写”并启动专用的压缩作业,它也不会起作用。

3.正常动态Bucket模式

当您的更新不跨分区(没有分区,或者主键包含所有分区字段)时,动态桶模式使用 HASH 索引来维护从键到桶的映射,它比固定桶模式需要更多的内存。

表现:

  • 一般来说,没有性能损失,但会有一些额外的内存消耗,一个分区中的 1 亿个条目多占用 1 GB 内存,不再活动的分区不占用内存。
  • 对于更新率较低的表,建议使用此模式,以显着提高性能。

普通动态桶模式支持排序压缩以加快查询速度。

4.跨分区更新插入动态存储桶模式

当需要跨分区upsert(主键不包含所有分区字段)时,Dynamic Bucket模式直接维护键到分区和桶的映射,使用本地磁盘,并在启动流写作业时通过读取表中所有现有键来初始化索引。不同的合并引擎有不同的行为:

  • 重复数据删除:删除旧分区中的数据,并将新数据插入到新分区中。
  • PartialUpdate & Aggregation:将新数据插入旧分区。
  • FirstRow:如果有旧值,则忽略新数据。

性能:对于数据量较大的表,性能会有明显的损失。而且,初始化需要很长时间。

如果你的upsert不依赖太旧的数据,可以考虑配置索引TTL来减少索引和初始化时间:

  • ‘cross-partition-upsert.index-ttl’:rocksdb索引和初始化中的TTL,这样可以避免维护太多索引而导致性能越来越差。
    但请注意,这也可能会导致数据重复。

三、Merge Engine

当Paimon接收器收到两条或更多具有相同主键的记录时,它会将它们合并为一条记录以保持主键唯一。通过指定 merge-engine 表属性,用户可以选择如何将记录合并在一起。

始终在 Flink SQL TableConfig 中将 table.exec.sink.upsert-materialize 设置为 NONE,sink upsert-materialize 可能会导致奇怪的行为。当输入乱序时,我们建议您使用序列字段来纠正乱序。

1.Deduplicate

去重合并引擎是默认的合并引擎。 Paimon 只会保留最新的记录,并丢弃其他具有相同主键的记录。

具体来说,如果最新的记录是DELETE记录,则所有具有相同主键的记录都将被删除。

2.部分更新

通过指定 ‘merge-engine’ = ‘partial-update’,用户可以通过多次更新来更新记录的列,直到记录完成。这是通过使用同一主键下的最新数据逐一更新值字段来实现的。但是,在此过程中不会覆盖空值。

例如,假设 Paimon 收到三个记录:

<1, 23.0, 10, NULL>-
<1, NULL, NULL, ‘This is a book’>
<1, 25.2, NULL, NULL>

假设第一列是主键,则最终结果将是 <1, 25.2, 10, ‘This is a book’>。

对于流式查询,部分更新合并引擎必须与查找或完全压缩变更日志生成器一起使用。 (还支持“输入”变更日志生成器,但仅返回输入记录。)

默认情况下,部分更新不接受删除记录,您可以选择以下解决方案之一:

  • 配置“partial-update.ignore-delete”以忽略删除记录。
  • 配置“sequence-group”以收回部分列。

3.序列组

序列字段并不能解决多流更新的部分更新表的乱序问题,因为多流更新时序列字段可能会被另一个流的最新数据覆盖。

因此我们引入了部分更新表的序列组机制。它可以解决:

  • 多流更新时出现混乱。每个流定义其自己的序列组。
  • 真正的部分更新,而不仅仅是非空更新。
CREATE TABLE t (
    k INT,
    a INT,
    b INT,
    g_1 INT,
    c INT,
    d INT,
    g_2 INT,
    PRIMARY KEY (k) NOT ENFORCED
) WITH (
    'merge-engine'='partial-update',
    'fields.g_1.sequence-group'='a,b',
    'fields.g_2.sequence-group'='c,d'
);

INSERT INTO t VALUES (1, 1, 1, 1, 1, 1, 1);

-- g_2 is null, c, d should not be updated
INSERT INTO t VALUES (1, 2, 2, 2, 2, 2, CAST(NULL AS INT));

SELECT * FROM t; -- output 1, 2, 2, 2, 1, 1, 1

-- g_1 is smaller, a, b should not be updated
INSERT INTO t VALUES (1, 3, 3, 1, 3, 3, 3);

SELECT * FROM t; -- output 1, 2, 2, 2, 3, 3, 3

对于 fields..sequence-group,有效的比较数据类型包括:DECIMAL、TINYINT、SMALLINT、INTEGER、BIGINT、FLOAT、DOUBLE、DATE、TIME、TIMESTAMP 和 TIMESTAMP_LTZ。

4.聚合部分更新

您可以为输入字段指定聚合函数,支持聚合中的所有函数。

CREATE TABLE t (
          k INT,
          a INT,
          b INT,
          c INT,
          d INT,
          PRIMARY KEY (k) NOT ENFORCED
) WITH (
     'merge-engine'='partial-update',
     'fields.a.sequence-group' = 'b',
     'fields.b.aggregate-function' = 'first_value',
     'fields.c.sequence-group' = 'd',
     'fields.d.aggregate-function' = 'sum'
 );
INSERT INTO t VALUES (1, 1, 1, CAST(NULL AS INT), CAST(NULL AS INT));
INSERT INTO t VALUES (1, CAST(NULL AS INT), CAST(NULL AS INT), 1, 1);
INSERT INTO t VALUES (1, 2, 2, CAST(NULL AS INT), CAST(NULL AS INT));
INSERT INTO t VALUES (1, CAST(NULL AS INT), CAST(NULL AS INT), 2, 2);


SELECT * FROM t; -- output 1, 2, 1, 2, 3

5.聚合

注意:始终在 Flink SQL TableConfig 中将 table.exec.sink.upsert-materialize 设置为 NONE。

有时用户只关心聚合结果。聚合合并引擎根据聚合函数将同一主键下的各个值字段与最新数据一一聚合。

每个不属于主键的字段都可以被赋予一个聚合函数,由 fields..aggregate-function 表属性指定,否则它将使用 last_non_null_value 聚合作为默认值。例如,请考虑下表定义。

CREATE TABLE my_table (
    product_id BIGINT,
    price DOUBLE,
    sales BIGINT,
    PRIMARY KEY (product_id) NOT ENFORCED
) WITH (
    'merge-engine' = 'aggregation',
    'fields.price.aggregate-function' = 'max',
    'fields.sales.aggregate-function' = 'sum'
);

现场价格将通过 max 函数聚合,现场销售额将通过 sum 函数聚合。给定两个输入记录 <1, 23.0, 15> 和 <1, 30.2, 20>,最终结果将是 <1, 30.2, 35>。

当前支持的聚合函数和数据类型有:

  • sum:sum 函数聚合多行中的值。它支持 DECIMAL、TINYINT、SMALLINT、INTEGER、BIGINT、FLOAT 和 DOUBLE 数据类型。
  • product:产品功能可以跨多行计算产品值。它支持 DECIMAL、TINYINT、SMALLINT、INTEGER、BIGINT、FLOAT 和 DOUBLE 数据类型。
  • count:count 函数对多行中的值进行计数。它支持 INTEGER、BIGINT 数据类型。
  • max:max函数识别并保留最大值。它支持 CHAR、VARCHAR、DECIMAL、TINYINT、SMALLINT、INTEGER、BIGINT、FLOAT、DOUBLE、DATE、TIME、TIMESTAMP 和 TIMESTAMP_LTZ 数据类型。
  • min:min 函数识别并保留最小值。它支持 CHAR、VARCHAR、DECIMAL、TINYINT、SMALLINT、INTEGER、BIGINT、FLOAT、DOUBLE、DATE、TIME、TIMESTAMP 和 TIMESTAMP_LTZ 数据类型。
  • last_value:last_value 函数用最近导入的值替换以前的值。它支持所有数据类型。
  • last_non_null_value:last_non_null_value 函数用最新的非空值替换先前的值。它支持所有数据类型。
  • listagg:listagg 函数将多个字符串值连接成一个字符串。它支持 STRING 数据类型。
  • bool_and:bool_and 函数评估布尔集中的所有值是否都为 true。它支持 BOOLEAN 数据类型。
  • bool_or:bool_or 函数检查布尔集中是否至少有一个值为 true。它支持 BOOLEAN 数据类型。
  • first_value:first_value 函数从数据集中检索第一个空值。它支持所有数据类型。
  • first_non_null_value:first_non_null_value 函数选择数据集中的第一个非空值。它支持所有数据类型。
  • nested_update:nested_update 函数将多行收集到一个数组中(所谓的“嵌套表”)。它支持 ARRAY 数据类型。
  • 使用 fields..nested-key=pk0,pk1,… 指定嵌套表的主键。如果没有键,行将被追加到数组中。
-- orders table
CREATE TABLE orders (
  order_id BIGINT PRIMARY KEY NOT ENFORCED,
  user_name STRING,
  address STRING
);

-- sub orders that have the same order_id 
-- belongs to the same order
CREATE TABLE sub_orders (
  order_id BIGINT,
  sub_order_id INT,
  product_name STRING,
  price BIGINT,
  PRIMARY KEY (order_id, sub_order_id) NOT ENFORCED
);

-- wide table
CREATE TABLE order_wide (
  order_id BIGINT PRIMARY KEY NOT ENFORCED,
  user_name STRING,
  address STRING,
  sub_orders ARRAY<ROW<sub_order_id BIGINT, product_name STRING, price BIGINT>>
) WITH (
  'merge-engine' = 'aggregation',
  'fields.sub_orders.aggregate-function' = 'nested_update',
  'fields.sub_orders.nested-key' = 'sub_order_id'
);

-- widen
INSERT INTO order_wide

SELECT 
  order_id, 
  user_name,
  address, 
  CAST (NULL AS ARRAY<ROW<sub_order_id BIGINT, product_name STRING, price BIGINT>>) 
FROM orders

UNION ALL 
  
SELECT 
  order_id, 
  CAST (NULL AS STRING), 
  CAST (NULL AS STRING), 
  ARRAY[ROW(sub_order_id, product_name, price)] 
FROM sub_orders;

-- query using UNNEST
SELECT order_id, user_name, address, sub_order_id, product_name, price 
FROM order_wide, UNNEST(sub_orders) AS so(sub_order_id, product_name, price)

collect: 收集函数将元素收集到数组中。您可以设置 fields..distinct=true 来删除重复元素。它仅支持 ARRAY 类型。

merge_map: merge_map函数合并输入映射。它仅支持 MAP 类型。

对于流式查询,聚合合并引擎必须与查找或完全压缩变更日志生成器一起使用。 (还支持“输入”变更日志生成器,但仅返回输入记录。)

6.Retract

仅 sum、product、count、collect、merge_map、nested_update、last_value 和 last_non_null_value 支持撤回(UPDATE_BEFORE 和 DELETE),其他聚合函数不支持撤回。如果允许某些函数忽略撤回消息,可以配置:‘fields.${field_name}.ignore-retract’=‘true’。

当接受撤回消息时,last_value和last_non_null_value只是将字段设置为null。

collect 和 merge_map 尽最大努力尝试处理撤回消息,但不保证结果准确。处理撤回消息时可能会出现以下行为:

  • 如果记录混乱,它可能无法处理撤回消息。例如表使用collect,上游分别发送+I[‘A’, ‘B’]和-U[‘A’]。如果表首先收到-U[‘A’],则它什么也不做;然后它收到+I[‘A’, ‘B’],合并结果将是+I[‘A’, ‘B’]而不是+I[‘B’]。
  • 来自一个上游的撤回消息将撤回多个上游合并的结果。例如表使用merge_map,一个上游发送+I[1->A],另一个上游发送+I[1->B],稍后发送-D[1->B]。表会先将两个插入值合并到+I[1->B],然后-D[1->B]会收回整个结果,所以最终结果是一个空映射而不是+I[1- >A]

7.First Row

通过指定 ‘merge-engine’ = ‘first-row’,用户可以保留同一主键的第一行。它与去重合并引擎不同,在第一行合并引擎中,它将生成仅插入更改日志。

  • 第一行合并引擎必须与查找变更日志生成器一起使用。
  • 您不能指定sequence.field。
  • 不接受 DELETE 和 UPDATE_BEFORE 消息。您可以配置first-row.ignore-delete来忽略这两种记录。

这对于替代流计算中的日志去重有很大的帮助。

四、Changelog Producer

流式查询会不断产生最新的变化。

通过在创建表时指定更改日志生成器表属性,用户可以选择从表文件生成的更改模式。

变更日志生成器表属性仅影响表文件中的变更日志。它不影响外部日志系统。

1.None

默认情况下,不会将额外的变更日志生成器应用于表的写入器。 Paimon 源只能看到跨快照的合并更改,例如删除了哪些键以及某些键的新值是什么。

但是,这些合并的更改无法形成完整的更改日志,因为我们无法直接从中读取键的旧值。合并的更改要求消费者“记住”每个键的值并重写这些值而不看到旧的值。然而,一些消费者需要旧的价值观来确保正确性或效率。

考虑一个消费者计算某些分组键的总和(可能不等于主键)。如果消费者只看到一个新值5,它无法确定应该将哪些值添加到求和结果中。例如,如果旧值为 4,则应在结果中加 1。但如果旧值是 6,则应依次从结果中减去 1。旧的价值观对于这些类型的消费者来说很重要。

总而言之,没有一个变更日志生成器最适合数据库系统等使用者。 Flink 还有一个内置的“规范化”运算符,可以将每个键的值保留在状态中。很容易看出,这种操作符的成本非常高,应该避免使用。 (您可以通过“scan.remove-normalize”强制删除“标准化”运算符。)

在这里插入图片描述

2.Input

通过指定 ‘changelog- Producer’ = ‘input’,Paimon 编写者依赖他们的输入作为完整变更日志的来源。所有输入记录将保存在单独的变更日志文件中,并由 Paimon 来源提供给消费者。

当 Paimon 编写者的输入是完整的变更日志(例如来自数据库 CDC)或由 Flink 状态计算生成时,可以使用输入变更日志生成器。

在这里插入图片描述

3.Lookup

如果您的输入无法生成完整的变更日志,但您仍然想摆脱昂贵的标准化运算符,则可以考虑使用“查找”变更日志生成器。

通过指定’changelog- Producer’ = ‘lookup’,Paimon将在提交数据写入之前通过’lookup’生成changelog。

在这里插入图片描述
Lookup 会将数据缓存在内存和本地磁盘上,您可以使用以下选项来调整性能:

在这里插入图片描述

Lookupchangelog- Producer 支持changelog- Producer.row-deduplicate 以避免为同一记录生成-U、+U 变更日志。

(注意:请增加 ‘execution.checkpointing.max-concurrent-checkpoints’ Flink 配置,这对性能非常重要)。

4.Full Compaction

如果你觉得“lookup”的资源消耗太大,可以考虑使用“full-compaction”的changelog Producer,它可以解耦数据写入和changelog生成,更适合高延迟的场景(例如10分钟) )。

通过指定 ‘changelog- Producer’ = ‘full-compaction’,Paimon 将比较完全压缩之间的结果并生成差异作为变更日志。变更日志的延迟受到完全压缩频率的影响。

通过指定 full-compaction.delta-commits 表属性,在增量提交(检查点)后将不断触发完全压缩。默认情况下设置为 1,因此每个检查点都会进行完全压缩并生成更改日志。

在这里插入图片描述
完全压缩变更日志生成器可以为任何类型的源生成完整的变更日志。然而,它不如输入变更日志生成器那么高效,并且生成变更日志的延迟可能很高。

全压缩changelog- Producer 支持changelog- Producer.row-deduplicate 以避免为同一记录生成-U、+U 变更日志。

(注意:请增加 ‘execution.checkpointing.max-concurrent-checkpoints’ Flink 配置,这对性能非常重要)。

五、Sequence and Rowkind

创建表时,可以通过指定字段来指定“sequence.field”来确定更新的顺序,也可以指定“rowkind.field”来确定记录的changelog种类。 ‘sequence.field’ 通过指定字段来确定更新的顺序,或者可以指定’rowkind.field’ 来确定记录的changelog 类型。

1.Sequence Field

默认情况下,主键表根据输入顺序确定合并顺序(最后输入的记录将是最后合并的)。然而在分布式计算中,会存在一些导致数据混乱的情况。这时,可以使用时间字段作为sequence.field,例如:

CREATE TABLE my_table (
    pk BIGINT PRIMARY KEY NOT ENFORCED,
    v1 DOUBLE,
    v2 BIGINT,
    update_time TIMESTAMP
) WITH (
    'sequence.field' = 'update_time'
);

最大的sequence.field值的记录将是最后合并的,如果值相同,将根据输入顺序来确定哪一条是最后一条。

您可以为sequence.field定义多个字段,例如’update_time,flag’,多个字段将按顺序进行比较。

2.Row Kind Field

默认情况下,主键表根据输入行确定行类型。您还可以定义“rowkind.field”以使用字段来提取行类型。

有效的行类型字符串应为“+I”、“-U”、“+U”或“-D”。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【内存分布管理】new与malloc以及delede与delete[]的区别

文章目录 1.内存分布示意图(重要)2.判断以下程序中的变量在什么区域3.new和delete4.malloc和new的区别5.内存泄漏内存泄漏的分类&#xff1a;_CrtDumpMemoryLeaks() 函数如何避免内存泄漏 1.内存分布示意图(重要) 其中数据共享区也叫内存映射段&#xff0c;是高效的I/O映射方式…

【Python】成功解决ModuleNotFoundError: No module named ‘bs4‘

【Python】成功解决ModuleNotFoundError: No module named ‘bs4’ &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望…

windows10+cpu+pycharm跑yolov5

1、安装anaconda和pycharm&#xff0c;安装方法参考&#xff1a; anaconda和pycharm安装&#xff08;windows10 &#xff09;-CSDN博客 2、创建yolov5环境&#xff0c;打开Anaconda Prompt命令打开cmd命令行窗口&#xff0c;如下所示&#xff1a; 输入&#xff1a;conda crea…

.net框架和c#程序设计第一次测试

一、html整体结构 首先&#xff0c;代码的一个整体结构&#xff0c;可以通过输入一个英文!来实现 加个备注&#xff1a; <!DOCTYPE html> <!-- 声明文档类型为 HTML --> <html lang"en"> <!-- 标识文档语言为英语 --> <head><met…

谷歌关键词优化#川圣SEO#蜘蛛池

baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; baidu搜索&#xff1a;如何联系八爪鱼SEO&#xff1f; 谷歌关键词优化&#xff0c;也被称为谷歌搜索引擎优化 (SEO)&#xff0c;是一种策略&#xff0c;旨在提高网…

一个悄然崛起的AI开源项目!

众所周知&#xff0c;最近这半年AI相关的话题实在是火到出圈。尤其是生成式AI的流行&#xff0c;让我们普通人也可以近距离地接触和应用AI。这其中最典型的就是ChatGPT。 那除了ChatGPT&#xff0c;还有一个非常实用的领域&#xff0c;也是我们今天要讨论的话题&#xff0c;那…

C++内存分布与动态内存管理

文章目录 :dizzy: C/C内存分布:dizzy:C语言中动态内存管理方式  :sparkles:malloc   :sparkles:calloc  :sparkles:realloc :dizzy:C语言中动态内存管理方式  :sparkles:new和delete操作内置类型  :sparkles:new和delete操作自定义类型 :dizzy:operator new与operato…

面试经典-16- 环形链表

题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#…

揭秘Kafka拦截器的神奇操作

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 揭秘Kafka拦截器的神奇操作 前言拦截器的基本概念Kafka 拦截器的定义和基本原理&#xff1a;拦截器是 Kafka 消息传递的不可或缺的组成部分的原因&#xff1a; 生产者拦截器配置和使用生产者拦截器的步…

C# 文件拖入控件中,显示文件路径

1.设置所需拖入的控件&#xff08;以Textbox为列&#xff09;属性为&#xff1a; this.textBox1.AllowDrop true; //设置AllowDrop 属性为 true&#xff0c;使之支持拖拽&#xff0c;否则拖拽显示禁用状态 2.设置该控件的两个事件&#xff0c;分别为&#xff1a; ①DragEnt…

Vcenter esxi web界面访问提示权限被拒绝

一、问题现象 原因 应该是在vCenter中添加主机的时候&#xff0c;将锁定模式设置成了严格。 二、解决过程 2.1 方式一 BMC登录主机&#xff0c;连接显示器和键盘。 输入账号密码&#xff0c;按F2进行设置&#xff0c;将会打开一个界面&#xff0c;第一个选项是设置密码&…

c++指针的定义和使用

1、定义一个指针 int a10; //定义指针的语法&#xff1a;数据类型 * 指针变量名&#xff1a;int * p&#xff1b; //让指针记录变量a的地址&#xff1a;p &a; int a 10;int* p; p &a; cout << "a的地址为&#xff1a;" << &a <<…

疯狂数钞票H5游戏

移动端微信h5 <template><div class"container" id"container"><div class"regBag"></div><div class"moneyBox"><transitionv-for"(item,index) in showImgList":key"index"…

微服务技术栈之rabbitMQ高级(二)

我们该如何确保MQ消息的可靠性&#xff1f; 如果真的发送失败&#xff0c;有没有其它的兜底方案&#xff1f; 这些问题&#xff0c;在这一次的学习中都会找到答案。 生产者的可靠性 首先&#xff0c;我们一起分析一下消息丢失的可能性有哪些。 消息从发送者发送消息&#…

leetcode一天一题-第1天

为了增加自己的代码实战能力&#xff0c;希望通过刷leetcode的题目&#xff0c;不断提高自己&#xff0c;增加对代码的理解&#xff0c;同时开拓自己的思维方面。 题目名称&#xff1a;两数之和 题目编号&#xff1a;1 题目介绍&#xff1a; 给定一个整数数组 nums 和一个整数…

Instant --java学习笔记

Instant 时间线上的某个时刻 / 时间戳过获取lnstant的对象可以拿到此刻的时间&#xff0c;该时间由两部分组成:从1970-01-01 00:00:00 开始走到此刻的总秒数不够1秒的纳秒数 Instant的常见方法&#xff1a; Instant可以用来记录代码的执行时间&#xff0c;或用于记录用户操作某…

利用Nginx正向代理实现局域网电脑访问外网

引言 在网络环境中&#xff0c;有时候我们需要让局域网内的电脑访问外网&#xff0c;但是由于网络策略或其他原因&#xff0c;直接访问外网是不可行的。这时候&#xff0c;可以借助 Nginx 来搭建一个正向代理服务器&#xff0c;实现局域网内电脑通过 Nginx 转发访问外网的需求。…

macbook使用Parallels Desktop虚拟机中使用外接拓展屏幕

macbook使用安装了windows虚拟机后&#xff0c;想让windows使用macbook外接的拓展屏&#xff0c;其实很简单&#xff0c;只需要在parallels desktop中点击全屏开启&#xff1a; 就可以在windows全屏模式下使用拓展屏幕了

Docker 镜像源配置

目录 一、 Docker 镜像源1.1 加速域名1.2 阿里云镜像源&#xff08;推荐&#xff09; 二、Docker 镜像源配置2.1 修改配置文件2.1.1 Docker Desktop 配置2.1.2 命令行配置 2.2 重启 Docker 服务2.2.1 Docker Desktop 重启2.2.2 命令行重启 2.3 检查是否配置成功 参考资料 一、 …

嘿!终于等到了!应用开发云资源套餐如约而至!

MemFire Cloud平台更新啦&#xff01;&#xff01;此次更新我们推出了万众期待的计费套餐&#xff0c;下面给大家带来详细的介绍~ 计费模式为“基础套餐按量付费”&#xff0c;您可选择购买带有一定配额的基础套餐&#xff0c;超出配额部分可以通过开启“超限按量”功能来转为…
最新文章