2023年10月

不论是在团队写作还是在个人工作中,PDF 文档往往会经过多次修订和更新。掌握 PDF 文档内容的变化对于管理文档有极大的帮助。通过对比 PDF 文档,用户可以快速找出文档增加、删除和修改的内容,更好地了解文档的演变过程,轻松地管理文档。本文将介绍如何
在 Java 程序中通过代码快速比较两个 PDF 文档并找出文档之间的内容差异

本文所使用的方法需要用到
Spire.PDF for Java
库,可点击
下载
后再手动将 Spire.Pdf.jar 引入程序中。

使用 Java 对比整个 PDF 文档

对比文档之前需要先将两个文档作为参数传递到
PdfComparer
类的构造函数创建对象,然后再使用
PdfComparer.compare(String fileName)
方法对比这两个 PDF 文档并将对比结果保存到第三个 PDF 文档。 对比结果文档将分两栏展示原文档,增加部分显示在左侧,删除部位显示在右侧。 步骤和代码如下:

  • 创建两个
    PdfDocument
    类的对象。
  • 使用
    PdfDocument.loadFromFile()
    方法加载两个 PDF 文档。
  • 创建
    PdfComparer
    类的对象。
  • 使用
    PdfComparer.compare()
    方法比较两个文档,并将结果保存为新的 PDF 文档。
importcom.spire.pdf.PdfDocument;importcom.spire.pdf.comparison.PdfComparer;public classComparePDF {public static voidmain(String[] args) {//创建PdfDocument对象并加载第一个PDF文档
        PdfDocument pdf1 = newPdfDocument();
pdf1.loadFromFile(
"文件1.pdf");//创建另一个PdfDocument对象并加载另一个PDF文档 PdfDocument pdf2 = newPdfDocument();
pdf2.loadFromFile(
"文件2.pdf");//创建PdfComparer对象 PdfComparer comparer = newPdfComparer(pdf1, pdf2);//比较两个PDF文档并将比较结果保存到新文档中 comparer.compare("比较1.pdf");
}
}

比较结果:

使用 Java 对比 PDF 文档的指定页面

初始化
PdfComparer
之后,还可以使用
PdfComparer.getOptions().setPageRanges()
方法限制用于对比的 PDF 页面范围。步骤和代码如下:

  • 创建两个
    PdfDocument
    类的对象。
  • 使用
    PdfDocument.loadFromFile()
    方法加载两个 PDF 文档。
  • 创建
    PdfComparer
    类的对象。
  • 使用
    PdfComparer.getOptions().setPageRanges()
    方法设置要对比的页面范围。
  • 使用
    PdfComparer.compare()
    方法比较两个文档,并将结果保存为新的 PDF 文档。
importcom.spire.pdf.PdfDocument;importcom.spire.pdf.comparison.PdfComparer;public classComparePDFPageRange {public static voidmain(String[] args) {//创建PdfDocument对象并加载第一个PDF文档
        PdfDocument pdf1 = newPdfDocument();
pdf1.loadFromFile(
"文件1.pdf");//创建另一个PdfDocument对象并加载另一个PDF文档 PdfDocument pdf2 = newPdfDocument();
pdf2.loadFromFile(
"文件2.pdf");//创建PdfComparer对象 PdfComparer comparer = newPdfComparer(pdf1, pdf2);//设置要比较的页面范围 comparer.getOptions().setPageRanges(1, 1, 1, 1);//比较两个PDF文档并将比较结果保存到新文档中 comparer.compare("比较2.pdf");
}
}

比较结果

以上示例可以看出用 Spire.PDF for Java 对比 PDF 文档的操作十分简单,仅需几行代码就能快速找出文档之间的差异。要了解该Java库支持的其他功能,可前往
Spire.PDF for Java 教程
查看。

一、前言

最近项目系统做安全加固,以前是本地备份,现在需要做远程内网服务器数据库备份,后期也有可能做异地备份。下面以SQL Server2016 内网服务器数据库备份为例,
数据库服务器地址:192.168.10.200
备份服务器地址:192.168.10.100

二、创建存储文件夹192.168.10.100

远程100服务器,创建文件夹(DataBak)

右击文件夹属性,选择共享,网络文件和文件夹共享、点共享,确定

设置完共享文件夹后,一定要记得设置密码保护

设置如下两个为启用状态,其余为关闭状态

三、设置数据库服务器192.168.10.200

使用cmd创建网络磁盘映射

net use Z: \\192.168.10.100\DataBak /user:administrator password

查看已创建映射

