当前位置: 首页 > news >正文

PostgreSQL 变化数据捕捉(CDC)

PostgreSQL 变化数据捕捉(CDC)

基于CDC(变更数据捕捉)的增量数据集成总体步骤:

1.捕获源数据库中的更改数据

2.将变更的数据转换为您的消费者可以接受的格式

3.将数据发布到消费者或目标数据库

PostgreSQL支持触发器(trigger)和预写日志(WAL)两种CDC机制。

如果您希望 Postgres 数据更改发生时逐行流式传输,则需要逻辑解码或 Postgres 逻辑复制功能。

使用 Postgres 逻辑解码

逻辑解码是 PostgreSQL 的基于日志的 CDC(逻辑复制)的正式名称。逻辑解码使用 PostgreSQL 预写日志的内容来存储数据库中发生的所有活动。

step1:修改PostgreSQL数据库配置postgresql.conf文件

wal_level = logical

max_replication_slots = 10

max_wal_senders = 20

wal_level :设置为 logical,允许 WAL 日志记录逻辑解码所需的信息。

max_replication_slots :确保 max_replication_slots >= 使用 WAL 的 PostgreSQL 连接器的数量加上您的数据库使用的其他复制槽的数量。

max_wal_senders :指定 WAL 的最大并发连接数的参数,确保 max_wal_senders 至少是逻辑复制槽数的两倍。例如,如果您的数据库总共使用 10 个复制槽,则该 max_wal_senders 值必须为 20 或更大。

配置修改以后,需要重复 PostgreSQL 服务生效配置。

step2:为需要同步的数据库(db)创建逻辑复制插槽(replication slot)

关于复制 SQL 函数更多详情,可以参考官方文档(9.26.系统管理函数)介绍: http://www.postgres.cn/docs/12/functions-admin.html

函数:pg_create_logical_replication_slot(slot_name name, plugin name)

返回类型:(slot_name name, lsn pg_lsn)

说明:使用输出插件plugin创建一个名为 slot_name的新逻辑(解码)复制槽。

创建时需要指定逻辑复制插槽名称和输出插件:

SELECT pg_create_logical_replication_slot('replication_slot01', 'test_decoding'); -- 使用 test_decoding 输出插件

SELECT pg_create_logical_replication_slot('replication_slot01', 'pgoutput'); -- 使用 pgoutput 输出插件

注意权限:

如果用户没有权限可能会报错:ERROR: must be superuser or replication role to use replication slots

授权:

\c - postgres

ALTER ROLE test REPLICATION; --流复制权限

\du

验证插槽是否创建成功:

SELECT * FROM pg_replication_slots;

testdb01=> SELECT * FROM pg_replication_slots;

slot_name | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn

--------------------+---------------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------

replication_slot01 | test_decoding | logical | 16437 | testdb01 | f | f | | | 585 | 0/1807EA8 | 0/1807EE0

replication_slot02 | pgoutput | logical | 16437 | testdb01 | f | f | | | 585 | 0/1809CD8 | 0/1809D10

(2 rows)

testdb01=>

注意:

1)逻辑复制插槽名称:每个逻辑复制插槽都有一个名称,创建时需要指定名称,可以包含小写字母、数字和下划线

2)输出插件:创建逻辑复制插槽时需要指定输出插件,输出插件有:test_decoding、pgoutput、wal2json等

test_decoding 输出插件 :PostgreSQL 9.4+原生附带了 test_decoding 输出插件($PG_HOME/lib 目录下对应有 test_decoding.so),如果您的消费者支持 test_decoding ,则可以使用 test_decoding 输出插件。

pgoutput 输出插件 :PostgreSQL 10+原生附带了 pgoutput 输出插件($PG_HOME/lib 目录下对应有 pgoutput.so),如果您的消费者支持 pgoutput ,则可以使用 pgoutput 输出插件。pgoutput插件输出的是二进制的数据

wal2json 输出插件 :wal2json 是另一个流行的逻辑解码输出插件,PostgreSQL原生不携带、需要数据库服务单独安装插件才能使用。wal2json输出的是json格式的数据

step3:为数据库中的所有表或指定表创建一个发布。如果以指定表方式创建发布则后面可以在发布中管理(添加、删除)表

帮助说明:\h create publication

DROP PUBLICATION IF EXISTS pub01;

CREATE PUBLICATION pub01 FOR TABLE test01;

或者

DROP PUBLICATION IF EXISTS pub01;

