tonglin0325的个人主页

MySQL学习笔记——undo log和redo log

MySQL的架构可以分成连接层,Server层和存储引擎层。

undo log 和 redo log 是 MySQL InnoDB 存储引擎管理的数据日志类型,它们主要用于支持事务的 ACID 特性,即原子性、一致性、隔离性和持久性。

1.Undo Log(回滚日志)

undo log 用于支持事务的原子性(Atomicity)隔离性(Isolation)。它在事务发生更改之前记录数据的原始状态,以便在事务回滚时能够将数据恢复到其原始状态。

undo log工作原理

  • 当一个事务对数据库中的数据进行修改(如 INSERTUPDATEDELETE 操作)时,InnoDB 会先将原始数据的副本写入 undo log
  • 如果事务需要回滚(无论是由于用户请求还是由于系统错误),InnoDB 使用 undo log 将更改撤销,使数据恢复到事务开始之前的状态。
  • undo log 也用于实现 一致性非锁定读(Consistent Non-Locking Reads)一致性非锁定读 是 MVCC 的核心特性之一。这意味着在事务的隔离级别为 REPEATABLE READREAD COMMITTED 时,可以提供一致的读取视图。它允许事务在读取数据时不加锁,从而避免阻塞其他事务。这种机制依赖于 undo logReadView,确保在高并发的情况下提供一致的读取视图。

undo log存储位置

  • undo log 存储在专用的 回滚段(rollback segment) 中,通常位于与数据文件相同的表空间内。

undo log主要用于事务回滚MVCC(多并发版本控制)

MVCC工作原理:

  • 可重复读(REPEATABLE READ)隔离级别:事务在开始时创建一个ReadView,并在整个事务期间使用这个ReadView,确保即使有其他事务提交了更改,当前事务的查询结果也保持一致。这样就保证了事务内的多次读取是可重复的。
  • 读已提交(READ COMMITTED)隔离级别下:ReadView是在每次执行查询时生成的,因此一个事务内的不同查询可能看到其他事务已经提交的更改。这样可以保证读取到的是其他事务已提交的数据,但无法保证事务内的多次读取结果一致。当事务执行读取操作时,ReadView 会判断数据的每个版本的可见性,这主要取决于版本的 事务 IDtrx_id

2.Redo Log(重做日志)

redo log 用于支持事务的持久性(Durability)。它记录了已提交的事务对数据库进行的更改,这样在系统崩溃后可以通过redo log来恢复数据。

在redo log记录完成后,事务才算完成。后续,InnoDB 引擎会在适当的时候,由后台线程将缓存在 Buffer Pool 的脏页刷新到磁盘里,这就是 WAL (Write-Ahead Logging)技术

工作原理:

  • 在事务提交之前,所有的更改都会被写入 redo log 的日志缓冲区(redo log buffer)。
  • 当事务提交时,redo log 缓冲区会刷新到磁盘上的 redo log 文件。
  • 如果数据库崩溃,在重启时,InnoDB 可以使用 redo log 来重新应用(重做)日志中的所有更改,以确保所有已提交的事务持久化到数据库中。

存储位置:

  • redo log 存储在专用的日志文件中,InnoDB存储引擎有一个redo log group,由2个文件组成(通常为 ib_logfile0ib_logfile1)中,这些文件位于 InnoDB 的日志目录中。
  • 重做日志文件组是以**循环写**的方式工作的,从头开始写,写到末尾就又回到开头,相当于一个环形。所以 InnoDB 存储引擎会先写 ib_logfile0 文件,当 ib_logfile0 文件被写满的时候,会切换至 ib_logfile1 文件,当 ib_logfile1 文件也被写满时,会切换回 ib_logfile0 文件。

参考:MySQL 日志:undo log、redo log、binlog 有什么用?

3.两阶段提交(Two-Phase Commit, 2PC)