net use

四、创建维护计划

右击管理,

使用维护计划向导进行备份任务管理

下一步

取个名字,点下一步

选择备份数据库和清除任务,点下一步

点下一步

选择要备份的指定数据库,或者全部数据库,点击目标

填写备份文件存储路径这里选择远程地址目录,点下一步,维护指定通知信息点击确定即可

双击计划添加执行计划,录入计划名称点击维护计划信息

点击确定整个备份计划就完成了

张良计诉园子侵权案(用户转载张良计公众号文章)一审结束了,案子很简单,庭审过程也很简单,向大家汇报一下最新进展。

一审的结果是以下2个关键点将决定法官如何判决:
1)原告(张良计)没有提供证据证明转载文章给原告造成的实际损失
2)被告(园子)缺少足够的证据证明转载文章是用户而不是平台自己发布的

对于第1点,原告不提供证据,由法官判断应该赔偿多少合理。

对于第2点,法官让我们在7天内提供用户的实名信息(身份证信息或者手机号)证明转载文章是用户发布的。如果能提供,平台就没有责任;如果不能提供,由平台承担侵权责任。

原告是律师出庭,轻车熟路,胸有成竹,胜券在握,在起诉之前就知道我们最大的弱点。

我们的弱点是转载文章的用户是在园子启用实名注册(手机号验证)之前注册的账号,注册时没有绑定手机号,后来我们虽然在博客后台针对没有绑定手机号的用户进行提示,但没有强制绑定,这位用户依然没有绑定。

另外一场还未开庭的诉讼也是这个情况,这位律师厉害之处就在于在发起这2个起诉之前就知道两位转载文章的用户没有进行完成实名验证。

目前我们只有这位用户的邮箱、IP地址与姓名信息,没有身份证号码,也没有手机号,现在园子胜诉只有2个可能:
1)通过这位用户的qq邮箱证明其身份
2)找到这位用户的手机号或者身份证号码

这位用户今年登录过2次,分别是2023年3月(原告取证时间是2023年2月,起诉时间是2023年5月)与2023年8月(立案时间是2023年9月),我们之前尝试邮件联系这位用户,但这位用户没有回复。

接下来就是法官根据我们提供的证据进行宣判,或者我们和原告进行调解。

(这篇博文在上海回杭州的高铁上匆匆完成)

大家好,我是阳哥。
专注Go语言的学习经验分享和就业辅导。

之前分享了很多 Golang 后端的大厂面经,不少同学在催更新,这篇给大家继续安排。

本文来自一位同学的投稿,面试深X服的面经汇总,前半部分主要是Go语言相关,后半部分也涉及微服务和Redis。

Slice扩容

  1. slice切片扩容机制?为什么不一直用2倍扩容?

go1.18版本之后,slice扩容使用了更加平滑的方式,不再使用1024作为临界点,而是使用threshold作为临界点(threshold 设定为256)。
当slice容量 < threshold 时,每次扩容为原来的两倍。

当slice容量 > threshold 时,每次
增加
(oldcap + 3
threshold)
3/4的容量。

如果每次扩容都用两倍扩容,那这是一个2的指数级增加,后面扩容会扩非常大,浪费很大的空间。

内存分配

  1. Go内存分配机制?多级缓存?组件?(mspan/mcache/mcentral...)

Go 内存会自己管理,释放的内存不会立马归还给操作系统,而是尽量对内存进行复用,减少和操作系统的沟通(涉及到内核态和用户态的转换,消耗较大)。

Go内存管理非常高效,每一个线程M独享一个mcache,在申请内存时优先从mcache上获取,如果mcache没有足够内存,则向mcentral获取内存到mcache中,如果mcentral中也没有足够的内存则向mheap申请,如果mheap也没有足够的内存的话就向操作系统申请了(mcache->mcentral->mheap->OS)。

高效体现在从mcache获取内存和释放都是无锁的,速度很快,向mcentral和mheap申请内存虽然需要竞争锁,但是mcentral 和mheap通过 span class 进行分类(设置桶锁而不是一把大锁),锁的粒度更小,申请内存时都是通过span class 找到对应的桶获取内存,锁竞争会减少很多,性能也得到了提升。

Go垃圾回收 GC原理

  1. go垃圾回收?三色标记法?读写屏障?STW?

go1.18之后垃圾回收采用三色标记法和混合写屏障。

三色标记法:

将对象标记为白色,灰色或黑色。

白色:不确定对象(默认色);黑色:存活对象。灰色:存活对象,子对象待处理。

标记开始时,先将所有对象加入白色集合(需要STW)。首先将根对象标记为灰色,然后将一个对象从灰色集合取出,遍历其子对象,放入灰色集合。同时将取出的对象放入黑色集合,直到灰色集合为空。最后的白色集合对象就是需要清理的对象。

这种方法有一个缺陷,如果对象的引用被用户修改了,那么之前的标记就无效了。因此Go采用了写屏障技术,当对象新增或者更新会将其着色为灰色。
一次完整的GC分为四个阶段:

  1. 准备标记(需要STW),开启写屏障。
  2. 开始标记
  3. 标记结束(STW),关闭写屏障
  4. 清理(并发)

基于插入写屏障和删除写屏障在结束时需要STW来重新扫描栈,带来性能瓶颈。混合写屏障分为以下四步:

  1. GC开始时,将栈上的全部对象标记为黑色(不需要二次扫描,无需STW);
  2. GC期间,任何栈上创建的新对象均为黑色
  3. 被删除引用的对象标记为灰色
  4. 被添加引用的对象标记为灰色

总而言之就是确保黑色对象不能引用白色对象,这个改进直接使得GC时间从 2s降低到2us。

CSP并发模型

  1. channel使用场景?CSP思想?优势?

在并发编程中,两个goroutine之间使用channel进行通信。

CSP思想是指通过通信来共享内存,而不是通过共享内存来实现通信。

CSP思想优势是使用非常灵活,缺点是容易造成死锁。

互斥锁和读写锁

  1. go的锁有哪些?互斥锁的读写锁的区别?

互斥锁和读写锁。

互斥锁是有一个goroutine获取到互斥锁后,其他goroutine必须要等待锁被释放才能获取到锁。

而读写锁是可以允许多个goroutine获取到读锁执行读操作,但是写锁被获取了就和互斥锁一样了,其他goroutine必须等待写锁释放才能获取到读锁或者写锁。

sync

  1. sync包还有什么?sync.map介绍一下?如何保证并发安全?

sync.waitgroup, sync.map ,sync.Lock, sync.RWLock,sync.Pool....

sync.map 底层有两个map,一个是read,一个是dirty, 在读操作时优先在read中查找,read没有就去dirty中查找,写操作时如果read中有则利用CAS机制尝试更新value值,read中没有则写入 dirty 中。在 misses >= len(dirty)时,同步read 和 dirty的数据。

在read中不需要加锁,在dirty中需要加锁

sync.map通过read和dirty实现读写分离来减少锁时间来提高并发效率

协程池

  1. sync.Pool?了解线程池吗?优势亮点?未工作的线程如何等待?协程池?

sync.Pool 其实就是一个
线程安全

对象池
,用于
保存

复用临时
对象,在大批量申请和释放相同类型的临时对象时使用 sync.Pool 可以
减少很多内存分配和回收操作

减小GC压力

线程池是一种并发编程的技术,可以
管理

复用
线程,提供一种高效的方式来处理并发。

线程池优势亮点:

  1. 提高性能,线程池通过复用线程,减少线程频繁地创建和销毁,避免线程的频繁创建和销毁的开销。
  2. 控制并发度,线程池可以控制并发任务的数量,通过设计线程池的大小来控制并发度,避免激烈的锁竞争导致系统性能的下降。
  3. 提高响应速度, 线程池可以提前创建好线程,在任务到来时可以立即处理任务,提高系统的响应速度。
  4. 资源管理,线程池可以更好的对线程进行调度和管理,避免线程资源的浪费。

未工作的线程可以通过以下方式等待:

  1. 使用条件变量:线程可以使用条件变量来等待某个条件的发生。当线程需要等待时,它可以调用条件变量的等待函数,将自己置于等待状态,直到条件满足时被唤醒。
  2. 使用信号量:线程可以使用信号量来进行等待操作。线程在需要等待时,可以调用信号量的等待操作,将自己阻塞,直到其他线程释放信号量时被唤醒。
  3. 使用锁和条件变量组合:线程可以使用锁和条件变量的组合来实现等待操作。线程在需要等待时,可以先获取锁,然后检查条件是否满足,如果条件不满足,则调用条件变量的等待函数将自己置于等待状态,直到条件满足时被唤醒。

协程池和线程池基本差不多,可以更好地管理和复用协程,提高系统的性能和资源利用率。

协程泄露

  1. 防止Go协程泄露/未关闭?(waitgroup,context)

通过管道channel通知关闭,使用waitgroup监控协程全部退出,使用contex上下文来设置超时或者手动cancle关闭协程

