编程技术分享

  • 首页
  1. 首页
  2. MongoDB
  3. 正文

MongoDB源码分析系列1——编译环境搭建

2023年11月2日 4062点热度 1人点赞 0条评论

1、背景

MongoDB是主流的NoSQL数据库,其弱化schema的文档存储方式在开发中可以提供很大的便利性,很多项目都将其作为主数据库存储重要的业务数据。作为经常和MongoDB打交道的开发有必要了解其底层原理,这有助于更好的去使用它。本系列会从源码层面介绍MongoDB底层实现,要读懂源码,编译调试是必不可少的,本文先从搭建MongoDB调试环境开始介绍。

2、编译环境

本文介绍的是Ubuntu平台MongoDB编译环境搭建。

2.1 版本说明

本文使用的系统或工具版本号:

  • ubuntu:20.04
  • gcc:9.4.0
  • make:4.2.1
  • python:2.7.18
  • mongo:4.0
  • wiredtiger:3.1.1

2.2 依赖工具

执行命令如下:

apt update
apt install -y vim
apt install -y zip
# scon使用git获取mongo源码仓库版本
apt install -y git
# 安装gcc、g++、make等工具
apt install -y build-essential
# 安装setuptools需要的依赖
apt install -y zlib1g
apt install -y zlib1g-dev
# 安装libcurl devel
apt install -y libcurl4-gnutls-dev
# 安装ssl,安装mongo依赖时需要
apt install -y libbz2-dev libffi-dev libssl-dev
# 安装boost
apt install -y libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev

2.3 安装python

下载源码编译安装python,mongo 4.0需要python2.7,本文选择版本2.7.18,下载地址为:https://www.python.org/downloads/。执行命令如下:

wget https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tgz
tar -zxf Python-2.7.18.tgz
cd Python-2.7.18
./configure
make
make install

2.4 安装setuptools

安装setuptools用于安装pip,下载地址为:https://pypi.python.org/pypi/setuptools。执行命令如下:

wget https://pypi.python.org/packages/45/29/8814bf414e7cd1031e1a3c8a4169218376e284ea2553cc0822a6ea1c2d78/setuptools-36.6.0.zip#md5=74663b15117d9a2cc5295d76011e6fd1
unzip setuptools-36.6.0.zip
cd setuptools-36.6.0
python setup.py install

2.5 安装pip

安装pip用于安装mongo依赖,下载地址为:https://pypi.python.org/pypi/pip。执行命令如下:

wget https://pypi.python.org/packages/11/b6/abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/pip-9.0.1.tar.gz#md5=35f01da33009719497f01a4ba69d63c9
tar -zxf pip-9.0.1.tar.gz
cd pip-9.0.1
python setup.py install

2.6 编译mongo源码

本文使用mongo 4.0,对应的wiredtiger引擎版本为3.1.1,执行命令如下:

git clone https://github.com/mongodb/mongo.git
git checkout -B v4.0 origin/v4.0
cd mongo
# 安装依赖
python -m pip install -r buildscripts/requirements.txt
# 禁用warning作为error
python buildscripts/scons.py core --disable-warnings-as-errors
# 创建db目录
mkdir -p /data/db
# 启动mongod服务
./mongod --directoryperdb
# gdb调试mongod
apt install -y gdb
gdb -p {mongod pid}

2.7 容器环境

以上步骤执行完后即可开始调试mongod服务了,不过因为每个人的环境差异,可能会遇到各种奇葩问题,为减少搭建环境的时间,可以采用容器来制作固定的调试环境,本文后续也是通过容器环境来调试mongo。制作镜像可以提前将依赖工具下载好放入目录,如下所示:

  • 构建镜像脚本sh
#!/bin/sh
docker build -t mongo-debug:1.0 -f ./Dockerfile .
  • 制作镜像Dockerfile
FROM ubuntu:20.04

WORKDIR /data

ADD ./ ./

# 安装依赖
RUN apt update && \
    apt install -y vim && \
    apt install -y git && \
    apt install -y build-essential && \
    apt install -y zlib1g && \
    apt install -y zlib1g-dev && \
    apt install -y zip && \
    apt install -y libcurl4-gnutls-dev && \
    apt install -y libbz2-dev libffi-dev libssl-dev && \
    apt install -y libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev

# 安装python
RUN tar -zxf Python-2.7.18.tgz && \
    cd Python-2.7.18 && \
    ./configure && \
    make && \
    make install

# 安装setuptools
# 下载地址:https://pypi.python.org/pypi/setuptools
# wget https://pypi.python.org/packages/45/29/8814bf414e7cd1031e1a3c8a4169218376e284ea2553cc0822a6ea1c2d78/setuptools-36.6.0.zip#md5=74663b15117d9a2cc5295d76011e6fd1
RUN unzip setuptools-36.6.0.zip && \
    cd setuptools-36.6.0 && \
    python setup.py install