CREATE PUBLICATION pub01 FOR TABLE test01, test02, test03;

或者

DROP PUBLICATION IF EXISTS pub01;

CREATE PUBLICATION pub01 FOR ALL TABLES;

查看创建发布的结果:

testdb01=> select * from pg_publication;

oid | pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate

-------+---------+----------+--------------+-----------+-----------+-----------+-------------

24629 | pub01 | 16436 | f | t | t | t | t

(1 row)

也可以执行查看:

testdb01=> \dRp

List of publications

Name | Owner | All tables | Inserts | Updates | Deletes | Truncates

-------+-------+------------+---------+---------+---------+-----------

pub01 | test | f | t | t | t | t

(1 row)

创建发布时,还可以选择在发布中包含哪些操作。例如,下面仅创建table01表 & 仅包含INSERT和UPDATE的发布:

CREATE PUBLICATION pub01 FOR TABLE table01 WITH (publish = 'INSERT, UPDATE');

注意:

非超级用户创建发布只能指定表创建、不能 FOR ALL TABLES 创建,否则报错:ERROR: must be superuser to create FOR ALL TABLES publication

FOR ALL TABLES 只允许超级用户创建发布,或者创建后由超级用户修改已创建的发布的配置开启foralltables。

testdb01=> \c - postgres

testdb01=# update pg_publication set puballtables=true where pubname is not null; -- 设置puballtables开关

testdb01=# select * from pg_publication;

oid | pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate

-------+---------+----------+--------------+-----------+-----------+-----------+-------------

24629 | pub01 | 16436 | t | t | t | t | t

(1 row)

step4:验证指定的表是否在发布中

testdb01=> SELECT * FROM pg_publication_tables WHERE pubname='pub01';

注意:创建发布不会开始复制。它只为未来的订阅者定义一个分组和过滤逻辑。

step5:查看逻辑复制效果:

获取结果的相关命令:

pg_logical_slot_get_changes : 查询并删除数据。仅在第一次返回结果,多次调用可能会返回空结果集,这意味着当get命令执行时,结果会被提供和删除,这增强了我们编写使用这些事件创建表副本的逻辑的能力。

函数:pg_logical_slot_get_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])

返回类型:(lsn pg_lsn, xid xid, data text)

说明:返回槽slot_name中的改变,从上一次已经被消费的点开始返回。 如果upto_lsn和upto_nchanges为 NULL,逻辑解码将一 直继续到 WAL 的末尾。如果upto_lsn为非 NULL,解码将只包括那些在指 定 LSN 之前提交的事务。如果upto_nchanges为非 NULL, 解码将在其产生的行数超过指定值后停止。不过要注意, 被返回的实际行数可能更大,因为对这个限制的检查只会在增加了解码每个新的提交事务产生 的行之后进行。

pg_logical_slot_get_binary_changes:变化数据以bytea返回。

pg_logical_slot_peek_changes : 只查询不删数据。多次调用每次都会返回相同的结果。是另一个 PostgreSQL 命令,用于在不使用 WAL 条目的情况下查看更改。

函数:pg_logical_slot_peek_changes(slot_name name, upto_lsn pg_lsn, upto_nchanges int, VARIADIC options text[])

返回类型:(lsn text, xid xid, data text)

说明:行为就像pg_logical_slot_get_changes()函数, 不过改变不会被消费, 即在未来的调用中还会返回这些改变。

pg_logical_slot_peek_binary_changes:变化数据以bytea返回。

关于复制 SQL 函数更多详情,可以参考官方文档(9.26.系统管理函数)介绍: http://www.postgres.cn/docs/12/functions-admin.html

测试表中I/D/U变更数据操作,然后查看逻辑复制效果:

testdb01=> SELECT * FROM pg_logical_slot_peek_changes('replication_slot01', NULL, NULL); -- 只查询不删数据

......

testdb01=> SELECT * FROM pg_logical_slot_get_changes('replication_slot01', NULL, NULL); -- 查询并删除数据

lsn | xid | data

-----------+-----+------------------------------------------------------------------------------------------

0/180A050 | 586 | BEGIN 586

0/180A050 | 586 | table public.test01: INSERT: id[bigint]:1 info[character varying]:'aa' cnt[integer]:123

0/180A160 | 586 | table public.test01: INSERT: id[bigint]:2 info[character varying]:'bb' cnt[integer]:456

0/180A1E8 | 586 | table public.test01: INSERT: id[bigint]:3 info[character varying]:'cc' cnt[integer]:55