select的用法?执行顺序?

select{
 case <-ctx.Done():
return
 case <-
 default:
}

执行顺序是随机的

map

  1. 什么可以作为map的键?结构体可以吗?

实现了 == 操作的可以比较的就可以作为map的键(基本数据类型、数组等),结构体中如果所有字段都可以比较那么久可以作为键,否则不行。

微服务

  1. 微服务之间通信方式 rpc、grpc、http等

介绍一下grpc?protobuf/json区别与优势?

GRPC是一种高性能、开源的远程过程调用(RPC)框架,由Google开发并基于HTTP/2协议实现。它允许在不同的计算机之间进行跨语言和跨平台的通信,使得构建分布式系统变得更加简单和高效。

GRPC使用Protocol Buffers(简称Protobuf)作为默认的序列化机制,而不是使用JSON。

Protobuf是一种轻量级的数据交换格式,具有以下优势:

  1. 效率高:Protobuf使用二进制编码,相比于文本格式的JSON,它的编码和解码速度更快,传输的数据量更小,节省了带宽和存储空间。
  2. 可读性好:虽然Protobuf是二进制格式,但它的定义文件是可读的,易于理解和维护。相比之下,JSON是一种文本格式,可读性较好,但在大型数据结构的情况下,Protobuf的定义文件更加清晰和简洁。
  3. 跨语言支持:Protobuf支持多种编程语言,包括Java、C++、Python等,这使得在不同语言之间进行通信变得更加方便。
  4. 版本兼容性:Protobuf支持向后和向前兼容的数据格式演化,这意味着可以在不破坏现有客户端和服务端的情况下,对数据结构进行扩展和修改。

相比之下,JSON是一种常用的文本格式,具有以下特点:

  1. 可读性好:JSON使用文本格式,易于阅读和理解,对于调试和开发过程中的数据交换非常方便。
  2. 平台无关性:JSON是一种独立于编程语言的数据格式,几乎所有的编程语言都支持JSON的解析和生成。
  3. 灵活性:JSON支持动态的数据结构,可以轻松地添加、删除和修改字段,适用于一些需要频繁变动的数据。

总的来说,GRPC使用Protobuf作为默认的序列化机制,相比于JSON,Protobuf在性能、可读性和跨语言支持方面具有优势。然而,选择使用GRPC还是JSON取决于具体的应用场景和需求。

介绍一下Gorm优势?

简单易用、支持多种数据库、自动迁移、支持事务、

Redis

1.常见数据结构

答:(字符串、set、zset、hash、list...)
hash和zset底层使用 skiplist 和 ziplist,同时满足两个条件时使用 ziplist,否则 skiplist

  1. 集合元素个数小于redis.conf 中 zset-max-ziplist-entries 属性的值,其默认值为128
  2. 每个集合元素大小都小于 redis.conf 中 zset-max-ziplist-value 属性的值,其默认值为 64 字节

list 底层使用了快表(quicklist),quickList 本质上是 zipList 和 linkedList 的混合体。其将 linkedList 按段切分,每一段使用 zipList 来紧凑存储若干真正的数据元素,多个 zipList 之间使用双向指针串接起来。对于每个zipList 中最多可存放多大容量的数据元素,在配置文件中通过 list-max-ziplist-size 属性可以指定。

2.zset介绍一下?zset底层借助那些数据结构实现?跳表介绍一下?查找过程?复杂度?

zset是redis中的有序集合,借助了跳表和哈希表(哈希表存储成员和对应的分数)实现有序集合,跳表是一种有序的链表结构,它通过在每个节点中维护多个指针,使得在查找和插入操作时可以跳过部分节点,从而提高了查找的效率。跳表的高度是通过随机函数决定的,因此在平均情况下,查找的时间复杂度为O(log n),其中n是跳表中的节点数量。

3.set介绍?

set 底层使用哈希表。

4.压缩列表介绍?

压缩列表的设计目标是在尽可能节省内存的同时,提供高效的插入、删除和访问操作。它通过使用连续的内存块来存储数据,减少了指针和额外的元数据开销,从而节省了内存空间。

5.渐进式rehash?

渐进式rehash是Redis在进行哈希表扩容时采用的一种策略。当哈希表需要扩容时,Redis会创建一个新的哈希表,并将原有哈希表中的数据逐步迁移到新的哈希表中。这个过程是逐步进行的,每次只迁移一小部分数据,以避免在一次性迁移过程中对系统性能造成较大的影响。

渐进式rehash的过程如下:

  1. Redis会创建一个新的空哈希表,大小是原有哈希表的两倍。
  2. Redis会将原有哈希表中的一个桶(bucket)中的键值对逐个迁移到新的哈希表中。这个迁移过程是逐个键值对进行的,而不是一次性迁移整个桶。
  3. 在每次迁移过程中,Redis会将新哈希表中对应的桶指向原有哈希表中的桶,以保持对原有哈希表的访问能力。
  4. 迁移完成后,Redis会将新哈希表设置为当前使用的哈希表,并释放原有哈希表的内存空间。

通过渐进式rehash,Redis可以在不中断服务的情况下进行哈希表的扩容。这种方式可以避免在一次性迁移过程中对系统性能造成较大的影响,因为每次只迁移一小部分数据。同时,渐进式rehash还可以保持对原有哈希表的访问能力,确保在迁移过程中数据的一致性。

更多面经

下面的面经同样精彩,希望对大家找工作有帮助:

噢耶!字节跳动后端Offer,拿到了!

一天约了4个面试,复盘一下面试经历和薪资范围

避免失业和35岁危机,把这份百度3面的面经分享出来

2023最新社招面经分享:字节 米哈游 富途 猿辅导

联系我

也欢迎关注我的公众号:
程序员升职加薪之旅

微信号:wangzhongyang1993

也欢迎大家
关注我的账号
,点赞、留言、转发。
你的支持,是我更文的最大动力!

抄袭转载的太多,请认准原文链接:
xtrabackup 的介绍与使用

前言

在网上找到教程都是复制粘贴抄袭的,而且还是陈旧资料,不得不说,当前中文互联网环境真是每况愈下。

如果你在网上找 xtrabackup 的教程,大概率会为你介绍 innobackupex。但在最新的 2.4 版本中,
innobackupex 已经废弃,只是一个指向 xtrabackup 的软连接
,官方推荐使用 xtrabackup,原文地址:
The innobackupex Program

本文教程使用的是 xtrabackup 2.4.28,是当前(本文发布时)最新的 xtrabackup2.4 版本,可以备份 MySQL 5.1、5.5、5.6 和 5.7 服务器上的 InnoDB、XtraDB 和 MyISAM 表的数据,以及带有 XtraDB 的 Percona Server。

环境安装

教程中使用 MySQL 5.7 版本,其安装方式不多赘述,主要介绍 xtrabackup 的安装,如果你已经准备好了环境可以略过此步,这里介绍三种不同的方式。

Docker

个人推荐使用 docker 安装的方式,这里贴出完整的 docker-compose.yml,仅供参考:

version: "3.7"
services:
    xtrabackup:
        image: percona/percona-xtrabackup:2.4.28
        container_name: xtrabackup
        restart: "always"
        command: bash -c "while true; do sleep 1; done"
        volumes:
            - "/home/docker/xtrabackup:/data"
            # 这里需要把 MySQL 的数据目录映射到容器中,原因请查看下文的注意事项
            - "/home/docker/mysql/data/:/var/lib/mysql"

command 命令是一个简单的 while 循环,用来保持容器运行,方便进入其中执行命令。

Debian/Ubuntu

从 Percona web 下载 deb 包

wget https://repo.percona.com/apt/percona-release_latest.$(lsb_release -sc)_all.deb

安装 dpkg,这一步需要 root 权限

sudo dpkg -i percona-release_latest.$(lsb_release -sc)_all.deb

启用存储库

percona-release enable-only tools release

apt 安装 xtrabackup

sudo apt install percona-xtrabackup-24

apt 安装 qpress, qpress 用于压缩备份

sudo apt install qpress

Red Hat/CentOs

安装 percona-release:

yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm

RHEL | Centos5 不支持直接从远程位置安装软件包,因此您需要先下载软件包并使用 rpm 手动安装:

wget https://repo.percona.com/yum/percona-release-latest.noarch.rpm
rpm -ivH percona-release-latest.noarch.rpm

启用存储库:

percona-release enable-only tools release

yum 安装 xtrabackup:

yum install percona-xtrabackup-24

yum 安装 qpress, qpress 用于压缩备份:

yum install qpress

注意事项

xtrabackup 在备份时需要访问 MySQL 的数据目录,且具备操作它的权限
,所以 MySQL 和 xtrabackup 不在同一主机的情况下,需要把 MySQL 的数据拷贝到 xtrabakcup 所在的主机上。

常用概念和参数

xtrabakcup 有几个常见的概念:

  • 完整备份:顾名思义,就是完整备份 o.O
  • 恢复备份:顾名思义,就是恢复备份 O.o
  • 准备备份:这个不能顾名思义了,它用来把完整备份出来的数据文件进行一些处理,处理后的数据才能用于恢复备份
  • 增量备份:每一次完整备份的数据文件会很大,增量备份会在其基础上做备份,这样可以节省资源
  • 压缩备份:把备份文件进行压缩,以节省空间

这里是几个常用xtrabackup 命令参数,在后续不明白时,可以过来查询

  • --backup 制作一个备份并放在 --target-dir 中,这个是制作完整备份和增量备份的必要参数
  • --prepare 创建准备备份
  • --target-dir 指定备份文件的目录,不存在将自动创建
  • --apply-log-only 这个选项在准备备份时使用,让其只执行 redo 阶段,一般用在增量备份上
  • --host 指定 MySQL 的 host
  • --port 指定 MySQL 的 port
  • --password 指定 MySQL 的密码
  • --defaults-file 指定 my.cnf,不能是 my.cnf 的软链接
  • --datadir 指定 MySQL 的数据目录,一般会从 my.cnf 中读取

预备数据

我们先预备一下 MySQL 的测试数据,创建一个用于测试的数据库:

CREATE DATABASE test;

创建一个表,并填充数据:

CREATE TABLE `user`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `user` (name) VALUES ("oldme");
INSERT INTO `user` (name) VALUES ("newton");
INSERT INTO `user` (name) VALUES ("watt");

完整备份

创建备份

xtrabackup --backup --host=mysql --password=12345678 --target-dir=/data/backups/base/

完成后的效果:

查看备份出来的文件:
ls -l /data/backups/base

然后我们删掉一条数据,以做恢复数据展示:

DELETE FROM `user` WHERE id=3

准备备份

必须先要准备备份后才能恢复数据:

xtrabackup --prepare --target-dir=/data/backups/base/

完成后的效果:

恢复备份

在恢复备份前需要关闭 MySQL 服务。

// base后面一定要带/
rsync -avrP /data/backups/base/ /var/lib/mysql/

恢复数据后更改文件所有权,如果是 docker 部署则略过:

chown -R mysql:mysql /var/lib/mysql

重启 MySQL 服务,查看恢复完成的数据:

SELECT* FROM `user`

id	name
--------------
1	oldme
2	newton
3	watt

增量备份

创建备份

在创建增量备份前,我们需要有一个完整备份:

xtrabackup --backup --host=mysql --password=12345678 --target-dir=/data/backups/base/

给数据库新增一条数据:

INSERT INTO `user` (name) VALUES ("Einstein");

然后以此为基础进行增量:

xtrabackup --backup --host=mysql --password=12345678 --target-dir=/data/backups/inc1 --incremental-basedir=/data/backups/base

/data/backups/inc1/ 目录现在应该包含增量文件,例如 ibdata1.delta 和 test/user.ibd.delta。

还可以以 inc1 为基础继续增量:

xtrabackup --backup --host=mysql --password=12345678 --target-dir=/data/backups/inc2 --incremental-basedir=/data/backups/inc1

删除数据,准备恢复:

DELETE FROM `user` WHERE id in (3,4)

准备备份与恢复

准备备份与完整备份不同,需要使用
--apply-log-only
选项来保持数据库一致。

先准备基础备份:

xtrabackup --prepare --apply-log-only --target-dir=/data/backups/base/

将增量备份应用到基础备份中:

xtrabackup --prepare --apply-log-only --target-dir=/data/backups/base --incremental-dir=/data/backups/inc1

然后执行同样的恢复备份命令,这点与完整备份的步骤一样,停止 MySQL 服务,执行命令,重启 MySQL:

// base后面一定要带/
rsync -avrP /data/backups/base/ /var/lib/mysql/

最后即可看到恢复的数据:

id	name
--------------
1	oldme
2	newton
3	watt
4	Einstein

压缩备份

创建压缩备份

xtrabackup --backup --compress --host=mysql --password=12345678 --target-dir=/data/backups/compressed/

可以使用
--compress-threads
配置多线程压缩,例如启用四个线程压缩:

xtrabackup --backup --compress --host=mysql --password=12345678 --compress-threads=4 --target-dir=/data/backups/compressed/

准备备份

在准备备份之前需要解压备份:

xtrabackup --decompress --target-dir=/data/backups/compressed/

解压完成后使用和完整备份一样的方式就可以恢复备份数据了。