PostgreSQL 表数据迁移策略

背景 随着公司某项业务的快速发展,相关表的数据量激增。为了应对这一增长,我们需要将这些表的数据迁移到新的数据库,并实现平滑切换。本文将探讨几种可能的迁移策略,并分析每种方案的优缺点。 测试环境搭建 为了模拟和测试各种迁移策略,我们首先使用 Docker 搭建了一个本地测试环境,包含三个 PostgreSQL 实例: pgsql:源数据库 pgsql1:目标数据库 pgsql2:用于其他操作 services: pgsql: image: postgres:13 ports: - 5433:5432 environment: POSTGRES_PASSWORD: 123456 pgsql1: image: postgres:13 ports: - 5434:5432 environment: POSTGRES_PASSWORD: 123456 pgsql2: image: postgres:13 ports: - 5435:5432 environment: POSTGRES_PASSWORD: 123456 迁移策略 一、主从复制 主从复制是一种直观的数据迁移方法,可以确保数据的实时同步。 配置步骤: 修改主库(源数据库)配置: vim /var/lib/postgresql/data/pg_hba.conf # 添加从库IP的访问权限 host replication all 172.20.0.3/32 md5 重新加载主库配置: SELECT pg_reload_conf(); 配置从库(目标数据库): touch /var/lib/postgresql/data/standby.signal vim /var/lib/postgresql/data/postgresql.conf # 添加主库连接信息 primary_conninfo = 'host=pgsql port=5432 user=postgres password=123456' 备份主库数据到从库: pg_basebackup -h pgsql -U postgres -p 5432 -F p -X s -v -P -R -D /var/lib/postgresql/data 重启从库 优点: 实现全量数据同步,操作相对简单。...

2024-09-23 · 3 分钟

Navicat HTTP tunnel 加速

背景 在公司环境中,测试和生产数据库通常不允许直接外网访问。为了使用 Navicat 管理这些数据库,我们需要通过隧道连接。我们选择使用 HTTP 隧道,Navicat 安装目录下提供了相应的 PHP 隧道脚本,只需将其部署到可以连接数据库的服务器上即可。 然而,在实际使用过程中,我发现 HTTP 隧道会显著降低操作速度,严重影响工作效率。 问题分析 通过查看 PHP 隧道脚本的代码,我了解到其工作原理:Navicat 将 SQL 语句发送给 PHP 脚本,PHP 执行 SQL 并返回结果。这个中转过程会引入一些延迟,但实际体验中的延迟似乎过高。 为了深入分析这个问题,我决定使用 Wireshark 进行抓包。我设置了以下过滤器来捕获所有通过 HTTP 隧道的 Navicat 请求: http.request.full_uri == "http://your-domain.com/ntunnel_pgsql.php" and http.request 我执行了一系列常见操作: 打开数据库 查看一个表结构 查看一个表数据 按 id 倒序筛选数据 抓包结果显示,这些基本操作总共触发了 177 次请求。虽然每个请求的详细内容不方便都截图出来,但是通过观察每个请求的 Length,也可以发现存在大量重复请求。 解决方案 最初,我尝试在 Navicat 设置中寻找可以缓存这些数据的选项,以减少重复请求。但是没有找到相关设置。 因此,我决定开发一个本地运行的代理程序,用于缓存重复请求,从而减少网络请求次数,提高操作速度。 我使用 Go 语言开发了一个代理工具 navicat-http-tunnel-acceleration。它的使用非常简单: 克隆仓库到本地 在项目目录下运行 go run . --url http://your-domain.com/ntunnel_pgsql.php --port <your-port> 将 Navicat 的 HTTP 隧道设置成 http://127....

2024-09-14 · 1 分钟

使用ChatGPT辅助开发鼠标手势扩展

前言 我一直在使用的鼠标手势插件 SmartUp Gestures 最近被 Chrome 提示包含恶意软件,导致无法继续使用了。作为一个鼠标手势的重度使用者,我不仅在日常操作中频繁依赖它,有时甚至会在无聊时用鼠标手势画画。尝试了其他几个替代的手势插件后,我发现它们都不是很好用。因此,我决定自己开发一个新的鼠标手势插件。 学习插件开发 由于以前没有开发过 Chrome 插件,我决定先了解一下开发流程。在查找资料时,我发现了一篇名为《chrome 插件开发指南(Manifest V3)》的文章,这篇文章简明扼要地介绍了开发流程。同时,我在其中找到了一个推荐的插件开发模板chibat/chrome-extension-typescript-starter。 下载了这个模板项目,然后运行起来。在 Chrome 的扩展程序管理页面加载 dist 目录后,便能看到插件的效果了。 在阅读上述文章和模板代码后,我了解到 manifest.json 是插件的配置文件。接下来,将文件内容发送给 GPT,让其为我解释其中的各项配置。 结合模板代码进行调试后,我大致了解了各个页面的作用。 插件开发 绘制鼠标滑动轨迹 不太会写这些逻辑,还是先问一下 GPT,我一般写的 prompt 都很简单,然后在遇到问题时再进行调整。 将代码复制进去进行测试,发现可以成功绘制鼠标轨迹。不过,代码中仍存在一些 Bug,需要进一步调试和修改。 1. 第一个 Bug 是右键松开了还能绘制轨迹 猜测 Bug 原因是没有取消事件监听的原因。 问了 GPT 发现在乱说,经过进一步调试,发现不是这个原因。经过细致的调试,我发现 handleMousedown 中的代码在鼠标按下并放开之后才执行。 GPT 推荐我使用 mousedown 事件。更换事件监听后,这个 Bug 得到了解决。之后,我又自己添加了在鼠标右键按下并移动时阻止右键菜单弹出的逻辑。 2. 第二个 Bug 是鼠标左键也能绘制轨迹 加上是否是右键按下的判断也顺利解决。 3. 优化 鼠标绘制的轨迹不顺滑 GPT 推荐使用贝塞尔曲线来平滑轨迹。添加这段逻辑后,效果达到了预期。 4. 第三个 Bug 是页面滚动之后轨迹不在跟随鼠标 猜测问题出在元素定位上。由于我对 CSS 不太熟悉,写得也不多,所以又咨询了 GPT 🤣。 最后的代码实现: // 是否阻止右键菜单 let blockMenu: boolean = false; document....

2024-06-04 · 2 分钟

Arch Linux配置

检查网络 # 启动dhcp systemctl start dhcpcd # 设置开机启动 systemctl enable dhcpcd ping baidu.com # 检查设置系统时间 timedatectl timedatectl set-time "yyyy-MM-dd hh:mm:ss" # 升级系统中所有已安装的软件包 pacman -Syu 用户和用户组 # 创建用户 useradd -m chance # 设置密码 passwd chance # 安装sudo pacman -S sudo EDITOR=vim visudo # 找到下面一行取消注释 #%wheel ALL=(ALL:ALL) ALL # 将用户加入wheel组 gpasswd -a [用户名] [组名] SSH pacman -S openssh systemctl start sshd systemctl enable sshd # 查看ip pacman -S net-tools ifconfig 配置共享文件夹 pacman -S open-vm-tools # 虚拟机设置里设置共享文件夹 名称为 share # 查看设置的共享文件夹名字 为 share vmware-hgfsclient # 创建要挂载的共享文件夹 mkdir /mnt/hgfs # 挂载 vmhgfs-fuse ....

2023-09-19 · 4 分钟

Arch Linux安装

前面把开发环境切换到 WSL 了,但是在使用过程中遇到一个无法解决的 Bug microsoft/WSL#5118,因为我使用 symlink 也是挺频繁的,无法忍受这个 Bug,所以决定将开发环境迁移到虚拟机 😂,经过一番了解决定使用 Arch Linux,下面是虚拟机中安装 Arch Linux 的过程。 检查网络 ping baidu.com 检查时间是否正确 timedatectl 创建硬盘分区 # 用 sgdisk 将 MBR 分区表转换为 GPT sgdisk -g /dev/sda # 列出磁盘分区 找到要分区的磁盘 fdisk -l 分区方案 - Arch Linux 中文维基 /boot UEFI 系统需要有 EFI 系统分区。 # 分区 fdisk /dev/sda # 创建一个新分区 Command (m for help):n Partition number (1-128,default 1): First sector (34-209715166,default 2048): Last sector,+/-sectors or +/-sizefK,M,G,T,P}(2048-209715166,default 209713151):+512m Created a new partition 1 of type 'Linux filesystem'and of size 512 MiB....

