Skip to content

Commit 7110c65

Browse files
committed
[A] very first version: simple file-per-table tablespace parse
1 parent 00c5c29 commit 7110c65

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1567
-286
lines changed

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Innodb 数据页解析工具
88

99
- Docker for Desktop
1010
- JRE 1.8
11+
- Maven
1112

1213
### 快速构建 innodb-parser
1314

@@ -33,6 +34,45 @@ alias innodb_parser="java -jar $(pwd)/target/innodb-parser-*.jar"
3334
innodb_parser -h
3435
```
3536

37+
### Usage
38+
39+
```bash
40+
$ innodb_parser -h
41+
42+
usage: java -jar /path/to/your/innodb-parser.jar [OPTION]...
43+
根据选项解析 Innodb 数据文件,具体操作完整 DEMO 请查看 README
44+
====================================
45+
-d,--database <arg> 需要分析的数据库名称
46+
-h,--help 打印帮助文档
47+
-p,--page <arg> 需要分析的表空间页号,页号从 0 开始
48+
-r,--root-dir-of-data <arg> 数据目录,所有表空间默认在该目录下, 默认值 /var/lib/mysql
49+
-s,--system-tablespace-file <arg> 系统表空间文件路径,-r 的相对路径, 默认值 ibdata1
50+
-t,--table <arg> 需要分析的表名
51+
-V,--verbose 打印更详细的信息
52+
-v,--version 打印版本号
53+
====================================
54+
如有问题,可以联系 pom.xml 中的开发者
55+
```
56+
3657
### 操作指北
3758

38-
// TODO
59+
### 独立表空间页结构分析
60+
61+
```bash
62+
63+
$ innodb_parser -r=/tmp/mysql -d=sparrow -t=test
64+
65+
[ PageNo][ SpaceID]Page<PageType> ...
66+
------------------------------------------
67+
[ 0][ 23]Page<FIL_PAGE_TYPE_FSP_HDR>
68+
[ 1][ 23]Page<FIL_PAGE_IBUF_BITMAP>
69+
[ 2][ 23]Page<FIL_PAGE_INODE>
70+
[ 3][ 23]Page<FIL_PAGE_INDEX>
71+
[ 4][ 23]Page<FIL_PAGE_INDEX>
72+
[ ][ ]Page<FIL_PAGE_TYPE_ALLOCATED>
73+
[ ][ ]Page<FIL_PAGE_TYPE_ALLOCATED>
74+
```
75+
76+
### Tips
77+
78+
- **i18n 部分,IDEA 建议勾选 Preferences | Editor | File Encodings 的 Transparent native-to-ascii conversion,中文在IDEA就可以正常展示了**

iprc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CONTAINER_NAME=mysql
2+
MOUNT_VOLUME=/tmp/mysql
3+
IMAGE=mysql:5.7
4+
MY_CONF_DIR=$(pwd)/conf.d
5+
INIT_DB_DIR=$(pwd)/docker-entrypoint-initdb.d
6+
PASSWORD_DIR=~/.password
7+
MYSQL_PASS_FILE=${PASSWORD_DIR}/mysql

run-mysql-in-docker.sh

Lines changed: 18 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,37 @@
11
#!/bin/bash
22

3-
function stopContainer(){
4-
CONTAINER_NAME=$1
5-
if [ "$(docker container ls --filter=name="${CONTAINER_NAME}" -q | wc -l)" -gt 0 ]; then
6-
echo "stopping container [${CONTAINER_NAME}]"
7-
docker container stop "${CONTAINER_NAME}"
8-
echo "done"
9-
fi
3+
source iprc
4+
source tools.sh
105

11-
if [ "$(docker container ls -a --filter=name="${CONTAINER_NAME}" -q | wc -l)" -gt 0 ]; then
12-
echo "removing container [${CONTAINER_NAME}]"
13-
docker container rm "${CONTAINER_NAME}"
14-
echo "done"
15-
fi
16-
}
6+
ensureImageExist "${IMAGE}"
177

18-
function ensureImageExist() {
19-
image=$1
20-
if [ "$(docker images --filter=reference="${image}" -q | wc -l)" -eq 0 ]; then
21-
echo "Image [${image}] not found, try to pull"
22-
if [ "$(docker pull "${image}")" ]; then
23-
echo "Image [${image}] pull success";
24-
else
25-
echo "Image [${image}] pull error! "
26-
exit 1;
27-
fi
28-
fi
29-
}
8+
mkdir -p "${MOUNT_VOLUME}"
9+
mkdir -p "${PASSWORD_DIR}"
3010

31-
CONTAINER_NAME=mysql
32-
MOUNT_VOLUME=/tmp/mysql
33-
IMAGE=mysql:5.7
34-
MY_CONF_DIR=$(pwd)/conf.d
35-
INIT_DB_DIR=$(pwd)/docker-entrypoint-initdb.d
36-
37-
ensureImageExist ${IMAGE}
38-
39-
rm -rf ${MOUNT_VOLUME}
40-
mkdir -p ${MOUNT_VOLUME}
41-
42-
mkdir -p ~/.password
43-
MYSQL_PASS_FILE=~/.password/mysql
4411
echo "######################################################"
45-
if [ ! -e ${MYSQL_PASS_FILE} ] || [ $(cat ${MYSQL_PASS_FILE} | wc -l) -eq 0 ]; then
46-
openssl rand -base64 10 > ${MYSQL_PASS_FILE}
12+
# shellcheck disable=SC2046
13+
# shellcheck disable=SC2002
14+
if [ ! -e "${MYSQL_PASS_FILE}" ] || [ $(cat "${MYSQL_PASS_FILE}" | wc -l) -eq 0 ]; then
15+
openssl rand -base64 10 >"${MYSQL_PASS_FILE}"
4716
echo "new password generated, find it in ${MYSQL_PASS_FILE}"
4817
else
4918
echo "use password in ${MYSQL_PASS_FILE} generated before"
5019
fi
5120
echo "######################################################"
5221

53-
stopContainer ${CONTAINER_NAME}
54-
55-
docker run --name ${CONTAINER_NAME} -p 3306:3306 \
56-
-v ${MY_CONF_DIR}:/etc/mysql/conf.d \
57-
-v ${MOUNT_VOLUME}:/var/lib/mysql \
58-
-v ${INIT_DB_DIR}:/docker-entrypoint-initdb.d \
59-
-e MYSQL_ROOT_PASSWORD="$(cat "${MYSQL_PASS_FILE}")" \
60-
-d ${IMAGE}
22+
stopContainer "${CONTAINER_NAME}"
6123

24+
docker run --name "${CONTAINER_NAME}" -p 3306:3306 \
25+
-v "${MY_CONF_DIR}":/etc/mysql/conf.d \
26+
-v "${MOUNT_VOLUME}":/var/lib/mysql \
27+
-v "${INIT_DB_DIR}":/docker-entrypoint-initdb.d \
28+
-e MYSQL_ROOT_PASSWORD="$(cat "${MYSQL_PASS_FILE}")" \
29+
-d "${IMAGE}"
6230

6331
echo "#######################DB 配置#######################"
64-
cat ${MY_CONF_DIR}/*
32+
cat "${MY_CONF_DIR}"/*
6533
echo "#######################DB 配置#######################"
6634

6735
echo "#######################DB 初始化#######################"
68-
cat ${INIT_DB_DIR}/*
36+
cat "${INIT_DB_DIR}"/*
6937
echo "#######################DB 初始化#######################"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package me.rainstorm.innodb.common;
2+
3+
import java.util.LinkedHashMap;
4+
import java.util.Map;
5+
6+
/**
7+
* @author traceless
8+
*/
9+
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
10+
private final int cacheSize;
11+
12+
public LRUCache(int cacheSize) {
13+
super(cacheSize, 0.75f, true);
14+
this.cacheSize = cacheSize;
15+
}
16+
17+
@Override
18+
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
19+
return size() >= cacheSize;
20+
}
21+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package me.rainstorm.innodb.common;
2+
3+
import java.util.Iterator;
4+
import java.util.Spliterators;
5+
import java.util.stream.Stream;
6+
import java.util.stream.StreamSupport;
7+
8+
/**
9+
* @author traceless
10+
*/
11+
public class StreamUtil {
12+
public static <T> Stream<T> of(Iterator<T> iterator) {
13+
return of(iterator, false);
14+
}
15+
16+
public static <T> Stream<T> of(Iterator<T> iterator, boolean parallel) {
17+
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), parallel);
18+
}
19+
20+
public static <T> Stream<T> of(Iterator<T> iterator, int size) {
21+
return of(iterator, size, false);
22+
}
23+
24+
public static <T> Stream<T> of(Iterator<T> iterator, int size, boolean parallel) {
25+
return StreamSupport.stream(Spliterators.spliterator(iterator, size, 0), parallel);
26+
}
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package me.rainstorm.innodb.common.i18n;
2+
3+
import java.text.MessageFormat;
4+
import java.util.Locale;
5+
import java.util.ResourceBundle;
6+
7+
import static me.rainstorm.innodb.parser.ParserConstants.LOCALE;
8+
9+
/**
10+
* @author traceless
11+
*/
12+
public class I18nUtil {
13+
private static ResourceBundle rb;
14+
15+
static {
16+
setLocale(LOCALE);
17+
}
18+
19+
public static void setLocale(Locale locale) {
20+
I18nUtil.rb = ResourceBundle.getBundle("i18n/message", locale);
21+
}
22+
23+
public static String message(String code, Object... args) {
24+
return MessageFormat.format(rb.getString(code), args);
25+
}
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package me.rainstorm.innodb.common.i18n;
2+
3+
import lombok.Getter;
4+
5+
import java.util.Locale;
6+
7+
/**
8+
* @author traceless
9+
*/
10+
11+
@Getter
12+
public enum SupportedLocaleEnum {
13+
/**
14+
* 英文
15+
*/
16+
EN_US(Locale.US),
17+
/**
18+
* 中文
19+
*/
20+
ZH_CN(Locale.CHINA);
21+
22+
SupportedLocaleEnum(Locale locale) {
23+
this.locale = locale;
24+
}
25+
26+
private final Locale locale;
27+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package me.rainstorm.innodb.domain;
2+
3+
/**
4+
* @author traceless
5+
*/
6+
public class InnodbConstants {
7+
public static final String MYSQL_INNODB_VERSION = "5.7.32";
8+
9+
public static int ONE_K = 1024;
10+
public static int ONE_M = ONE_K * ONE_K;
11+
public static int PAGE_SIZE = 16 * ONE_K;
12+
13+
/**
14+
* storage/innobase/include/fsp0types.h
15+
* <p>
16+
* File space extent size in pages
17+
* <p>
18+
* page size | file space extent size
19+
* <p>
20+
* ----------+-----------------------
21+
* <p>
22+
* 4 KiB | 256 pages = 1 MiB
23+
* <p>
24+
* 8 KiB | 128 pages = 1 MiB
25+
* <p>
26+
* 16 KiB | 64 pages = 1 MiB
27+
* <p>
28+
* 32 KiB | 64 pages = 2 MiB
29+
* <p>
30+
* 64 KiB | 64 pages = 4 MiB
31+
* <p>
32+
* #define FSP_EXTENT_SIZE((UNIV_PAGE_SIZE <=(16384) ?
33+
* <p>
34+
* (1048576/UNIV_PAGE_SIZE): \
35+
* <p>
36+
* ((UNIV_PAGE_SIZE <=(32768))? \
37+
* <p>
38+
* (2097152/UNIV_PAGE_SIZE): \
39+
* <p>
40+
* (4194304/UNIV_PAGE_SIZE))))
41+
*/
42+
public static int PAGE_NUM_IN_EXTEND =
43+
PAGE_SIZE <= 16 * ONE_K ?
44+
ONE_M / PAGE_SIZE :
45+
PAGE_SIZE <= 32 * ONE_K ?
46+
2 * ONE_M / PAGE_SIZE :
47+
4 * ONE_M / PAGE_SIZE;
48+
49+
public static int EXTEND_SIZE = PAGE_SIZE * PAGE_NUM_IN_EXTEND;
50+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package me.rainstorm.innodb.domain.extend;
2+
3+
import lombok.Getter;
4+
import lombok.extern.slf4j.Slf4j;
5+
import me.rainstorm.innodb.domain.page.LogicPage;
6+
import me.rainstorm.innodb.domain.page.LogicPageFactory;
7+
import me.rainstorm.innodb.domain.page.PhysicalPage;
8+
import me.rainstorm.innodb.domain.page.core.PageBody;
9+
import me.rainstorm.innodb.domain.tablespace.TableSpace;
10+
11+
import java.nio.ByteBuffer;
12+
13+
import static me.rainstorm.innodb.domain.InnodbConstants.PAGE_NUM_IN_EXTEND;
14+
import static me.rainstorm.innodb.domain.InnodbConstants.PAGE_SIZE;
15+
16+
/**
17+
* 表空间的一个区,
18+
*
19+
* @author traceless
20+
*/
21+
@Slf4j
22+
@Getter
23+
public class Extend {
24+
private final TableSpace tableSpace;
25+
private final int extendOffset;
26+
private final ByteBuffer data;
27+
28+
public Extend(TableSpace tableSpace, int extendOffset, ByteBuffer data) {
29+
this.tableSpace = tableSpace;
30+
this.extendOffset = extendOffset;
31+
this.data = data;
32+
}
33+
34+
/**
35+
* @param pageOffsetInExtend extend 内页偏移量
36+
* @return Page
37+
*/
38+
public <Page extends LogicPage<? extends PageBody>> Page page(int pageOffsetInExtend) {
39+
PhysicalPage physicalPage = new PhysicalPage(this, pageOffsetInExtend,
40+
ByteBuffer.wrap(data.array(), pageOffsetInExtend * PAGE_SIZE, PAGE_SIZE).asReadOnlyBuffer());
41+
return (Page) LogicPageFactory.of(physicalPage);
42+
}
43+
44+
public static int pageOffsetOfExtend(int pageNo) {
45+
return pageNo % PAGE_NUM_IN_EXTEND;
46+
}
47+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package me.rainstorm.innodb.domain.page;
2+
3+
import me.rainstorm.innodb.domain.page.core.Undefined;
4+
5+
/**
6+
* @author traceless
7+
*/
8+
public class AllocatedPage extends LogicPage<Undefined> {
9+
public AllocatedPage(PhysicalPage physicalPage) {
10+
super(physicalPage);
11+
}
12+
13+
@Override
14+
protected Undefined createPageBody(PhysicalPage physicalPage) {
15+
return Undefined.INSTANCE;
16+
}
17+
18+
@Override
19+
public String toString() {
20+
return String.format("[%10s][%10s]Page<%s>", "", "", fileHeader.getPageType().getMessageKey());
21+
}
22+
}

0 commit comments

Comments
 (0)