在 MySQL 中,事务的操作首先写入到 redo log(InnoDB 的物理日志)中,用于崩溃恢复;同时,如果 MySQL 服务器配置了 binlog(二进制日志),事务的变化也需要记录到 binlog 中,作为逻辑日志,用于主从复制和增量备份。

为了确保这两个日志之间的一致性(即要么都成功,要么都失败),MySQL 使用了两阶段提交。如果没有这种机制,在事务提交过程中如果崩溃,可能会出现 redo log 和 binlog 之间的不一致,从而导致数据不一致的情况。

全文 >>

Python爬虫——selenium语法

1.获取元素

通过a标签的文本筛选

1
2
driver.find_element(By.LINK_TEXT, 'xx').click()

通过css筛选

1
2
3
4
driver.find_element(By.CSS_SELECTOR, "input[type='email']").send_keys("xxx")
driver.find_element(By.CSS_SELECTOR, "button[type='button']").click()
driver.find_element(By.CSS_SELECTOR, "a.btn_download").click()

通过element name或者css name筛选

1
2
3
driver.find_element(By.NAME, "xx").send_keys("xxx")
driver.find_element(By.CLASS_NAME, "xx").click()

通过xpath筛选,只会contains,starts-with等语法

1
2
3
driver.find_element(By.XPATH, '//a[starts-with(@title, "xx")]').click()
driver.find_element(By.XPATH, '//iframe[contains(@title, "xx")]').click()

2.时间筛选器选择时间

可以使用driver.execute_script(js)将元素的readonly属性去掉,再click后clear掉日期,最后send_keys输入新的日期时间

1
2
3
4
5
6
7
element = driver.find_elements(By.CSS_SELECTOR, "input[class='xxx']")
js = 'document.getElementsByClassName("xxx")[0].removeAttribute("readonly");'
driver.execute_script(js)
element[0].click()
element[0].clear()
element[0].send_keys("2022-01-01")

全文 >>

parquet-tools使用

使用parquet-tools的方法有2种

1.在安装了CDH的机器上,会自动有parquet-tools命令

1
2
3
4
lintong@master:/opt/cloudera/parcels/CDH/bin$ ls| grep parquet-tools
parquet-tools
lintong@master:/opt/cloudera/parcels/CDH/bin$ parquet-tools

 

2.自行编辑jar

git clone并指定分支,master分支已经删除了parquet-tools

1
2
git clone git@github.com:apache/parquet-mr.git -b apache-parquet-1.10.1

编译

1
2
cd parquet-tools && mvn clean package -Plocal

 

全文 >>

使用avro-protobuf将protobuf转换成avro

avro-protobuf项目提供ProtobufDatumReader类,可以用于从protobuf定义生成的java class中获得avro schema

使用方法如下:

1.引入依赖

1
2
3
4
5
6
7
8
9
10
11
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-protobuf</artifactId>
<version>1.11.1</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.21.7</version>
</dependency>

2.定义protobuf schema,名为other.proto,schema如下

1
2
3
4
5
6
7
8
9
10
11
12
syntax = "proto3";
package com.acme;

message MyRecord {
string f1 = 1;
OtherRecord f2 = 2;
}

message OtherRecord {
int32 other_id = 1;
}

从使用protobuf定义生成java class

1
2
protoc -I=./ --java_out=./src/main/java ./src/main/proto/other.proto

3.编写java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.example.demo;

import com.acme.Other;

import java.util.*;
import org.apache.avro.protobuf.ProtobufDatumReader;

public class Demo {

public static void main(String[] args) throws Exception {

ProtobufDatumReader<Other.MyRecord> datumReader = new ProtobufDatumReader<Other.MyRecord>(Other.MyRecord.class);
System.out.println(datumReader.getSchema().toString(true));

}

}

全文 >>

Amazon EMR使用指南

Amazon EMR是Amazon提供的托管大数据套件,可选的组件包括Hadoop,Hive,Hue,Hbase,Presto,Spark等

