PgSQL - 17新特性 - 块级别增量备份
PgSQL - 17新特性 - 块级别增量备份
PgSQL可通过pg_basebackup进行全量备份。在构建复制关系时,创建备机时需要通过pg_basebackup全量拉取一个备份,形成一个mirror。但很多场景下,我们往往不需要进行全量备份/恢复,数据量特别大的时候,这个代价太大了。GPDB中有个工具gprecoverseg支持全量备份和增量备份。所谓全量备份,主要通过pg_basebackup从其他节点全量拷贝一份数据过来;而增量备份主要通过pg_rewind工具,只拷贝新增的数据。而PgSQL中单独的pg_rewind,仅从分叉点之前最近的checkpoint位置开始解析WAL,解析出变动的数据页,然后仅将变动的数据页拷贝过来。所以,仅靠pg_rewind实现不了完美的增量备份。
正在开发中的PgSQL17在pg_basebackup中新增了增量备份的功能。
1、使用方法
1.1 创建用例表及插入数据
=# CREATE TABLE just_for_fun (last_updated timestamptz);
=# INSERT INTO just_for_fun (last_updated) VALUES (now());
=# UPDATE just_for_fun SET last_updated = now();
1.2 执行pg_basebackup
=$ mkdir /var/tmp/backups; pg_basebackup -D /var/tmp/backups
=$ ls -l /var/tmp/backups/
total 360
-rw------- 1 pgdba pgdba 227 Jan 8 17:16 backup_label
-rw------- 1 pgdba pgdba 226076 Jan 8 17:16 backup_manifest
drwx------ 7 pgdba pgdba 4096 Jan 8 17:16 base/
…
-rw------- 1 pgdba pgdba 88 Jan 8 17:16 postgresql.auto.conf
-rw------- 1 pgdba pgdba 29806 Jan 8 17:16 postgresql.conf
相对于老版本的pg_basebackup多了backup_mainfest文件。该备份将PGDATA下的内容拷贝到/var/tmp/backups下。如果修改下冲突配置项,比如端口配置port,则可以通过pg_ctl -D /var/tmp/backups start直接启动。
当然,也可以备份成.tar文件:
=$ rm -rf /var/tmp/backups/; mkdir /var/tmp/backups; pg_basebackup -Ft -D /var/tmp/backups
=$ ls -l /var/tmp/backups/
total 56176
-rw------- 1 pgdba pgdba 226218 Jan 8 17:19 backup_manifest
-rw------- 1 pgdba pgdba 40509440 Jan 8 17:19 base.tar
-rw------- 1 pgdba pgdba 16778752 Jan 8 17:19 pg_wal.tar
1.3 backup_mainfest文件
=$ cat /var/tmp/backups/backup_manifest | head -n 10
{ "PostgreSQL-Backup-Manifest-Version": 1,
"Files": [
{ "Path": "backup_label", "Size": 227, "Last-Modified": "2024-01-08 16:21:14 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "f6db08ca" },
{ "Path": "tablespace_map", "Size": 0, "Last-Modified": "2024-01-08 16:21:14 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "00000000" },
{ "Path": "pg_xact/0000", "Size": 8192, "Last-Modified": "2024-01-08 16:21:13 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "c79e44f3" },
{ "Path": "PG_VERSION", "Size": 3, "Last-Modified": "2024-01-08 13:08:53 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "64440205" },
{ "Path": "pg_multixact/offsets/0000", "Size": 8192, "Last-Modified": "2024-01-08 13:09:02 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "23464490" },
{ "Path": "pg_multixact/members/0000", "Size": 8192, "Last-Modified": "2024-01-08 13:08:53 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "23464490" },
{ "Path": "conf.d/depesz.conf", "Size": 512, "Last-Modified": "2024-01-08 13:08:54 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "c6f171e0" },
{ "Path": "pg_ident.conf", "Size": 2640, "Last-Modified": "2024-01-08 13:08:53 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "0ce04d87" },
…
{ "Path": "base/5/2652", "Size": 16384, "Last-Modified": "2024-01-08 13:08:53 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "259eec8e" },
{ "Path": "pg_logical/replorigin_checkpoint", "Size": 8, "Last-Modified": "2024-01-08 16:21:13 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "c74b6748" },
{ "Path": "current_logfiles", "Size": 44, "Last-Modified": "2024-01-08 13:08:54 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "97357c1c" },
{ "Path": "log/postgresql-2024-01-08_140854.log", "Size": 1021834, "Last-Modified": "2024-01-08 16:21:14 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "d2498fb2" },
{ "Path": "global/pg_control", "Size": 8192, "Last-Modified": "2024-01-08 16:21:14 GMT", "Checksum-Algorithm": "CRC32C", "Checksum": "43872087" }
],
"WAL-Ranges": [
{ "Timeline": 1, "Start-LSN": "4/86000028", "End-LSN": "4/86000750" }
],
"Manifest-Checksum": "106517baea81404769cd4deb686ff58b58997308f0d90d9afbfa9d0111a5003d"}
这个文件可以用于校验备份是否完成,也可以用于看下自从上次备份以来改变了哪些东西。
1.4 做一个全量备份
=$ rm -rf /var/tmp/backups/; mkdir /var/tmp/backups/
=$ pg_basebackup -Ft -D "/var/tmp/backups/$( date +%Y-%m-%d_%H%M%S-FULL )"
=$ ls -l /var/tmp/backups/
total 4
drwx------ 2 pgdba pgdba 4096 Jan 8 17:39 2024-01-08_173902-FULL/
=$ ls -l /var/tmp/backups/2024-01-08_173902-FULL/
total 56356
-rw------- 1 pgdba pgdba 226219 Jan 8 17:39 backup_manifest
-rw------- 1 pgdba pgdba 40691712 Jan 8 17:39 base.tar
-rw------- 1 pgdba pgdba 16778752 Jan 8 17:39 pg_wal.tar
做增量备份,指定-i:
=$ pg_basebackup -i /var/tmp/backups/2024-01-08_173902-FULL/backup_manifest -Ft -D "/var/tmp/backups/$( date +%Y-%m-%d_%H%M%S-INCREMENTAL )"
pg_basebackup: error: could NOT initiate base backup: ERROR: incremental backups cannot be taken unless WAL summarization IS enabled
pg_basebackup: removing DATA directory "/var/tmp/backups/2024-01-08_173956-INCREMENTAL"
需要开启wal summarization:
$ ALTER system SET summarize_wal = ON;
$ SELECT pg_reload_conf();
1.5 wal_summarization
默认为off,开启后会启动一个wal summarizer进程,自动生成wal summarize信息;当然还需要wal_level>minimal才能开启。记录到一段WAL的内容中:文件大小的变化、哪些block发生变化、需要被更新或删除、lsn范围。每个summary文件包含的信息:
1)某一个TLI上的一个LSN范围
2)每个relation,包括:
a "limit block" which is 0(文件被创建或销毁) if a relation is created or destroyed withina certain range of WAL records
or otherwise the shortest length(文件缩小至某个值) to which the relation was truncated during that range of WAL records
or otherwise InvalidBlockNumber(无效块号).
In addition, it stores a list of blocks which have been modified during that range of WAL records, (被修改过的blocks id). but excluding blocks which were removed by truncation after they were modified and never subsequently modified again. (不记录被truncate并且后面没有被修改过的blocks id)
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=174c480508ac25568561443e6d4a82d5c1103487
Wal summarizer就是哪个LSN范围内的变动?2.1节进行讲述。
1.6 增量备份
=$ pg_basebackup -i /var/tmp/backups/2024-01-08_173902-FULL/backup_manifest -Ft -D "/var/tmp/backups/$( date +%Y-%m-%d_%H%M%S-INCREMENTAL )"
=$ ls -l /var/tmp/backups/
total 8
drwx------ 2 pgdba pgdba 4096 Jan 8 17:39 2024-01-08_173902-FULL/
drwx------ 2 pgdba pgdba 4096 Jan 8 17:40 2024-01-08_174043-INCREMENTAL/
=$ ls -l /var/tmp/backups/2024-01-08_174043-INCREMENTAL/
total 23860
-rw------- 1 pgdba pgdba 236528 Jan 8 17:40 backup_manifest
-rw------- 1 pgdba pgdba 7413248 Jan 8 17:40 base.tar
-rw------- 1 pgdba pgdba 16778752 Jan 8 17:40 pg_wal.tar
增量备份的base.tar只有7MB,而全量备份有40MB。
增量备份和全量备份中的backup_manifest中文件个数一样,增量备份有2中类型文件:
=$ jq .Files[13] /var/tmp/backups/2024-01-08_174043-INCREMENTAL/backup_manifest
{
"Path": "global/1214_fsm",
"Size": 24576,
"Last-Modified": "2024-01-08 16:10:41 GMT",
"Checksum-Algorithm": "CRC32C",
"Checksum": "722d586a"
}
以及
=$ jq .Files[12] /var/tmp/backups/2024-01-08_174043-INCREMENTAL/backup_manifest
{
"Path": "global/INCREMENTAL.2695",
"Size": 12,
"Last-Modified": "2024-01-08 13:08:53 GMT",
"Checksum-Algorithm": "CRC32C",
"Checksum": "e34c7d7c"
}
不以INCREMENTAL开头的文件是普通文件,不是增量的。否则需要拉取一些更早的备份。
1.7 增量备份的合并
=$ mkdir /var/tmp/backups/FULL
=$ tar -x -C /var/tmp/backups/FULL -f /var/tmp/backups/2024-01-08_173902-FULL/base.tar
=$ tar -x -C /var/tmp/backups/FULL/pg_wal/ -f /var/tmp/backups/2024-01-08_173902-FULL/pg_wal.tar
=$ cp /var/tmp/backups/2024-01-08_173902-FULL/backup_manifest /var/tmp/backups/FULL/
=$ mkdir /var/tmp/backups/INCR
=$ tar -x -C /var/tmp/backups/INCR -f /var/tmp/backups/2024-01-08_174043-INCREMENTAL/base.tar
=$ tar -x -C /var/tmp/backups/INCR/pg_wal -f /var/tmp/backups/2024-01-08_174043-INCREMENTAL/pg_wal.tar
=$ cp /var/tmp/backups/2024-01-08_174043-INCREMENTAL/backup_manifest /var/tmp/backups/INCR/
pg_combinebackup将一个全量备份+一个或多个增量备份合并为一个全新的全量备份:
=$ pg_combinebackup -o /var/tmp/backups/combined /var/tmp/backups/FULL /var/tmp/backups/INCR
2、内核原理
2.1 manifest中的WAL-ranges
1)WAL-ranges中的Timeline为备份前checkpoint时的时间线
2)WAL-ranges中的Start-LSN为备份前checkpoint的位置
3)WAL-ranges中的End-LSN备份后XLOG_BACKUP_END后的位置
详情可查询下面函数调用逻辑:
perform_base_backupdo_pg_backup_start(...);|-- RequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT | (fast ? CHECKPOINT_IMMEDIATE : 0));| state->startpoint = ControlFile->checkPointCopy.redo;|-- state->starttli = ControlFile->checkPointCopy.ThisTimeLineID;state.startptr = backup_state->startpoint;state.starttli = backup_state->starttli;...备份do_pg_backup_stop(backup_state, !opt->nowait);|-- state->stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);| state->stoptli = XLogCtl->InsertTimeLineID;| RequestXLogSwitch(false);|-- ...endptr = backup_state->stoppoint;endtli = backup_state->stoptli;//ckp时的redo点 -- 备份结束后XLOG_BACKUP_END位置AddWALInfoToBackupManifest(&manifest, state.startptr, state.starttli, endptr, endtli);|-- manifest 中End-LSN为endptr位置即backup end位置,Start-LSN为state.startptr位置即开始备份前ckp位置
2.2 wal Summarize中每条记录的是哪个WAL范围的数据变化?
其实,记录的是每个checkpoint周期的WAL中记录的变动的block等信息:
SummarizeWAL...while (1){//Now read the next record.record = XLogReadRecord(xlogreader, &errormsg);switch (XLogRecGetRmid(xlogreader)){case RM_SMGR_ID:SummarizeSmgrRecord(xlogreader, brtab);break;case RM_XACT_ID:SummarizeXactRecord(xlogreader, brtab);break;case RM_XLOG_ID:stop_requested = SummarizeXlogRecord(xlogreader);|-- info = XLogRecGetInfo(xlogreader) & ~XLR_INFO_MASK;| if (info == XLOG_CHECKPOINT_REDO || info == XLOG_CHECKPOINT_SHUTDOWN){| return true;| }|-- return false;break;default:break;}if (stop_requested && xlogreader->ReadRecPtr > summary_start_lsn){//遇到Checkpoint即停止解析summary_end_lsn = xlogreader->ReadRecPtr;//checkpointbreak;}解析得到更改的blockif (summary_end_lsn > summary_start_lsn){//summary文件名://tli(最老的未summarized的时间线)startlsn(最老的未summarized的wal)endlsn(ckp位置)snprintf(temp_path, MAXPGPATH,XLOGDIR "/summaries/temp.summary");snprintf(final_path, MAXPGPATH,XLOGDIR "/summaries/%08X%08X%08X%08X%08X.summary",tli,LSN_FORMAT_ARGS(summary_start_lsn),LSN_FORMAT_ARGS(summary_end_lsn));...将summary内容写入该文件}}
2.3 增量备份
1)pg_basebackup作为客户端通过GetConnection连接服务端,服务端会fork出wal sender进程与之交互
2)pg_basebackup通过RetrieveWalSegSize向wal sender进程发送“SHOW wal_segment_size”,wal sender通过exec_replication_command处理发过来的命令。GetPGVariable获取到wal_segment_size大小,并发送回去:直到ReadyForQuery的pq_flush才将内容发送过去
3)接着pg_basebackup就进入了BaseBackup函数中
4)RunIdentifySystem向wal sender发送IDENTIFY_SYSTEM,wal sender通过IdentifySystem函数获取到系统标记systemid、时间线timeline等发送回去
5)然后进入增量备份相关步骤:PQsendQuery向wal sender发送UPLOAD_MANIFEST命令,wal sender通过UploadManifest进行处理,先发送PGRES_COPY_IN,pg_basebackup接收到后,读取指定的backup_manifest并将它发送给wal sender;wal sender通过HandleUploadManifestPacket放到IncrementalBackupInfo::buf中,直到pg_basebackup发来EOF ‘c’表示发送完。
6)wal sender解析出WAL Ranges内容,也就是得到备份前checkpoint位置
7)pg_basebackup发送BASE_BACKUP命令发起增量备份
8)wal sender通过SendBaseBackup从backup_mainifest解析的checkpoint位置开始(因为checkpoint前的内容都是已备份过的)找到需要的wal summary文件,根据其文件名(tli+ start lsn+ckp )找到需要增量的summary文件(记录的是本次增量备份需要的变动block列表等信息),根据summary文件中的内容,将本次增量备份内容发送回去
3、总结
1)wal Summarize进程通过解析每个checkpoint周期内的WAL日志,将变更信息记录到summary文件中
2)每次备份(全量备份或增量备份)都会生成一个manifest文件,文件中WAL-ranges部分会记录下备份前执行的checkpoint的WAL位置
3)通过manifest中记录的checkpoint位置就可以判断哪个summary文件是上次备份结束,本次增量备份开始的地方
4)遍历summary文件,得到增量变更,然后将变更页发送到pg_basebackup,由pg_basebackup写到指定位置,完成增量备份。
5)增量备份的完成,需要借助wal summary进程,该进程会读取WAL日志并解析,记录到变更,这个IO等代价需要考虑到业务中
参考
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=dc212340058b4e7ecfc5a7a81ec50e7a207bf288
https://www.depesz.com/2024/01/08/waiting-for-postgresql-17-add-support-for-incremental-backup/
相关文章:

PgSQL - 17新特性 - 块级别增量备份
PgSQL - 17新特性 - 块级别增量备份 PgSQL可通过pg_basebackup进行全量备份。在构建复制关系时,创建备机时需要通过pg_basebackup全量拉取一个备份,形成一个mirror。但很多场景下,我们往往不需要进行全量备份/恢复,数据量特别大的…...
Vue3setup()的非语法糖和语法糖的用法
1、setup()的语法糖的用法 script标签上写setup属性,不需要export default {} setup() 都可以省 创建每个属性或方法时也不需要return 导入某个组件时也不需要注册 <script setup > // script标签上写setup属性,不需要export default {} set…...
HTTP状态信息
1xx: 信息 消息:描述:100 Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。101 Switching Protocols服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。 2xx: 成功 消息:描述:200…...

CSS之边框样式
让我为大家介绍一下边框样式吧!如果大家想更进一步了解边框的使用,可以阅读这一篇文章:CSS边框border 属性描述none没有边框,即忽略所有边框的宽度(默认值)solid边框为单实线dashed边框为虚线dotted边框为点线double边框为双实线 代码演示&…...

k8s-helm
Helm: 什么是helm,在没有这个heml之前,deployment service ingress的作用就是通过打包的方式,把deployment service ingress这些打包在一块,一键式的部署服务,类似于yum 官方提供的一个类似于安全仓库的功能,可以实现…...

黑马程序员JavaWeb开发|Maven高级
一、分模块设计与开发 分模块设计: 将项目按照功能拆分成若干个子模块,方便项目的管理维护、扩展,也方便模块间的相互调用,资源共享。 注意:分模块开发需要先对模块功能进行设计,再进行编码。不会先将工…...

【经验分享】MAC系统安装R和Rstudio(保姆级教程)安装下载只需5min
最近换了Macbook的Air电脑,自然要换很多新软件啦,首先需要安装的就是R和Rstudio啦,网上的教程很多很繁琐,为此我特意总结了最简单实用的安装方式: 一、先R后Rstudio 二、R下载 下载网址:https://cran.r-project.org …...

探索设计模式的魅力:“感受单例模式的力量与神秘” - 掌握编程的王牌技巧
在软件开发的赛场上,单例模式以其独特的魅力长期占据着重要的地位。作为设计模式中的一员,它在整个软件工程的棋盘上扮演着关键性角色。本文将带你深入探索单例模式的神秘面纱,从历史渊源到现代应用,从基础实现到高级技巧…...

SpringCloud Aliba-Seata【上】-从入门到学废【7】
目录 🧂.Seata是什么 🌭2.Seata术语表 🥓3.处理过程 🧈4.下载 🍿5.修改相关配置 🥞6.启动seata 1.Seata是什么 Seata是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能…...

C# Cad2016二次开发选择csv导入信息(七)
//选择csv导入信息 [CommandMethod("setdata")] //本程序在AutoCAD的快捷命令是"DLLLOAD" public void setdata() {Microsoft.Win32.OpenFileDialog dlg new Microsoft.Win32.OpenFileDialog();dlg.DefaultExt ".csv";// Display OpenFileDial…...

[陇剑杯 2021]日志分析
[陇剑杯 2021]日志分析 题目做法及思路解析(个人分享) 问一:单位某应用程序被攻击,请分析日志,进行作答: 网络存在源码泄漏,源码文件名是_____________。(请提交带有文件后缀的文件名&…...

Java面试汇总——jvm篇
目录 JVM的组成: 1、JVM 概述(⭐⭐⭐⭐) 1.1 JVM是什么? 1.2 JVM由哪些部分组成,运行流程是什么? 2、什么是程序计数器?(⭐⭐⭐⭐) 3、介绍一下Java的堆(⭐⭐⭐⭐) 4、虚拟机栈(⭐⭐⭐⭐) 4.1 什么是虚拟机栈&…...

数据结构:完全二叉树(递归实现)
如果完全二叉树的深度为h,那么除了第h层外,其他层的节点个数都是满的,第h层的节点都靠左排列。 完全二叉树的编号方法是从上到下,从左到右,根节点为1号节点,设完全二叉树的节点数为sum,某节点编…...

RK3568 移植Ubuntu
使用ubuntu-base构建根文件系统 1、到ubuntu官网获取 ubuntu-base-18.04.5-base-arm64.tar.gz Ubuntu Base 18.04.5 LTS (Bionic Beaver) 2、将获取的文件拷贝到ubuntu虚拟机,新建目录,并解压 mkdir ubuntu_rootfs sudo tar -xpf u...

C++大学教程(第九版)6.34猜数字游戏 6.35 修改的猜数字游戏
文章目录 6.34题目代码运行截图6.35题目代码运行截图 6.34题目 猜数字游戏)编写一个程序,可以玩“猜数字”的游戏。具体描述如下:程序在1~1000之间的整数中随机选择需要被猜的数,然后显示: 代码 #include <iostream> #include <cstdlib>…...