2023-09-18 · 3 分钟

从Docker-Desktop迁移到wsl2

最近更新升级了最新版本的 Docker Desktop 4.20.0 ,然后发现了一个 bug #13524,然后降级一个版本之后又发现了另一个 bug #13477。决定寻找替代品,尝试了 Podman Desktop 之后放弃了,最终决定直接使用 wsl2。记录一下迁移以及踩坑过程。 WSL 遇到的无法解决的问题 microsoft/WSL#5118 导致无法使用 pnpm,只能使用 yarn、npm 替代。 修改源 使用中科大的软件源 https://mirrors.ustc.edu.cn/ # 备份源文件 $ sudo mv /etc/apt/sources.list /etc/apt/sources.list.backup # 切换 root 用户 $ sudo su # 写入中科大的源 $ echo "deb https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy main restricted universe multiverse deb https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse deb-src https://mirrors.ustc.edu.cn/ubuntu/ jammy-security main restricted universe multiverse deb https://mirrors....

2023-06-02 · 3 分钟

Xdebug使用Dbgp协议与PHPStorm通信过程

向 IDE 发起连接请求 <?xml version="1.0" encoding="ISO-8859-1"?> <init appid="1" idekey="PHPSTORM" language="PHP" protocol_version="1.0" fileuri="" xmlns="urn:debugger_protocol_v1"> <engine version="1.0.0"> <![CDATA[SDB]]> </engine> <author> <![CDATA[Chance]]> </author> </init> feature_set -i 1 -n show_hidden -v 1 feature_set:命令名称,用于设置调试器的功能。 -i 1:命令的唯一标识符,用于在调试器和 IDE 之间进行通信。 -n show_hidden:要设置的功能名称,即显示隐藏变量。 -v 1:功能的值,表示要显示隐藏变量。 回复: <?xml version="1.0" encoding="ISO-8859-1"?> <response xmlns="urn:debugger_protocol_v1" command="feature_set" transaction_id="1" feature="show_hidden" success="1"/> stdout -i 8 -c 1 stdout:命令名称,用于将输出发送到调试器的控制台。 -i 8:命令的唯一标识符,用于在调试器和 IDE 之间进行通信。 -c 1:输出的内容类型,表示输出的是文本内容。 回复: <?xml version="1.0" encoding="ISO-8859-1"?> <response xmlns="urn:debugger_protocol_v1" command="stdout" transaction_id="8" success="1"/> status -i 9 status:命令名称,用于查询调试器的状态。 -i 9:命令的唯一标识符,用于在调试器和 IDE 之间进行通信。 回复: <?...

2023-03-29 · 2 分钟

Casbin的Model详解与使用

简介 文档:https://casbin.org/docs/zh-CN/overview Casbin 是干什么的文档说的很详细,就不在重复说了。相信很多人在看完文档之后,仍然是不知道 model.conf 和 policy.csv 是什么,看不懂里面的内容,有一种无从下手的感觉。 知识是有一个知识壁垒的,在你打破这个壁垒之前,你觉得很难,查资料看文章也是云里雾里的。但是在打破壁垒之后,掌握了这个知识在回头看,感觉真的好简单。 我查看了好几篇讲 Casbin 的文章,讲的都不是很通俗易懂,对于掌握了 Casbin 的人来看没什么,但是对于还不会的人看了还是有些云里雾里。所以我放弃了去搜索查看别人的经验,转而去读官方文档,在反反复复读了几遍文档之后终于掌握了 Casbin。不得不说在去学习一个新东西的时候,最好的办法就是去查看官方文档。接下来我会以不会 Casbin 的人的角度来尽可能通俗易懂的讲一讲 Casbin。 Model 与 Policy 接下来我们以 Linux 的文件系统权限为例来讲解 model.conf 和 policy.csv 。 语法 文档:https://casbin.org/docs/zh-CN/syntax-for-models Request 定义 不管哪个语言的 Casbin 都会提供一个 enforce 函数方法来校验是否有权限,[request_definition] 部分就定义了 enforce 函数的参数。 [request_definition] r = sub, obj, act sub, obj, act 经典三元组: 访问实体 (Subject),访问资源 (Object) 和访问方法 (Action),是可以自定义的。表示 enforce 函数需要按此顺序传入三个参数。 在 Linux 文件系统中: sub:代表用户 obj:代表访问的文件或文件夹 act:代表读、写或执行权限 在 RESTful 风格的 API 中: sub:代表请求用户 obj:代表请求路由 例如:/api/user act:代表请求方法 GET、POST、PUT、DELETE Policy 定义 [policy_definition] 部分是对 policy 的定义,对策略权限的规则定义,可以自定义多个规则。...