0/180A2A0 | 586 | COMMIT 586

0/180A2A0 | 587 | BEGIN 587

0/180A2A0 | 587 | table public.test01: INSERT: id[bigint]:4 info[character varying]:'uuu' cnt[integer]:66

0/180A358 | 587 | COMMIT 587

0/180A358 | 588 | BEGIN 588

0/180A358 | 588 | table public.test01: DELETE: id[bigint]:2

0/180A3D0 | 588 | COMMIT 588

0/180A3D0 | 589 | BEGIN 589

0/180A3D0 | 589 | table public.test01: UPDATE: id[bigint]:3 info[character varying]:'xxxx' cnt[integer]:55

0/180A458 | 589 | COMMIT 589

(14 rows)

testdb01=> SELECT * FROM pg_logical_slot_get_changes('replication_slot01', NULL, NULL); -- 查询并删除数据

lsn | xid | data

-----+-----+------

(0 rows)

testdb01=>

pgoutput输出插件的结果查看,则执行:

testdb01=> SELECT * FROM pg_logical_slot_peek_binary_changes('replication_slot02', null, null, 'proto_version', '1', 'publication_names', 'pub01'); -- 查询但不删除数据

......

testdb01=> SELECT * FROM pg_logical_slot_get_binary_changes('replication_slot02', null, null, 'proto_version', '1', 'publication_names', 'pub01'); -- 查询并删除数据

lsn | xid | data

-----------+-----+------------------------------------------------------------------------------------------------------------------------------

0/1809DF8 | 585 | \x420000000001809fd0000296c5f556e57d00000249

0/1809DF8 | 585 | \x52000060417075626c696300746573743031006400030169640000000014ffffffff00696e666f00000004130000006800636e740000000017ffffffff

0/1809DF8 | 585 | \x44000060414b00037400000001316e6e

0/1809F40 | 585 | \x44000060414b00037400000001346e6e

0/1809F88 | 585 | \x44000060414b00037400000001336e6e

0/180A000 | 585 | \x43000000000001809fd0000000000180a000000296c5f556e57d

0/180A050 | 586 | \x42000000000180a270000296c5f7bb75300000024a

0/180A050 | 586 | \x49000060414e0003740000000131740000000261617400000003313233

0/180A160 | 586 | \x49000060414e0003740000000132740000000262627400000003343536

0/180A1E8 | 586 | \x49000060414e00037400000001337400000002636374000000023535

0/180A2A0 | 586 | \x4300000000000180a270000000000180a2a0000296c5f7bb7530

0/180A2A0 | 587 | \x42000000000180a328000296c5f7d8abe90000024b

0/180A2A0 | 587 | \x49000060414e0003740000000134740000000375757574000000023636

0/180A358 | 587 | \x4300000000000180a328000000000180a358000296c5f7d8abe9

0/180A358 | 588 | \x42000000000180a3a0000296c5f80ae6ca0000024c

0/180A358 | 588 | \x44000060414b00037400000001326e6e

0/180A3D0 | 588 | \x4300000000000180a3a0000000000180a3d0000296c5f80ae6ca

0/180A3D0 | 589 | \x42000000000180a428000296c5f826073e0000024d

0/180A3D0 | 589 | \x55000060414e000374000000013374000000047878787874000000023535

0/180A458 | 589 | \x4300000000000180a428000000000180a458000296c5f826073e

(20 rows)

testdb01=> SELECT * FROM pg_replication_slots;

slot_name | plugin | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn

--------------------+---------------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------

replication_slot01 | test_decoding | logical | 16437 | testdb01 | f | f | | | 590 | 0/180A540 | 0/180A578

replication_slot02 | pgoutput | logical | 16437 | testdb01 | f | f | | | 590 | 0/180A540 | 0/180A578

(2 rows)

PG logical replication plug-in ("pgoutput") 输出的消息格式详情可以参考PG官方说明:https://www.postgresql.org/docs/10/protocol-logicalrep-message-formats.html

step6:销毁逻辑复制插槽

SELECT pg_drop_replication_slot('replication_slot01');

使用复制插槽的注意事项:

1)每个插槽只有一个输出插件(创建插槽时由您来选择使用哪个)。

2)每个插槽仅提供来自一个数据库的更改。

3)一个数据库可以有多个插槽。

4)每个数据更改通常在每个插槽中发出一次。