使用Amazon EMR的好处是快速伸缩,版本升级也较为方便,如果配合S3存储,可以做到计算和存储分离,这样对于运维的压力会小一些,存储的稳定性交给S3,计算集群即使故障也可以很方便的进行重建,很适合小团队。缺点是界面友好程度远不如CDH和HDP。

如果使用Amazon EMR,最好阅读一下官方的2个文档:

1.Amazon EMR最佳实践

1
2
https://d0.awsstatic.com/whitepapers/aws-amazon-emr-best-practices.pdf

2.Amazon EMR迁移指南

1
https://d1.awsstatic.com/whitepapers/amazon_emr_migration_guide.pdf

1.创建EMR集群

在创建Amazon EMR集群的时候可以选择快速模式,界面如下

也可以选择高级模式

全文 >>

Grafana学习笔记——filter语法

在使用grafana的filter的时候,其支持一些语法用于对指标进行过滤,如下

literal_or : tagv的过滤规则: 精确匹配多项迭代值,多项迭代值以’|’分隔,大小写敏感

iliteral_or: tagv的过滤规则: 精确匹配多项迭代值,多项迭代值以’|’分隔,忽略大小写

wildcard: tagv的过滤规则: 通配符匹配,大小写敏感

iwildcard: tagv的过滤规则: 通配符匹配,忽略大小写

regexp: tagv的过滤规则: 正则表达式匹配

not_literal_or: tagv的过滤规则: 通配符取非匹配,大小写敏感

not_iliteral_or: tagv的过滤规则: 通配符取非匹配,忽略大小写

 

全文 >>

Mongo的oplog和change stream

1.oplog

MongoDB副本集由一组服务器组成,这些服务器都具有相同数据的副本,复制可确保客户端对副本集主副本集上的文档所做的所有更改都正确应用于其他副本集的服务器,称为secondaries副本。

MongoDB 复制的工作原理是让主节点在其操作日志(或操作日志)中记录更改,然后每个从节点读取主节点的操作日志并将所有操作按顺序应用到它们自己的文档中。

将新服务器添加到副本集时,该服务器首先对主服务器上的所有数据库和集合执行snapshot,然后读取主服务器的 oplog 以应用自启动快照以来可能所做的所有更改。这个新服务器在赶上主服务器oplog的尾部时成为secondary服务器(并且能够处理查询)。

2.change stream

Debezium MongoDB 连接器使用与上述类似的复制机制,尽管它实际上并没有成为副本集的成员。主要区别在于连接器不直接读取 oplog,而是将捕获和解码 oplog 委托给 MongoDB 的 Change Streams 功能。

使用change stream,MongoDB服务器将集合的更改公开为事件流。Debezium 连接器监视流并将更改传递到下游。而且,当连接器第一次看到副本集时,它会查看 oplog 以获取上次记录的事务,然后执行主数据库和集合的快照。复制完所有数据后,连接器会从之前从操作日志中读取的位置创建更改流。

当MongoDB连接器进程发生变化时,它会定期记录oplog/stream中事件发起的位置。当连接器停止时,它会记录它处理的最后一个oplog/流位置,以便在重新启动时它只是从该位置开始流式传输。换句话说,连接器可以停止、升级或维护,并在一段时间后重新启动,它将准确地从中断的位置继续,而不会丢失单个事件。当然,MongoDB的oplog通常被限制在最大大小,这意味着连接器不应该停止太久,否则oplog中的某些操作可能会在连接器有机会读取它们之前被清除。在这种情况下,重新启动时,连接器将检测缺少的oplog操作,执行快照,然后继续流式传输更改。

限制:change stream只能用于 replica set 集群和 sharded cluster 集群,单节点mongo因为没有oplog,所以不支持

参考:MongoDB Change Stream之一——上手及初体验

3.change stream相关操作

1.获取某个database的某个collection的Change Stream

1
2
3
use xx_db
db.your_collection.watch([],{maxAwaitTimeMS:60000})

全文 >>