# 安装pip
# 下载地址:https://pypi.python.org/pypi/pip
# wget https://pypi.python.org/packages/11/b6/abcb525026a4be042b486df43905d6893fb04f05aac21c32c638e939e447/pip-9.0.1.tar.gz#md5=35f01da33009719497f01a4ba69d63c9
RUN tar -zxf pip-9.0.1.tar.gz && \
    cd pip-9.0.1 && \
    python setup.py install

# 编译mongo
RUN cd mongo && \
    python -m pip install -r buildscripts/requirements.txt && \
    python buildscripts/scons.py core --disable-warnings-as-errors

CMD python pause.py
  • MongoDB源码目录mongo
  • 启动容器脚本sh
#/bin/sh
docker run --privileged -it -d -v /data/datacenter/mongodb:/data/db mongo-debug:1.0

注意:需要带--privileged提供内核参数的更改权限,否则无法通过gdb调试,会报错Could not attach to process, ptrace: Operation not permitted。

  • 容器常驻脚本py
import time
time.sleep(9999999)

3、gdb使用

Linux系统中常用的调试工具是gdb,后面我们也会采用gdb调试mongod,因此有必要熟悉gdb的用法。

首先是安装gdb,可以执行命令apt install -y gdb。

针对已经启动的服务,gdb可以通过指定pid attach到该服务,执行命令gdb -p {pid}。

进入gdb后,常用命令如下:

info threads //显示所有线程信息
thread {tid} //切换到指定线程
info breakpoints //简写info b,显示所有断点
break {函数名} //简写b {函数名},如:b mongo::BasicCommand::Invocation::run、b mongo::(anonymous namespace)::FindCmd::run
break {文件名:行数} //简写b {文件名:行数},如:b src/mongo/db/commands.cpp:517
continue //简写c,执行到下一断点
next //简写n,单步跟踪,遇到函数调用不进入函数体
step //简写s,单步跟踪,遇到函数调用进入函数体
delete {n} //删除第n个断点
delete breakpoints //删除所有断点
list //简写l,列出执行点附件源码
print {变量名} //打印变量值
set scheduler-locking on //调试加锁当前线程,停止所有其他线程调度,在多线程情况下很有用,防止调试过程中切换到其他线程
show scheduler-locking //显示线程的scheduler-locking状态,查看上面设置是否生效

4、调试mongo

4.1 启动mongod

编译mongo源码后会生成mongo、mongod,可以执行如下命令启动mongod服务:

mkdir -p /data/db && /data/mongo/mongod --directoryperdb #directoryperdb表示每个db单独目录

启动mongod后,可以通过ps -ef|grep mongod查看mongod进程:

4.2 查看线程信息

mongod服务会启动多个线程,可以通过ps -p {pid} -T查看线程情况:

当通过mongo客户端连接mongod后,再次查看线程情况:

进入gdb也可以查看mongod的线程相关信息,以下分别为显示所有线程、切换指定线程:

4.3 CURD调试

本文接下来会介绍单步跟踪CURD的调用栈。

4.3.1 Insert

单步跟踪insert语句的调用栈如下:

由调用栈可以看到class RecordStore提供了抽象的底层数据操作接口,由各存储引擎具体实现,MongoDB默认使用的是WiredTiger存储引擎,insert最终会调用wt引擎的insertRecords()进行实际的插入操作。

调用链如下:

src/mongo/db/service_entry_point_common.cpp/mongo::ServiceEntryPointCommon::handleRequest() //处理请求
=>src/mongo/db/service_entry_point_common.cpp/mongo::receivedCommands()
=>src/mongo/db/service_entry_point_common.cpp/mongo::execCommandDatabase()
=>src/mongo/db/service_entry_point_common.cpp/mongo::runCommandImpl()
=>src/mongo/db/commands/write_commands/write_commands.cpp/mongo::WriteCommand::InvocationBase::run()
=>src/mongo/db/commands/write_commands/write_commands.cpp/mongo::CmdInsert::Invocation::runImpl()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::performInserts()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::insertBatchAndHandleErrors()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::insertDocuments()
=>src/mongo/db/catalog/collection.h/mongo::Collection::insertDocuments()
=>src/mongo/db/catalog/collection_impl.cpp/mongo::CollectionImpl::insertDocuments()
=>src/mongo/db/catalog/collection_impl.cpp/mongo::CollecctionImpl::_insertDocuments()
=>src/mongo/db/storage/record_store.h/mongo::RecordStore::insertRecords() //class RecordStore提供抽象存储接口,底层由存储引擎实现
=>src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp/mongo::WiredTigerRecordStore::insertRecords() //调用wt存储引擎

4.3.2 Update

单步跟踪update语句的调用栈如下:

由调用栈可以看到update最终会调用wt引擎的updateRecord()进行实际的更新操作,源码中执行update时是需判断能否inplace update,进而采用不同的策略,此处调用栈是非inplace update。

调用链如下:

src/mongo/db/service_entry_point_common.cpp/mongo::ServiceEntryPointCommon::handleRequest()
=>src/mongo/db/service_entry_point_common.cpp/mongo::receivedCommands()
=>src/mongo/db/service_entry_point_common.cpp/mongo::execCommandDatabase()
=>src/mongo/db/service_entry_point_common.cpp/mongo::runCommandImpl()
=>src/mongo/db/commands/write_commands/write_commands.cpp/mongo::WriteCommand::InvocationBase::run()
=>src/mongo/db/commands/write_commands/write_commands.cpp/mongo::CmdUpdate::Invocation::runImpl()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::performUpdates()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::performSingleUpdateOp()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::executePlan()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::getNext()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::getNextImpl()
=>src/mongo/db/exec/plan_stage.cpp/mongo::PlanStage::work()
=>src/mongo/db/exec/update.cpp/mongo::UpdateStage::doWork()
=>src/mongo/db/exec/update.cpp/mongo::UpdateStage::transformAndUpdate()
=>src/mongo/db/catalog/collection_impl.cpp/mongo::CollectionImpl::updateDocument()
=>src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp/mongo::WiredTigerRecordStore::updateRecord() //调用wt存储引擎

4.3.3 Find

单步跟踪find语句的调用栈如下:

由调用栈可以看到find最终会调用wt引擎进行迭代查找。

调用链如下:

src/mongo/db/service_entry_point_common.cpp/mongo::ServiceEntryPointCommon::handleRequest()
=>src/mongo/db/service_entry_point_common.cpp/mongo::receivedCommands()
=>src/mongo/db/service_entry_point_common.cpp/mongo::execCommandDatabase()
=>src/mongo/db/service_entry_point_common.cpp/mongo::runCommandImpl()
=>src/mongo/db/commands.cpp/mongo::BasicCommand::Invocation::run()
=>src/mongo/db/commands/find_cmd.cpp/mongo::FindCmd::run()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::getNext()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::getNextImpl()
=>src/mongo/db/exec/plan_stage.cpp/mongo::PlanStage::work()
=>src/mongo/db/exec/collection_scan.cpp/mongo::CollectionScan::doWork()
=>src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp/mongo::WiredTigerRecordStoreCursorBase::next() //调用wt存储引擎

4.3.4 Delete

单步跟踪delete语句的调用栈如下:

由调用栈可以看到delete最终会调用wt引擎的deleteRecord进行删除。

调用链如下:

src/mongo/db/service_entry_point_common.cpp/mongo::ServiceEntryPointCommon::handleRequest()
=>src/mongo/db/service_entry_point_common.cpp/mongo::receivedCommands()
=>src/mongo/db/service_entry_point_common.cpp/mongo::execCommandDatabase()
=>src/mongo/db/service_entry_point_common.cpp/mongo::runCommandImpl()
=>src/mongo/db/commands/write_commands/write_commands.cpp/mongo::WriteCommand::InvocationBase::run()
=>src/mongo/db/commands/write_commands/write_commands.cpp/mongo::CmdDelete::Invocation::runImpl()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::performDeletes()
=>src/mongo/db/ops/write_ops_exec.cpp/mongo::performSingleDeleteOp()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::executePlan()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::getNext()
=>src/mongo/db/query/plan_executor.cpp/mongo::PlanExecutor::getNextImpl()
=>src/mongo/db/exec/plan_stage.cpp/mongo::PlanStage::work()
=>src/mongo/db/exec/delete.cpp/mongo::DeleteStage::doWork()
=>src/mongo/db/exec/delete.cpp/mongo::Collection::deleteDocument()
=>src/mongo/db/catalog/collection_impl.cpp/mongo::CollectionImpl::deleteDocument()
=>src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp/mongo::WiredTigerRecordStore::deleteRecord() //调用wt存储引擎
标签: 暂无
最后更新:2023年11月2日

jemuel

这个人很懒,什么都没留下

点赞
文章目录
  • 1、背景
  • 2、编译环境
    • 2.1 版本说明
    • 2.2 依赖工具
    • 2.3 安装python
    • 2.4 安装setuptools
    • 2.5 安装pip
    • 2.6 编译mongo源码
    • 2.7 容器环境
  • 3、gdb使用
  • 4、调试mongo
    • 4.1 启动mongod
    • 4.2 查看线程信息
    • 4.3 CURD调试
      • 4.3.1 Insert
      • 4.3.2 Update
      • 4.3.3 Find
      • 4.3.4 Delete
最新 热点 随机
最新 热点 随机
K8S源码分析系列2—远程调试K8S组件 Volcano源码分析系列—调度篇 K8S源码分析系列1—搭建K8S调试集群 K8S Controller开发 6.5840 Lab 1: MapReduce MongoDB源码分析系列1——编译环境搭建
K8S源码分析系列2—远程调试K8S组件
K8S源码分析系列1—搭建K8S调试集群 MySQL源码分析系列3——登录协议解析 大数据平台之binlog采集方案 Java热更新 Golang优先级调度 Volcano源码分析系列—调度篇

COPYRIGHT © 2021 www.miaozhouguang.com. ALL RIGHTS RESERVED.

THEME KRATOS MADE BY VTROIS

粤ICP备2022006024号

粤公网安备 44030602006568号