5)但是当 Postgres 实例重新启动时,插槽可能会重新发出更改。消费者必须处理这种情况。

6)未使用的插槽对 Postgres 实例的可用性构成威胁。Postgres 将为这些未使用的更改保存所有 WAL 文件。这可能导致存储溢出。

关于 PostgreSQL WAL 消费者:

可以获取 Postgres 逻辑解码流的任何应用程序都是 PostgreSQL WAL 消费者。

pg_recvlogical:

pg_recvlogical 是一个 PostgreSQL 应用程序,他是 PostgreSQL 原生的逻辑解码工具,它可以管理槽并使用槽中的流。它包含在 Postgres 发行版中,因此它可能已经随 PostgreSQL 一起安装,在 $PG_HOME/bin 目录下。

pg_recvlogical 使用默认的 test_decoding 逻辑解码插件,pg安装完后会在pg安装目录的lib目录下创建test_decoding链接库文件。

pg_recvlogical 使用介绍:

创建逻辑复制插槽:

$ pg_recvlogical --create-slot -S replication_slot01 -d testdb01

启动复制槽解码(启动后会实时的将日志解码到制定的文件中,也可以不启动、等需要解码时再启动解码)

$ pg_recvlogical --start -S replication_slot01 -d testdb01 -f replication_slot01_decoding.log &

查看解码结果:

$ cat replication_slot01_decoding.log

查看数据库lsn:

postgres=# select pg_current_wal_lsn();

使用pg_recvlogical进行日志区间解码:

$ pg_recvlogical --start -S replication_slot01 -d testdb01 -I 4C/180B020 -E 4C/180B1EE -f ret.log

相关文章:

PostgreSQL 变化数据捕捉(CDC)

PostgreSQL 变化数据捕捉(CDC)基于CDC(变更数据捕捉)的增量数据集成总体步骤:1.捕获源数据库中的更改数据2.将变更的数据转换为您的消费者可以接受的格式3.将数据发布到消费者或目标数据库PostgreSQL支持触发器&#x…...

Spring 事务【隔离级别与传播机制】

Spring 事务【隔离级别与传播机制】🍎一.事务隔离级别🍒1.1 事务特性回顾🍒1.2 事务的隔离级别(5种)🍒1.3 事务隔离级别的设置🍎二.Spring 事务传播机制🍒2.1 Spring 事务传播机制的作用🍒2.2 事…...

HTTP和HTTPS协议

HTTP协议 HTTP协议是一种应用层的协议,全称为超文本传输协议。 URL URL值统一资源定位标志,也就是俗称的网址。 协议方案名 http://表示的就是协议方案名,常用的协议有HTTP协议、HTTPS协议、FTP协议等。HTTPS协议是以HTTP协议为基础&#…...

day3——有关java运算符的笔记

今天主要学习的内容有java的运算符 赋值运算符算数运算符关系运算符逻辑运算符位运算符(专门写一篇笔记)条件运算符运算符的优先级流程控制 赋值运算符 赋值运算符()主要用于给变量赋值,可以跟算数运算符相结合&…...

Git多人协同远程开发