【立创EDA-PCB设计基础】5.布线设计规则设置
前言:本文详解布线前的设计规则设置。经过本专栏中的【立创EDA-PCB设计基础】前几节已经完成了布局,接下来开始进行布线,在布线之前,要设置设计规则。 目录 1.间距设置 1.1 安全间距设置 1.2 其它间距设置 2.物理设置 2.1 导…...
ElementUI简介以及相关操作
ElementUI是一套基于Vue.js的桌面端组件库,提供了丰富的组件帮助开发人员快速构建功能强大、风格统一的页面。以下是ElementUI的简介以及相关操作: 简介:ElementUI是一套为开发者、设计师和产品经理准备的基于Vue 2.0的桌面端组件库ÿ…...

内存耗尽排查思路
内存耗尽排查思路 – WhiteNights Site 标签:日志 内存间断性耗尽问题的排查思路 先简单说下背景。排查了两天给我整麻了。 找了个镜像模板做虚拟机。但是发现只要一开机,内存每隔几秒就会被耗尽。看内存的波形图就和坐过山车一样,一会占…...

OpenCV书签 #差值哈希算法的原理与相似图片搜索实验
1. 介绍 差值哈希算法(Difference Hash Algorithm,简称dHash) 是哈希算法的一种,主要可以用来做以图搜索/相似图片的搜索工作。 2. 原理 差值哈希算法通过计算相邻像素的差异来生成哈希,即通过缩小图像的每个像素与平…...

Unity中URP下获取主灯信息
文章目录 前言一、计算BulinnPhone的函数有两个重载1、 目前最新使用的是该方法(这是我们之后主要分析的函数)2、 被淘汰的老方法,需要传入一堆数据 二、GetMainLight1、Light结构体2、GetMainLight具有4个方法重载3、1号重载干了什么&#x…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...

LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...

边缘计算网关提升水产养殖尾水处理的远程运维效率
一、项目背景 随着水产养殖行业的快速发展,养殖尾水的处理成为了一个亟待解决的环保问题。传统的尾水处理方式不仅效率低下,而且难以实现精准监控和管理。为了提升尾水处理的效果和效率,同时降低人力成本,某大型水产养殖企业决定…...
Docker、Wsl 打包迁移环境
电脑需要开启wsl2 可以使用wsl -v 查看当前的版本 wsl -v WSL 版本: 2.2.4.0 内核版本: 5.15.153.1-2 WSLg 版本: 1.0.61 MSRDC 版本: 1.2.5326 Direct3D 版本: 1.611.1-81528511 DXCore 版本: 10.0.2609…...