2022-10-25 · 3 分钟

多个docker-compose项目之间通信与环境架构

在讲多个 docker-compose 项目之间通信之前,想要先说一说我从刚使用 Docker 到目前为止遇到的一些问题与想法。 在刚学习 Docker 的时候,我相信很多人都跟我一样有过这种想法:我们的代码运行环境、MySQL、Redis 等服务是放到一个容器里面呢,还是放到多个容器里面呢?经过一番学习知道,Docker 官方是推荐将这些环境放置到多个容器中的,至于为什么就不详细说了,可以自行百度学习一下。 容器通信 但是当放到多个容器中,又会出现一个问题,我的 API 服务该怎么访问 MySQL、Redis 呢?有三种办法可以解决。 通过容器内 ip + 映射出的端口进行访问(不推荐) 通过 --link 链接另一个容器访问(不推荐) --link 只能在 docker run 一个容器时链接另一个已运行的容器,所以说该方法只能单向访问。 通过 network 将两个容器链接到同一个 network 中可以进行双向访问 docker-compose 这么看来第三种方式是最好的解决方案,但是当我们容器越来越多时,需要记住所有容器的 run 指令,将一个个的容器 run 起来,然后链接到同一个 network 中去,操作起来非常的麻烦。所以我更推荐使用 docker-compose 来编排管理多个容器。下面来编写一个 docker-compose.yml 文件演示一下。 编写接口 因为我们只是来演示一下容器之间的通信,使用 MySQL、Nginx 等容器反而更加的麻烦,所以我们使用 Golang 简单写一个接口用来构建一个演示的镜像。 package main import ( "flag" "github.com/gin-gonic/gin" ) func main() { name := flag.String("name", "", "") flag.Parse() r := gin.Default() r....

2022-06-25 · 2 分钟

PHP百万数据导出Excel

背景 公司业务部门经常会需要导出各种数据进行分析,因为需要的数据模板多变,产品一直没有将其规划成系统功能,每次需要的数据都是手写 SQL 语句进行导出。在写了上万行 SQL 语句以后,我对这种体力劳动感觉到了厌烦,于是和领导申请,自己利用空闲时间规划开发了一个灵活配置、适用性强的导表系统。 使用技术 API 框架 Hyperf Web 框架 Ant Design Pro Excel 扩展 xlsWriter 优化 为了尽快实现功能,导出 Excel 功能是做的同步导出,但在我的设计中应该是异步导出,因此在后续的优化中引入了 RabbitMQ 消息队列实现异步导出功能。 因为不能将 Db 对象投递到消息队列中,所以投递过去的是 SQL 的预处理语句以及对应的变量,使用Db::select($sql, $bindings);执行 SQL 。然后尝试查询百万条数据,直接就内存溢出了。 vendor/hyperf/database/src/Connection.php 271 行 /** * Run a select statement against the database. */ public function select(string $query, array $bindings = [], bool $useReadPdo = true): array { return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) { if ($this->pretending()) { return []; } // For select statements, we'll simply execute the query and return an array // of the database result set....

2022-05-19 · 4 分钟