1. 李四(项目负责人)操作步骤 在github中创建远程版本库testgit将基础代码上传⾄testgit远程库远程库中基于main分⽀创建dev分⽀将 githubleaflife/testgit 共享给组员李四继续在基础代码上添加⾃⼰负责的模块内容 2. 张三、王五(组员&…...

Chapter4:机器人仿真

ROS1{\rm ROS1}ROS1的基础及应用,基于古月的课,各位可以去看,基于hawkbot{\rm hawkbot}hawkbot机器人进行实际操作。 ROS{\rm ROS}ROS版本:ROS1{\rm ROS1}ROS1的Melodic{\rm Melodic}Melodic;实际机器人:Ha…...

python(14)--集合

前言 本篇文章学习的是 python 中集合的基础知识。 集合元素的内容是不可变的,常见的元素有整数、浮点数、字符串、元组等。至于可变内容列表、字典、集合等不可以是集合元素。虽然集合不可以是集合的元素,但是集合本身是可变的,可以去增加或…...

【Spark分布式内存计算框架——Spark Core】4. RDD函数(中)Transformation函数、Action函数

3.2 Transformation函数 在Spark中Transformation操作表示将一个RDD通过一系列操作变为另一个RDD的过程,这个操作可能是简单的加减操作,也可能是某个函数或某一系列函数。值得注意的是Transformation操作并不会触发真正的计算,只会建立RDD间…...

Mysql 数据类型

1、数值数据类型 1.1 整数类型(精确值) INTEGER, INT, SMALLINT, TINYINT, MEDIUMINT, BIGINT MySQL支持SQL标准的整数类型INTEGER (或INT)和SMALLINT。作为标准的扩展,MySQL还支持整数类型TINYINT、MEDIUMINT和BIGINT。下表显示了每种整数类型所需的存储和范围。…...

运行Whisper笔记(1)

最近chatGPT很火,就去逛了一下openai的github项目。发现了这个项目。 这个项目可以识别视频中的音频,转换出字幕。 带着一颗好奇的心就尝试自己去部署玩一玩 跟着这篇文章一步步来进行安装,并且跟着这篇文章解决途中遇到的问题。 途中还会遇…...

2023年最强大的12款数据可视化工具,值得收藏

做数据分析也有年头了,好的坏的工具都用过,推荐几个觉得很好用的,避坑必看! PS:一般比较成熟的公司里,数据分析工具不只是满足业务分析和报表制作,像我现在给我们公司选型BI工具,是做…...

LeetCode刷题系列 -- 523. 连续的子数组和

给你一个整数数组 nums 和一个整数 k ,编写一个函数来判断该数组是否含有同时满足下述条件的连续子数组:子数组大小 至少为 2 ,且子数组元素总和为 k 的倍数。如果存在,返回 true ;否则,返回 false 。如果存…...

LeetCode刷题系列 -- 525. 连续数组

给定一个二进制数组 nums , 找到含有相同数量的 0 和 1 的最长连续子数组,并返回该子数组的长度。示例 1:输入: nums [0,1]输出: 2说明: [0, 1] 是具有相同数量 0 和 1 的最长连续子数组。示例 2:输入: nums [0,1,0]输出: 2说明: [0, 1] (或 [1, 0]) 是具有相同数…...

JavaEE15-Spring Boot统一功能处理

目录 1.统一用户登录权限效验 1.1.最初用户登录验证 1.2.Spring AOP用户统一登录验证的问题 1.3.Spring拦截器 1.3.1.创建自定义拦截器,实现 HandlerInterceptor 接口并重写 preHandle(执行具体方法之前的预处理)方法 1.3.2.将自定义拦…...

centos7.6 设置防火墙

1、查看系统版本 cat /etc/redhat-release2、查看防火墙运行状态 systemctl status firewalld这里可以看到当前是未运行状态(inactive)。 3、关闭开机自启动防火墙 systemctl disable firewalld.service4、启动防火墙并查看状态,系统默认 22 端口是开启的。 sy…...

在线支付系列【22】微信支付实战篇之集成服务商API

有道无术,术尚可求,有术无道,止于术。 文章目录前言1. 环境搭建2. 特约商户进件3. 统一下单总结前言 在上篇文档中,我们做好了接入前准备工作,接下来使用开源框架集成服务商相关API。 一个简单的支付系统完成支付流程…...

3.2 埃尔米特转置

定义 对于复矩阵,转置又不一样,常见的操作是共轭转置,也叫埃尔米特转置Hermitian transpose。埃尔米特转置就是对矩阵先共轭,再转置,一般来说用三种符号表示埃尔米特转置: 第一种符号是AHA^HAH&#xff0c…...

Python爬虫之Scrapy框架系列(13)——实战ZH小说爬取数据入MySql数据库

目录:1 数据持久化存储,写入Mysql数据库①定义结构化字段:②重新编写爬虫文件:③编写管道文件:④辅助配置(修改settings.py文件):⑤navicat创库建表:⑥ 效果如下&#xf…...

MySQL篇02-三大范式,多表查询

数据入库时,由于数据设计不合理,会存在数据重复、更新插入异常等情况, 故数据库中表的设计遵循的设计规范:三大范式1.第一范式(1NF)要求数据库的每一列都是不可分割的原子数据项,即原子性。强调的是列的原子性,即数据库中每一列的…...

vue-cli3创建Vue项目

文章目录前言一、使用vue-cli3创建项目1.检查当前vue的版本2.下载并安装Vue-cli33.使用命令行创建项目二、关于配置前言 本文讲解了如何使用vue-cli3创建属于自己的Vue项目,如果本文对你有所帮助请三连支持博主,你的支持是我更新的动力。 下面案例可供…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言:多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

【配置 YOLOX 用于按目录分类的图片数据集】

现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

《基于Apache Flink的流处理》笔记

思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...