微调实践 - 让AI学完所有PolarDB文章
参考文档点击文末阅读原文打开; 推荐《最好的PostgreSQL学习镜像》;
微调实践 - 让AI学完所有PolarDB文章
如下文章介绍了如何在 Apple Silicon Mac 上微调(fine-tuning)大型语言模型(LLM)? 其用于微调的指令和回复都来自更大参数的模型(例如405b的llama 3), 被微调的模型是更小参数的模型(例如7b的mistral), 有点像用大模型来蒸馏小模型. 因为大模型更加全面, 但是要求的内存和算力都更大, 对于仅用于专业领域的场景来说, 蒸馏的小模型性价比更高.
《AI大模型+全文检索+tf-idf+向量数据库+我的文章 系列之5 - 在 Apple Silicon Mac 上微调(fine-tuning)大型语言模型(LLM) 并发布GGUF》
本文将用我写过的所有关于PolarDB的文章来微调mistral 7b, 看看微调后的模型会不会对PolarDB PG的问题更从容?
我写过的所有关于PolarDB的文章在此: https://github.com/digoal/blog
训练用的机器就是普通的笔记本, 但是要求apple chipset. 可以使用mlx框架( https://github.com/ml-explore/mlx ), 加速训练. mac就是最廉价且触手可及的模型训练和运行机器.
Macbook pro m2 16g
开始微调
0、升级python版本
升级到 python 3.12
- https://www.python.org/downloads/macos/ # 假设已安装到
/Library/Frameworks/Python.framework/Versions/
$ ll /Library/Frameworks/Python.framework/Versions/
lrwxr-xr-x 1 root wheel 4B Jan 8 17:56 Current -> 3.12
drwxrwxr-x 11 root admin 352B Jan 8 17:56 3.12
# 添加软链
ln -s /Library/Frameworks/Python.framework/Versions/Current/bin/pip3 /Library/Frameworks/Python.framework/Versions/Current/bin/pip
ln -s /Library/Frameworks/Python.framework/Versions/Current/bin/python3 /Library/Frameworks/Python.framework/Versions/Current/bin/python
# 环境变量
echo"
export PATH=/Library/Frameworks/Python.framework/Versions/Current/bin:\$PATH
" >> ~/.bash_profile
. ~/.bash_profile
1、把python pip源换成aliyun
https://segmentfault.com/a/1190000044200422 https://www.jianshu.com/p/71924b5a8aaa
echo"
[global]
index-url = https://mirrors.aliyun.com/pypi/simple/
" > ~/.config/pip/pip.conf
2、准备用于微调的jsonl文件(train.jsonl和valid.jsonl)
微调不同的模型, 需要不同的jsonl内容格式, 请参考模型说明(通常ollama 对应模型 models页面中有相关的介绍. 或者进入ollama shell后输入/show modelfile), 或者问chatgpt.
$ ollama run mistral:7b
>>> /show modelfile
# Modelfile generated by "ollama show"
# To build a new Modelfile based on this, replace FROM with:
# FROM mistral:7bFROM /Users/digoal/.ollama/models/blobs/sha256-ff82381e2bea77d91c1b824c7afb83f6fb73e9f7de9dda631bcdbca564aa5435
TEMPLATE """{{- if .Messages }}
{{- range $index, $_ := .Messages }}
{{- if eq .Role "user" }}
{{- if and (eq (len (slice $.Messages $index)) 1) $.Tools }}[AVAILABLE_TOOLS] {{ $.Tools }}[/AVAILABLE_TOOLS]
{{- end }}[INST] {{ if and $.System (eq (len (slice $.Messages $index)) 1) }}{{ $.System }}
{{ end }}{{ .Content }}[/INST]
{{- else if eq .Role "
assistant" }}
{{- if .Content }} {{ .Content }}
{{- else if .ToolCalls }}[TOOL_CALLS] [
{{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}
{{- end }}]
{{- end }}</s>
{{- else if eq .Role "tool" }}[TOOL_RESULTS] {"content": {{ .Content }}} [/TOOL_RESULTS]
{{- end }}
{{- end }}
{{- else }}[INST] {{ if .System }}{{ .System }}{{ end }}{{ .Prompt }}[/INST]
{{- end }} {{ .Response }}
{{- if .Response }}</s>
{{- end }}"
""
PARAMETER stop [INST]
PARAMETER stop [/INST]
LICENSE """
... ...
""">>>
微调mistral模型为例, jsonl内容格式参考如下:
https://github.com/apeatling/simple-guide-to-mlx-finetuning/blob/trunk/train-example.jsonl
jsonl文件内容, 每行的格式:
{"text":"<s>[INST] 文章标题 [/INST] 文章内容 </s>"}
注意, jsonl使用utf-8编码, 中文会被转义. https://jsonlines.org/
首先下载包含PolarDB文章的blog
cd ~
git clone --depth 1 https://github.com/digoal/blog
编辑一个python脚本, 脚本内容~/a.py附在文末. 从README.md中提取PolarDB相关文章的文件名, 从文件名第一行得到文章标题以及文件路径. 为了避免训练时超出内存限制, 分割文件, 标题+partN, 然后每1024个字符作为一行(如果结束时不是换行符, 则超出1024个字符, 确保不会把段落或文字切分开. 输出为jsonl.
cd ~
python3 a.py
输出示例
{"text":"<s>[INST] 文章标题 part1 [/INST] 第一部分内容 </s>"}
...
{"text":"<s>[INST] 文章标题 partN [/INST] 第N部分内容 </s>"}
人工看一下output.jsonl, 把结果放到~/train.jsonl和~/valid.jsonl文件中. 确保一篇文章的所有part都在同一个jsonl里.
$ wc -l output.jsonl
3031 output.jsonl# 人工观测, 确保一篇文章的所有part都在同一个jsonl里.
$ less -N output.jsonl
$ head -n 2046 output.jsonl > ./train.jsonl
$ tail -n 985 output.jsonl > ./valid.jsonl
提取一行jsonl文件的内容看看是否符合前面说的格式要求?
$ head -n 1 train.jsonl
{"text": "<s>[INST]大模型微调(fine-tuning)实践 - 让它学完所有PolarDB文章 part1 [/INST] ... 背景 \n</s>"}
3、克隆mlx-examples项目, 这个项目利用mlx框架加速apple chipset训练. 本例用里面的lora/qlora来微调模型. 详细说明可以参考lora目录中的readme文档.
cd ~
git clone --depth 1 https://github.com/ml-explore/mlx-examples.git
安装依赖
cd ~/mlx-examples/lora
pip install -r requirements.txt
pip install torch# 可选:
pip install -U mlx-lm
4、微调如下模型
https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3
下载模型相关文件前, 需要注册huggingface账号, 同时在模型页面内要授权同意使用该模型, 否则无法下载.
下载
https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3/tree/main
千万不要下载 consolidated.safetensors , 微调时会导致报错. 下载切割的几个safetensors文件即可:
Loading pretrained model
Traceback (most recent call last):
File "/Users/digoal/mlx-examples/lora/lora.py", line 336, in <module>
model, tokenizer, _ = lora_utils.load(args.model, tokenizer_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/digoal/mlx-examples/lora/utils.py", line 162, in load
model.load_weights(list(weights.items()))
File "/Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages/mlx/nn/layers/base.py", line 178, in load_weights
raise ValueError(f"Received parameters not in model: {extras}.")
ValueError: Received parameters not in model: layers.25.feed_forward.w1.weight ....
将相关文件放到目标位置
mkdir -p ~/finetune_mistral/jsonl
mkdir -p ~/finetune_mistral/model
mv ~/train.jsonl ~/finetune_mistral/jsonl/
mv ~/valid.jsonl ~/finetune_mistral/jsonl/ mv ~/Downloads/config.json ~/finetune_mistral/model
mv ~/Downloads/model.safetensors.index.json ~/finetune_mistral/model
mv ~/Downloads/tokenizer.json ~/finetune_mistral/model
mv ~/Downloads/tokenizer_config.json ~/finetune_mistral/model
mv ~/Downloads/tokenizer.model ~/finetune_mistral/model
mv ~/Downloads/params.json ~/finetune_mistral/model
mv ~/Downloads/special_tokens_map.json ~/finetune_mistral/model
mv ~/Downloads/generation_config.json ~/finetune_mistral/model
mv ~/Downloads/model-00001-of-00003.safetensors ~/finetune_mistral/model
mv ~/Downloads/model-00003-of-00003.safetensors ~/finetune_mistral/model
mv ~/Downloads/model-00002-of-00003.safetensors ~/finetune_mistral/model
执行微调, 参数影响内存的使用、效果、耗时. 参数设置建议可参考: https://github.com/ml-explore/mlx-examples/tree/main/lora#memory-issues
cd ~/mlx-examples/lora
python3 lora.py \
--train \
--model ~/finetune_mistral/model \
--data ~/finetune_mistral/jsonl \
--adapter-file ~/finetune_mistral/adapters.npz \
--batch-size 1 \
--lora-layers 4 \
--iters 1000
5、微调完成后, 会产出微调后的参数文件adapters.npz. 使用该参数文件可以获得微调模型的效果.
python lora.py --model ~/finetune_mistral/model \
--adapter-file ~/finetune_mistral/adapters.npz \
--max-tokens 2048 \
--prompt "如何修改pg_bulkload代码? 让它能适配PolarDB PFS, 实现数据导入的加速"
一些小问题
小内存Mac (例如16G) 如果禁用了swap可能会OOM. 解除swap的禁用参考下文:
《禁用 MacOS 的 Swap 分区 - 实测真实有效》 sudo nvram boot-args="vm_compressor=4 serverperfmode=1"
然后重启恢复禁止swap:
sudo nvram boot-args="vm_compressor=2 serverperfmode=1"
然后重启
注意, 解除了swap禁用, 训练过长可能导致大量写磁盘行为, 导致磁盘寿命快速被消耗. 我这里短短几个小时写了几个TB.
16G内存可能还是不够, 可以考虑量化后再微调.
Loading pretrained model
Total parameters 7248.130M
Trainable parameters 0.106M
Loading datasets
Training
[WARNING] Some sequences are longer than 2048 tokens. Consider pre-splitting your data to save memory.
上面这个警告和jsonl有关, 可以切分一下内容.
参考: https://milvus.io/docs/zh/how_to_enhance_your_rag.md 下面这个是 开启swap后还是报错了, 原因是jsonl一行内容太大, split后就好了.
libc++abi: terminating due to uncaught exception of type std::runtime_error: Attempting to allocate 9535252608 bytes which is greater than the maximum allowed buffer size of 8589934592 bytes.
Abort trap: 6
量化参考:
https://zhuanlan.zhihu.com/p/676249749 https://github.com/ggerganov/llama.cpp/blob/master/examples/quantize/README.md
如果你的macos版本较低, 可能遇到类似报错
Traceback (most recent call last):
File "/Users/digoal/mlx-examples/lora/lora.py", line 9, in <module>
import mlx.core as mx
ImportError: dlopen(/Users/digoal/Library/Python/3.9/lib/python/site-packages/mlx/core.cpython-39-darwin.so, 0x0002): Symbol not found: _cblas_sgemm$NEWLAPACK
Referenced from: <A9854BA1-AE0A-3D86-956A-FA129ACFDAC5> /Users/digoal/Library/Python/3.9/lib/python/site-packages/mlx/lib/libmlx.dylib (built for macOS 13.5 which is newer than running OS)
Expected in: <0494A450-9778-31EB-84F6-88A045586FBF> /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate
# 当前为13.2.1 , 更新到13.7.2
(built for macOS 13.5 which is newer than running OS)
中文tokenization问题, 被微调的模型没有训练过中文词汇, 怎么基于中文语料微调? 如何基于已有中文语料训练出合适的中文词汇(bpe?)? 如何扩tokenization词汇表? 如何保持原有词汇的mapping id不变?
https://zhuanlan.zhihu.com/p/639144223 https://zhuanlan.zhihu.com/p/690019010 https://github.com/google/sentencepiece https://huggingface.co/docs/tokenizers/index
附录
脚本, 准备用于微调的jsonl文件(train.jsonl和valid.jsonl)
vi ~/a.py
import re
import os
import json
import randomdef get_lines_from_marker(file_path, marker, search_term):
with open(file_path, 'r', encoding='utf-8') as file:
lines = file.readlines()
# 查找标记行,并获取从该行开始的所有后续行
start_index = None
for i, line in enumerate(lines):
if marker in line:
start_index = i
break
# 如果找到了标记行,筛选从该行开始的所有内容
if start_index is not None:
filtered_lines = []
for line in lines[start_index:]:
# 使用正则表达式进行不区分大小写的匹配
if re.search(search_term, line, re.IGNORECASE):
filtered_lines.append(line.strip()) # strip() 用于去除行末的换行符
return filtered_lines
else:
return []
def split_text_into_jsonl(text, split_size=1024):
# 分离第一行并去掉前三个字符
lines = text.split('\n')
first_line = lines[0][3:] # 去掉第一行前3个字符
remaining_text = '\n'.join(lines[1:]) # 剩余的文本
parts = []
current_part = ""
# 逐行处理剩余的文本
for line in remaining_text.splitlines():
if len(current_part) + len(line) + 1 <= split_size: # 当前part可以容纳此行
current_part += line + "\n"
else:
# 当前part达到限制,保存并开始新的part
parts.append({
"text": f"<s>[INST]{first_line} part{len(parts)+1} [/INST]{current_part}</s>"
})
current_part = line + "\n"# 新的part从当前行开始
# 添加最后一个part
if current_part:
parts.append({
"text": f"<s>[INST]{first_line} part{len(parts)+1} [/INST]{current_part}</s>"
})
return parts
def process_line(line, output_file):
# 使用正则表达式从每行中提取文件路径和标题
match = re.match(r'##### (\S+)\s+\[([^\]]+)\]\((\S+)\)', line)
if not match:
return# 如果匹配失败,跳过该行
# 提取文件路径、标题和链接路径
file_path = match.group(1) # 例如 "202412/20241210_01.md"
title = match.group(2) # 例如 "《PolarDB 100 问 | PolarDB 11 使用 pg_bulkload 插件导入数据报错》"
link_path = match.group(3) # 例如 "202412/20241210_01.md"
# 读取文件内容
try:
with open("blog/"+link_path, 'r', encoding='utf-8') as file:
file_content = file.read()
except FileNotFoundError:
print(f"文件 {link_path} 未找到,跳过此行。")
return
# 按照1024字符切分文本并生成JSONL格式内容
jsonl_result = split_text_into_jsonl(file_content)
return jsonl_result
def write_jsonl_to_file(jsonl_result, output_file):
# 将 JSONL 对象写入到输出的 jsonl 文件中
with open(output_file, 'a', encoding='utf-8') as out_file:
for item in jsonl_result:
out_file.write(json.dumps(item, ensure_ascii=False) + "\n")
"""
def split_jsonl(input_file, train_file, valid_file, train_ratio=0.8):
# 读取原始 JSONL 文件内容
with open(input_file, 'r', encoding='utf-8') as file:
lines = file.readlines()
# 按文章分割,确保每篇文章的所有部分都放在同一数据集里
article_parts = {}
current_article = None
for line in lines:
try:
data = json.loads(line)
text = data['text']
if '[INST]' in text:
# 找到新的文章部分
start_idx = text.index('[INST]') + len('[INST]')
end_idx = text.index('[/INST]')
article_name = text[start_idx:end_idx]
if article_name not in article_parts:
article_parts[article_name] = []
article_parts[article_name].append(data)
except json.JSONDecodeError:
continue
# 计算分割点
total_articles = len(article_parts)
train_size = int(total_articles * train_ratio)
# 随机打乱文章顺序,确保数据集分割的随机性
articles = list(article_parts.items())
random.shuffle(articles)
# 将数据分为训练集和验证集
train_articles = articles[:train_size]
valid_articles = articles[train_size:]
# 将训练集写入新的 JSONL 文件
with open(train_file, 'w', encoding='utf-8') as train_out:
for article, parts in train_articles:
for part in parts:
train_out.write(json.dumps(part, ensure_ascii=False) + "
\n")
# 将验证集写入新的 JSONL 文件
with open(valid_file, 'w', encoding='utf-8') as valid_out:
for article, parts in valid_articles:
for part in parts:
valid_out.write(json.dumps(part, ensure_ascii=False) + "\n")
print(f"数据分割完成!{train_file} 和 {valid_file} 已生成。")
"""# 示例:处理文件中的内容
file_path = 'blog/README.md'# 替换为你文件的路径
marker = "### 六、所有文档如下"
search_term = "polardb"# 匹配 'polardb',不区分大小写
lines_from_marker = get_lines_from_marker(file_path, marker, search_term)
output_file = 'output.jsonl'# 输出的 JSONL 文件
# 打印包含 'polardb' 的行并处理它们
for line in lines_from_marker:
jsonl_result = process_line(line.strip(), output_file)
if jsonl_result:
write_jsonl_to_file(jsonl_result, output_file)
print(f"处理完成!输出已保存至 {output_file}.")
"""
# 分割为训练集和验证集
train_file = 'train.jsonl' # 训练集 JSONL 文件
valid_file = 'valid.jsonl' # 验证集 JSONL 文件
split_jsonl(output_file, train_file, valid_file)
"
""量化, 降低精度
当前从hugging face下载的模型文件约14G.
$ cd ~/finetune_mistral/model
$ ll
total 28318176
-rw-r--r--@ 1 digoal staff 601B Jan 8 17:16 config.json
-rw-r--r--@ 1 digoal staff 23K Jan 8 17:22 model.safetensors.index.json
-rw-r--r--@ 1 digoal staff 1.9M Jan 8 17:22 tokenizer.json
-rw-r--r--@ 1 digoal staff 138K Jan 8 17:22 tokenizer_config.json
-rw-r--r--@ 1 digoal staff 574K Jan 8 17:22 tokenizer.model
-rw-r--r--@ 1 digoal staff 202B Jan 8 17:24 params.json
-rw-r--r--@ 1 digoal staff 414B Jan 8 17:24 special_tokens_map.json
-rw-r--r--@ 1 digoal staff 116B Jan 8 17:24 generation_config.json
-rw-r--r--@ 1 digoal staff 4.6G Jan 9 09:42 model-00001-of-00003.safetensors
-rw-r--r--@ 1 digoal staff 4.2G Jan 9 09:56 model-00003-of-00003.safetensors
-rw-r--r--@ 1 digoal staff 4.7G Jan 9 10:13 model-00002-of-00003.safetensors
-rw-r--r-- 1 digoal staff 62K Jan 9 11:17 valid.jsonl
-rw-r--r-- 1 digoal staff 13K Jan 9 11:22 train.jsonl
使用llms进行转换, 可以指定量化bits控制精度(及大小)
$ cd ~/mlx-examples/llms $ python -m mlx_lm.convert --help
usage: convert.py [-h] [--hf-path HF_PATH] [--mlx-path MLX_PATH] [-q] [--q-group-size Q_GROUP_SIZE] [--q-bits Q_BITS] [--dtype {float16,bfloat16,float32}] [--upload-repo UPLOAD_REPO] [-d]
Convert Hugging Face model to MLX format
options:
-h, --help show this help message and exit
--hf-path HF_PATH Path to the Hugging Face model.
--mlx-path MLX_PATH Path to save the MLX model.
-q, --quantize Generate a quantized model.
--q-group-size Q_GROUP_SIZE
Group size for quantization.
--q-bits Q_BITS Bits per weight for quantization.
--dtype {float16,bfloat16,float32}
Type to save the non-quantized parameters.
--upload-repo UPLOAD_REPO
The Hugging Face repo to upload the model to.
-d, --dequantize Dequantize a quantized model.
转换方法如下
$ python -m mlx_lm.convert --hf-path ~/finetune_mistral/model -q --q-group-size 64 --q-bits 2
转换后的模型默认放在当前目录的mlx_model目录里, 看你在哪执行上面这条命令. 如果在~/mlx-examples/llms执行则
$ cd mlx-examples/llms/mlx_model
$ ll
total 4465360
drwxr-xr-x 14 digoal staff 448B Jan 10 14:03 ..
-rw-r--r-- 1 digoal staff 2.1G Jan 10 14:03 model.safetensors
-rw-r--r-- 1 digoal staff 51K Jan 10 14:03 model.safetensors.index.json
-rw-r--r-- 1 digoal staff 138K Jan 10 14:03 tokenizer_config.json
-rw-r--r-- 1 digoal staff 414B Jan 10 14:03 special_tokens_map.json
-rw-r--r--@ 1 digoal staff 574K Jan 10 14:03 tokenizer.model
-rw-r--r-- 1 digoal staff 3.5M Jan 10 14:03 tokenizer.json
drwxr-xr-x 9 digoal staff 288B Jan 10 14:03 .
-rw-r--r-- 1 digoal staff 801B Jan 10 14:03 config.json
对转换后的模型执行微调
cd ~/mlx-examples/lora
python3 lora.py \
--train \
--model ~/mlx-examples/llms/mlx_model \
--data ~/finetune_mistral/jsonl \
--adapter-file ~/finetune_mistral/adapters.npz \
--batch-size 1 \
--lora-layers 4 \
--iters 1000
输出示例
Loading pretrained model
Total parameters 680.169M
Trainable parameters 0.426M
Loading datasets
Training
Iter 1: Val loss 3.979, Val took 67.505s
Iter 10: Train loss 3.374, It/sec 0.290, Tokens/sec 151.964
Iter 20: Train loss 3.372, It/sec 0.245, Tokens/sec 134.992
Iter 30: Train loss 3.009, It/sec 0.273, Tokens/sec 135.847
Iter 40: Train loss 3.085, It/sec 0.229, Tokens/sec 137.804
Iter 50: Train loss 3.183, It/sec 0.230, Tokens/sec 138.675
Iter 60: Train loss 2.766, It/sec 0.240, Tokens/sec 136.055
Iter 70: Train loss 2.955, It/sec 0.308, Tokens/sec 134.104
Iter 80: Train loss 3.013, It/sec 0.234, Tokens/sec 138.356
Iter 90: Train loss 2.776, It/sec 0.236, Tokens/sec 133.391
Iter 100: Train loss 2.887, It/sec 0.241, Tokens/sec 135.631
Iter 100: Saved adapter weights to /Users/digoal/finetune_mistral/adapters.npz.
参考
https://github.com/ml-explore/mlx-examples/tree/main/lora#memory-issues
https://github.com/apeatling/simple-guide-to-mlx-finetuning/blob/trunk/train-example.jsonl
https://www.geekyuncle.com/mlx-installation-and-environment-setup/
https://www.geekyuncle.com/part2-mlx-and-yi/
https://milvus.io/docs/zh/how_to_enhance_your_rag.md
切割文本
中文词汇训练:
https://zhuanlan.zhihu.com/p/639144223 https://github.com/taishan1994/sentencepiece_chinese_bpe
《AI大模型+全文检索+tf-idf+向量数据库+我的文章 系列之5 - 在 Apple Silicon Mac 上微调(fine-tuning)大型语言模型(LLM) 并发布GGUF》
https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3/tree/main
文末彩蛋:国产数据库周边生态
当然一款数据库要流行起来, 除了自己要强大, 还离不开生态. 用好周边生态工具, 管理水平战胜90%老司机!!! 下面简单介绍一下国产数据库周边生态.
1、管控软件
鸣嵩(前阿里云数据库总经理 / 研究员)等大佬们创业创办的云猿生, 核心产品是KubeBlocks. 他们的理念是让管理数据库和搭积木一样简单, 如果你要管理很多套并且种类(OLTP\OLAP\NoSQL\KV\TS\MQ等)很多的数据库产品, 推荐首选.
https://github.com/apecloud/kubeblocks
PG中文社区核心委员唐成老师的公司乘数开源的Clup, 专用管理PostgreSQL和PolarDB的集群管理软件, 如果你要管理很多套数据库, 推荐选择. 并且Clup还提供了企业版、自研的连接池、分布式存储、一体机、备份平台等, 是企业用户推荐之选.
https://www.csudata.com/
若航老司机开源的pigsty, 集成了300多个PG插件的PG集群和PolarDB集群管理软件, 如果你要管理很多套PG或PolarDB数据库, 且对插件有特别多的需求, 推荐选择.
https://pigsty.cc/zh/
2、审计监控诊断优化
翟总(曾经是我背后的男人)到海信聚好看后研发的 DBdoctor, 采用ebpf技术, 在对数据库几乎没有影响的情况下实时监控数据库和服务器的各项指标, 发现和诊断问题根因非常方便.
https://www.dbdoctor.cn/
天舟老哥的核心产品Bytebase 是位于您和数据库之间的中间件。它是数据库 DevOps 的 GitLab/GitHub,专为开发人员、DBA 和平台工程师打造。
https://bytebase.cc/docs/introduction/what-is-bytebase/
PawSQL, SQL优化和诊断产品.
D-Smart, Oracle老前辈白老大出品, 专注企业级市场, 将业界顶级DBA经验的产品化作品, 产品功能包括数据库监控、诊断、优化等.
https://www.modb.pro/db/567140
3、国产数据库IDE
IDE是开发者的必备工具,例如社区有pgAdmin, 国产IDE则可以看看老程序猿达刚老师的DeskUI:
https://www.deskui.com
4、数据同步&迁移&备份恢复
NineData, 老领导出去创业做的产品, 产品涵盖了数据同步、迁移、备份、比对、devops、chatDBA等.
https://www.ninedata.cloud/home
DSG, 非常老牌的数据库同步迁移企业级产品, 支持各种数据库的异构和同构迁移, 用他们的话说, 没有dsg搞不定的迁移, 比goldengate还牛.
https://www.dsgdata.com/
公开课
如果你对PolarDB学习感兴趣可以阅读这个公开课系列:
除了PolarDB还非常值得关注的几款PG栈国产数据库:
HaloDB(基于PG兼容PostgreSQL、Oracle、MySQL. http://www.halodbtech.com/ )、 IvorySQL(基于开源PG兼容PG、Oracle. https://www.ivorysql.org/zh-cn/ )、 ProtonBase(云原生分布式数仓. https://protonbase.com/ )、 成都文武数据库(https://ww-it.cn)
参考文档点击阅读原文获得
感谢关注我的github (https://github.com/digoal/blog) 及视频号: