MongoDB备份与恢复实战(二)

前戏 老花: 话接上回, 我们今天总结下MongoDB分片集群部署模式下, 需要注意一些地方。 分布式全量备份和恢复实践 首先, 明确一点, mongdump/mongorestore支持热备份和热恢复。 为了提升备份工作流的并发性能, 我们可以将命令改为多个shard同时后台下发, 那么如何确保这些备份任务及时下发和反馈进度呢? 我们可以引入一个管控面, 这可以是一个组件暴露成接口, 也可以是k8s上的一个job或者cronjob, 定时去执行。 不管怎么说, 它理论上应该有这些功能: 异步下发指令。 监控指令完成。 执行下一个指令。 最终返回备份成功或者失败的状态, 以及一些其他附加信息。 我们把这个流程画成时序图。 全备时序图 主要步骤: 管控系统遍历每个Shard查找主节点:管控系统首先需要确定每个分片(Shard)的主节点。 并行执行mongodump异步命令:对于每个Shard,管控系统并行执行mongodump命令来备份数据。这里需要进行二次封装,确保每次执行后都能写入一个本地的status文件,记录备份的成功或失败状态。 轮询每个Shard上的状态文件:管控系统需要轮询每个Shard上的状态文件,检查备份是否成功。如果所有Shard都返回成功,则备份成功;如果存在失败或文件不存在,则需要继续轮询。 完成Shard备份后执行Configsvr备份:一旦所有Shard的备份都成功完成,管控系统将开始备份Configsvr。这同样需要查找Configsvr的主节点,然后执行备份。 Configsvr备份结果:Configsvr备份完成后,管控系统将收到备份结果,完成整个备份流程。 由此可见, 封装mongodump后的需要附加以下功能: 对接不同的灾备产品, 比如oss 对备份进行切分, 将备份上传到存储系统 上传备份数据到远程存储 全量恢复时序图 主要步骤: 管控系统遍历每个Shard查找主节点:管控系统首先需要确定每个分片(Shard)的主节点。 主节点上下载远程的存储文件:管控系统从远程存储下载备份文件。 重命名本地存储或发起本地备份:管控系统将下载的文件重命名或备份到本地存储,以便在恢复程序报错时支持数据回滚。 并行执行mongorestore异步命令:对于每个Shard,管控系统并行执行mongorestore命令来恢复数据。这里需要进行二次封装,确保每次执行后都能写入一个本地的status文件,记录恢复的成功或失败状态。 轮询每个Shard上的状态文件:管控系统需要轮询每个Shard上的状态文件,检查恢复是否成功。如果所有Shard都返回成功,则恢复成功;如果存在失败或文件不存在,则需要继续轮询。 完成Shard恢复后执行Configsvr恢复:一旦所有Shard的恢复都成功完成,管控系统将开始恢复Configsvr。这同样需要查找Configsvr的主节点,然后执行恢复。 Configsvr恢复结果:Configsvr恢复完成后,管控系统将收到恢复结果,完成整个恢复流程。 上面的时序新增了远程存储, 但是恢复的时候需要先下载到容器中, 可能导致数据有可能的三份膨胀: 下载到本地的远程的备份 本节点的原有备份数据 恢复过程中的数据 除了数据膨胀, 如果原先孵化pod的pvc存储不够, 可能会直接导致失败。 可能有人会这样想: 直接把本地数据删除, 然后远程下载解压不香吗? 还有人这样想, 我直接搞个新集群, 恢复完了, 再重命名回来? 第一个问题, 可以是可以, 但是服务就相当于宕机了, 而且恢复失败, 回滚也是比较费事了。第二个问题呢, 云原生里面的设计, 多租户隔离比较常见的, 可能这么操作比较不优雅。 ...

十二月 17, 2024 · 1 分钟 · 137 字 · zhu733756

MongoDB备份与恢复实战(一)

前戏 小白: 嘿, 老花。今天能聊聊 MongoDB 的数据备份与恢复, 特别是涉及到用户和用户权限数据的部分。这可是个技术活儿。 老花: 好的, 在回答你的问题前, 我们先思考几个问题。 关于备份恢复的几个问题 我们先搞清楚要备份的数据有哪些? 生产数据 用户 用户权限 备份是追求准确性还是性能? 从master备份, 可能会影响写数据, 但数据是全的 从slave备份, 不影响读写, 单如果主从同步延迟, 可能会丢数据 备份的集群模式是分片还是副本? 分片模式: 需要从多个节点上同时备份, 以提高性能, 注意, 需要备份configsvr。 副本模式: 只需要考虑从一个节点上备份。 要做增量备份还是全量备份? 恢复到其他实例? 管理员账号要调过还是覆盖? (这个只能业务处理了) 备份恢复工具简介 mongodump mongodump是官方提供的一个备份工具, 它支持一些参数: -h, –host:这个参数用来指定远程 MongoDB 数据库的地址。如果你不设置这个参数, mongodump 默认会尝试连接到本地的 MongoDB 实例。 –port:这个参数用来指定远程 MongoDB 数据库的端口号。默认情况下, mongodump 会连接到端口 27017, 这是 MongoDB 的默认端口。 -u, –username:如果你的数据库设置了认证, 这个参数就非常重要了。它用来指定连接远程数据库的用户名。 -p, –password:与用户名相对应, 这个参数用来输入对应用户名的密码, 以验证身份。 -d, –db:这个参数让你指定想要备份的特定数据库。 -c, –collection:如果你只想备份数据库中的某个特定集合, 这个参数就能派上用场。 -o, –out:这个参数用来指定备份文件的输出目录。备份完成后, 所有数据都会存储在这个目录下。 -q, –query:这个参数允许你指定查询条件, 从而只备份满足特定条件的数据。 -j, –numParallelCollections:这个参数用来设置并行转储的集合数, 可以提高备份效率。默认情况下, mongodump 会并行转储 4 个集合。 –gzip:如果你想要节省存储空间, 可以使用这个参数。它会将备份文件压缩成 gzip 格式。 –oplog:这个参数允许你使用 oplog(操作日志)进行时间点快照, 这对于需要精确到特定时间点的数据备份非常有用。` –authenticationDatabase:这个参数用来指定进行用户认证的数据库。有时候, 认证数据库和你要备份的数据库不是同一个。 全量备份 全量备份有两个思路, 挨个db进行备份, 或者通过--oplog来做日志全量备份。 ...

十二月 13, 2024 · 5 分钟 · 1052 字 · zhu733756

聊聊MongoDB BSON数据格式

前戏 小白:老花,最近我在研究MongoDB,听说它使用BSON作为数据格式,这BSON到底是个啥玩意儿? 老花:哈哈,小白,BSON是Binary JSON的缩写,它是一种类JSON的二进制编码格式。BSON的设计目的, 是为了在MongoDB中存储和交换文档数据。它继承了JSON的灵活性和可读性,同时增加了一些额外的数据类型,比如日期、二进制数据和代码等。 小白:那为啥要用BSON这种数据存储格式呢? 老花:有几个原因。首先,BSON是二进制格式,这意味着它比JSON更紧凑,传输效率更高。其次,BSON支持更多的数据类型,这使得它能够存储更复杂的数据结构。再者,BSON是自描述的,每个字段都有类型信息,这使得解析BSON数据时更加方便。 小白:听起来挺酷的,那我们怎么解析BSON数据呢? BSON 数据格式 解析BSON数据其实很简单。 在 Go 语言中,MongoDB的官方驱动提供了很好的支持。你可以使用go.mongodb.org/mongo-driver/bson这个包来处理BSON数据。 比如,你可以这样解析一个BSON文档: var doc bson.M err := bson.Unmarshal(data, &doc) if err != nil { log.Fatal(err) } fmt.Println(doc) 这里的data是你从 MongoDB 获取的 BSON 格式的字节切片,bson.M是一个可以存储任何类型值的 map,非常适合用来解析BSON文档。 如何实现 BSON 模糊搜索? 小白:那如果我想对 BSON 数据进行模糊搜索,比如搜索名字中包含某个字符串的文档? 老花:这个也好办。在 MongoDB 中,你可以使用正则表达式来进行模糊搜索。在 Go 语言中,你可以这样构建查询: import "go.mongodb.org/mongo-driver/bson" // 假设我们要搜索名字中包含"haha"的文档 filter := bson.M{"name": bson.M{"$regex": "haha", "$options": "i"}} // "i"代表忽略大小写 // 然后使用这个 filter 来查询数据库 cursor, err := collection.Find(context.TODO(), filter) if err != nil { log.Fatal(err) } // 遍历结果 for cursor.Next(context.TODO()) { var result bson.M err := cursor.Decode(&result) if err != nil { log.Fatal(err) } fmt.Println(result) 这段代码会查询所有 name 字段中包含haha的文档,并且忽略大小写。 在 MongoDB 中,除了$regex 算子用于模糊搜索外,还有一些其他的算子可以用于不同的搜索场景: ...

十二月 13, 2024 · 5 分钟 · 935 字 · zhu733756

Loki Stack收集MongoDB日志最佳实践(下)

前戏 小白: 前文我们提到了 Fluent Bit 采集器, 我对它的一些配置信息很感兴趣, 它是如何高效采集的? 老花:Fluent Bit 作为一个轻量级的日志收集器,特别适合在 Kubernetes 环境中作为 DaemonSet 运行。它的核心配置涉及服务(services), 解析器(parsers),输入配置(inputs), 输出配置(outputs)等。总结来说, 一套日志流可以定义多个插件, 通过tag和match来流转。 Fluent Bit 核心配置解析 Fluent Bit 架构图 在这个时序图中: Inputs:输入插件收集来自不同输入源的日志记录。 Parsers:解析器插件解析输入的日志记录。 Filters:过滤器插件对日志记录进行特定的修改,如添加或删除键值对,丰富特定元数据,或基于特定条件丢弃记录。 Storage:处理后的数据被认为是安全状态(要么在内存中,要么在文件系统中),然后记录通过适当的输出目的地进行路由。 StreamProcessor:流处理器是一个独立的子系统,它检查存储接口上的新记录。通过配置,流处理器可以附加到来自特定输入插件的记录,或通过应用标签和匹配规则。 Outputs:输出插件将处理后的数据发送到配置的输出目的地。 多线程 Fluent Bit会自动管理输入和输出插件的线程,确保在多线程环境中高效运行。过滤器始终在主线程中运行,而处理器则在各自的输入或输出的独立线程中运行。 内存管理 在容器化环境中,估算Fluent Bit的内存使用量至关重要。可以通过设置Mem_Buf_Limit选项来限制输入插件的内存使用。例如,如果设置Mem_Buf_Limit为 10MB,输出插件在最坏情况下可能会使用20MB,因此总内存需求应为30MB(10MB + 20MB),再加上一定的安全余量。 增量采集 Fluent Bit 通过检查日志文件的偏移量来实现增量采集。它会记录每个文件的最后读取位置,并在下一次采集时从该位置开始,确保不会重复采集。 缓冲与存储 Fluent Bit 通过缓冲机制临时存储处理后的数据,直到数据准备好被发送。 Fluent Bit 使用内存作为主要的临时存储位置,但在某些场景下,使用基于文件系统的持久缓冲机制可以提供聚合和数据安全能力。 输入插件发出的记录被引擎组合在一起形成块,通常大小约为 2MB,默认仅在内存中创建。 如果仅设置内存作为缓冲,它将尽可能多地在内存中存储数据。这是最快的机制,但如果服务因网络慢或远程服务无响应而无法快速分发记录,Fluent Bit 的内存使用量将增加。 背压问题 当日志或数据的产生速度, 超过其被刷新到某些目的地的能力时,会产生背压,导致服务内存消耗增加。Fluent Bit通过Mem_Buf_Limit和storage.Max_Chunks_Up配置参数限制输入插件可以摄入的数据量。 核心配置项 buf_chunk_size: 缓冲区块大小,用于存储日志数据。 buf_max_size: 缓冲区最大大小。 batch_size: 每个批次发送的日志条目数。 batch_wait: 等待更多日志条目到达以填满批次的时间。 mem_buf_limit: 内存缓冲区限制。 mem_buf_flush_count: 触发缓冲区刷新的日志条目数。 这些配置影响内存的使用, 是否产生背压, 以及写入后端outputs的效率(攒批)。 ...

十二月 9, 2024 · 3 分钟 · 622 字 · zhu733756

Loki Stack收集MongoDB日志最佳实践(上)

前戏 老花:小白,我们今天来聊聊收集容器日志的那些事儿。 小白:好啊,老花。我听说有好几种方法可以收集容器日志,你能给我讲讲吗? 常见收集容器日志的方法 老花:当然可以。最常见的有两种方法:Sidecar和DaemonSet。 Sidecar:俗称边车, 在每个需要收集日志的容器旁边运行一个Sidecar容器,专门负责日志收集。这种方法的好处是可以为每个应用提供定制化的日志收集配置,但缺点是资源消耗较大,因为每个应用都需要额外的容器。 DaemonSet:在Kubernetes集群的每个节点上运行一个Pod,负责收集该节点上所有容器的日志。优点是资源消耗较小,因为每个节点只需要一个Pod。缺点是配置不够灵活,因为所有容器共享同一个日志收集配置。 小白:我明白了,那我们通常用哪种方法呢? 老花:这取决于你的具体需求。如果你需要高度定制化的日志收集策略,可能会选择Sidecar。如果你更关心资源消耗,DaemonSet可能更适合。 日志收集系统优缺点 老花:不管是哪种收集方式, 都需要暴露日志路径给收集器。日志除了收集, 通常还有轮转、转换、存储和可视化查询等组件整合在一起, 组成强大日志收集系统。 我们来聊聊几个常见的日志收集系统: 采集、转换、扭转: Loki:轻量级,易于水平扩展,但不支持全文搜索。 Fluentd/Fluent Bit:Fluent Bit 轻量级,插件丰富,Fluentd 插件丰富,支持多种数据源和目的地,灵活的配置,适合大规模部署。 Logstash:功能强大,但资源消耗大,配置复杂。 Filebeat:轻量级,易于配置,但功能不如 Logstash。 网易和阿里开源的采集组件也很强大。 存储: Elasticsearch/ OpenSearch,搜索能力强,但成本高; ClickHouse(CK),查询速度快,成本较低。 套件: ELK Stack(Elasticsearch, Logstash, Kibana):功能强大,但资源消耗大,配置复杂。 EFK Stack(Elasticsearch, Fluent, Kibana):功能强大,fluent-bit资源消耗低, 插件不如fluentd丰富,配置容易。 Loki on grafana:采集组件可以使用promtail或者fluent-bit, 不需要额外的存储, 还自带可视化界面 UI, 多租户, 读写分离, 但不支持全文搜索。 各种采集器+ Kafka + ClickHouse +ClickVisual 等等。 小白:听起来每个系统都有它的优缺点,我们应该怎么选择呢? 老花:确实,选择哪个系统取决于你的具体需求,比如预算、资源、搜索需求等。 Loki 接入 MongoDB 分片集群日志最佳实践 小白:老花,我们公司正在用 MongoDB 分片集群,你觉得用 Loki 来收集日志怎么样? 老花:这是个好问题。Loki 可以很好地集成 MongoDB 分片集群的日志。我们可以部署 Promtail或者Fluent-bit 等采集器 作为 DaemonSet 来收集 MongoDB 的日志,然后将它们发送到 Loki。 ...

十二月 6, 2024 · 3 分钟 · 553 字 · zhu733756

深入了解MongoDB日志系统

前戏 小白: 嘿,老花,我最近在研究 MongoDB 的日志系统,你能给我介绍一下 MongoDB 有哪些日志吗? 老花: 当然可以,小白。MongoDB 主要有四种日志:系统日志、Journal 日志、主从日志(oplog)和慢查询日志 。让我一一解释给你听。 首先, 我们看下不同组件的配置文件描述信息。 不同组件的配置文件 shardsvr 下面我们演示从之前部署的集群上获取一个数据节点shardsvr的启动配置: $ kubectl -n mongodb-sharded exec -it mongodb-sharded-shard0-data-0 -- bash $ ps -ef UID PID PPID C STIME TTY TIME CMD 1001 1 0 2 Nov30 ? 00:46:47 /opt/bitnami/mongodb/bin/mongod --config=/opt/bitnami/mongodb/conf/mongodb.conf $ cat /opt/bitnami/mongodb/conf/mongodb.conf # mongod.conf # for documentation of all options, see: # http://docs.mongodb.org/manual/reference/configuration-options/ # where and how to store data. storage: dbPath: /bitnami/mongodb/data/db directoryPerDB: false # where to write logging data. systemLog: destination: file quiet: false logAppend: true logRotate: reopen path: /opt/bitnami/mongodb/logs/mongodb.log verbosity: 0 # network interfaces net: port: 27017 unixDomainSocket: enabled: true pathPrefix: /opt/bitnami/mongodb/tmp ipv6: false bindIpAll: true #bindIp: # replica set options replication: replSetName: mongodb-sharded-shard-0 enableMajorityReadConcern: true # sharding options sharding: clusterRole: shardsvr # process management options processManagement: fork: false pidFilePath: /opt/bitnami/mongodb/tmp/mongodb.pid # set parameter options setParameter: enableLocalhostAuthBypass: false # security options security: authorization: enabled keyFile: /opt/bitnami/mongodb/conf/keyfile 这个 MongoDB 配置文件包含了多个部分,每个部分都用于设置 MongoDB 实例的不同配置选项。以下是对每个部分的详细分析: ...

十二月 3, 2024 · 6 分钟 · 1067 字 · zhu733756

实战 | 用golang撸一个极简MongoDB测试客户端?

前戏 小白:你好,老花!最近有个刁民让我测试部署的分片集群是否可用, 除了上官方上下载mongosh, 还有其他思路? 老花:当然有的! 我们可以用golang直接撸一个! 我们先整理一个功能清单: mclient flags: --uri mongodb 连接地址 --user 测试创建随机用户,并返回用户和密码 --insert 测试写入数据, 打印写入的随机条数 --read 测试插入随机数据, 并读取改数据 --remove 测试插入随机数据, 并删除该数据, 返回删除条数 搭建golang环境 下载 Go 语言 SDK 工具包:访问 Go 官网下载适合Linux的安装包。 解压安装包到指定目录,例如/root/go。 配置环境变量: 使用root权限编辑/etc/profile文件。 添加export GOROOT=/root/go和export PATH=$PATH:$GOROOT/bin。 执行source /etc/profile刷新配置。 测试配置是否生效,运行go version。 配置 Go 语言开发工具 可以选择多种开发工具,如 Visual Studio Code、GoLand 等,并安装相应的 Go 语言插件以支持智能提示、编译运行等功能 。 配置国内代理 由于国内网络环境的特殊性,配置国内代理可以加速 Go 模块的下载。可以通过设置环境变量 GOPROXY 来实现: go env -w GOPROXY=https://goproxy.cn,direct go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/ mclient 功能实现 连接数据库 client, err := mongo.NewClient(options.Client().ApplyURI(uri)) if err != nil { log.Fatalf("Failed to create MongoDB client: %v", err) } // 设置连接超时 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // 连接到MongoDB集群 err = client.Connect(ctx) if err != nil { log.Fatalf("Failed to connect to MongoDB cluster: %v", err) } defer client.Disconnect(ctx) // 确认连接成功,发送ping命令 err = client.Ping(ctx, readpref.Primary()) if err != nil { log.Fatalf("Failed to ping MongoDB cluster: %v", err) } fmt.Println("Connected to MongoDB cluster successfully!") 创建随机用户 func createRandomUser(client *mongo.Client) { collection := client.Database("admin").Collection("system.users") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() rand.Seed(time.Now().UnixNano()) username := fmt.Sprintf("user%d", rand.Int()) password := fmt.Sprintf("password%d", rand.Int()) user := bson.M{"user": username, "pwd": password, "roles": []bson.M{{"role": "readWrite", "db": "testdb"}}} _, err := collection.InsertOne(ctx, user) if err != nil { log.Fatalf("Failed to create user: %v", err) } fmt.Printf("User created with username: %s and password: %s\n", username, password) } 插入随机数据 func insertRandomDocuments(client *mongo.Client, num int) { collection := client.Database("testdb").Collection("testdata") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() var documents []interface{} for i := 0; i < num; i++ { documents = append(documents, bson.M{"name": fmt.Sprintf("hahahaa%d", i), "age": rand.Intn(100)}) } result, err := collection.InsertMany(ctx, documents) if err != nil { log.Fatalf("Failed to insert documents: %v", err) } fmt.Printf("Inserted %d documents with IDs: %v\n", len(result.InsertedIDs), result.InsertedIDs) } 读取和删除数据 func insertAndReadRandomDocuments(client *mongo.Client, num int) { collection := client.Database("testdb").Collection("testdata") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() var documents []interface{} for i := 0; i < num; i++ { documents = append(documents, bson.M{"name": fmt.Sprintf("hahahaa%d", i), "age": rand.Intn(100)}) } result, err := collection.InsertMany(ctx, documents) if err != nil { log.Fatalf("Failed to insert documents: %v", err) } var results []bson.M cur, err := collection.Find(ctx, bson.M{}) if err != nil { log.Fatalf("Failed to read documents: %v", err) } defer cur.Close(ctx) for cur.Next(ctx) { var elem bson.M err := cur.Decode(&elem) if err != nil { log.Fatal(err) } results = append(results, elem) } if err := cur.Err(); err != nil { log.Fatal(err) } fmt.Println("Documents read from database:", results) } func insertAndRemoveRandomDocuments(client *mongo.Client, num int) { collection := client.Database("testdb").Collection("testdata") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() var documents []interface{} for i := 0; i < num; i++ { documents = append(documents, bson.M{"name": fmt.Sprintf("hahahaa%d", i), "age": rand.Intn(100)}) } result, err := collection.InsertMany(ctx, documents) if err != nil { log.Fatalf("Failed to insert documents: %v", err) } filter := bson.M{"name": bson.M{"$in": []string{"hahahaa0"}}} deleteResult, err := collection.DeleteMany(ctx, filter) if err != nil { log.Fatalf("Failed to delete documents: %v", err) } fmt.Printf("Deleted %d documents\n", deleteResult.DeletedCount) } 完整代码 package main import ( "context" "flag" "fmt" "log" "math/rand" "time" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/mongo/readpref" ) // 定义命令行参数 var uri string var createUserFlag bool var insertFlag bool var readFlag bool var removeFlag bool var numDocuments int func init() { // 默认URI设置为localhost,用户可以通过命令行参数--uri来覆盖 flag.StringVar(&uri, "uri", "mongodb://localhost:27017", "MongoDB connection URI") flag.BoolVar(&createUserFlag, "user", false, "Test creating a random user") flag.BoolVar(&insertFlag, "insert", false, "Test inserting random data") flag.BoolVar(&readFlag, "read", false, "Test inserting and reading random data") flag.BoolVar(&removeFlag, "remove", false, "Test inserting and removing random data") flag.IntVar(&numDocuments, "num", 1, "Number of random documents to insert") } func main() { flag.Parse() // 解析命令行参数 // 创建MongoDB客户端 client, err := mongo.NewClient(options.Client().ApplyURI(uri)) if err != nil { log.Fatalf("Failed to create MongoDB client: %v", err) } // 设置连接超时 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // 连接到MongoDB集群 err = client.Connect(ctx) if err != nil { log.Fatalf("Failed to connect to MongoDB cluster: %v", err) } defer client.Disconnect(ctx) // 确认连接成功,发送ping命令 err = client.Ping(ctx, readpref.Primary()) if err != nil { log.Fatalf("Failed to ping MongoDB cluster: %v", err) } fmt.Println("Connected to MongoDB cluster successfully!") if createUserFlag { createRandomUser(client) } if insertFlag { insertRandomDocuments(client, numDocuments) } if readFlag { insertAndReadRandomDocuments(client, numDocuments) } if removeFlag { insertAndRemoveRandomDocuments(client, numDocuments) } } func createRandomUser(client *mongo.Client) { collection := client.Database("admin").Collection("system.users") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() rand.Seed(time.Now().UnixNano()) username := fmt.Sprintf("user%d", rand.Int()) password := fmt.Sprintf("password%d", rand.Int()) user := bson.M{"user": username, "pwd": password, "roles": []bson.M{{"role": "readWrite", "db": "testdb"}}} _, err := collection.InsertOne(ctx, user) if err != nil { log.Fatalf("Failed to create user: %v", err) } fmt.Printf("User created with username: %s and password: %s\n", username, password) } func insertRandomDocuments(client *mongo.Client, num int) { collection := client.Database("testdb").Collection("testdata") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() var documents []interface{} for i := 0; i < num; i++ { documents = append(documents, bson.M{"name": fmt.Sprintf("hahahaa%d", i), "age": rand.Intn(100)}) } _, err := collection.InsertMany(ctx, documents) if err != nil { log.Fatalf("Failed to insert documents: %v", err) } fmt.Printf("Inserted %d documents with IDs: %v\n", len(result.InsertedIDs), result.InsertedIDs) } func insertAndReadRandomDocuments(client *mongo.Client, num int) { collection := client.Database("testdb").Collection("testdata") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() var documents []interface{} for i := 0; i < num; i++ { documents = append(documents, bson.M{"name": fmt.Sprintf("hahahaa%d", i), "age": rand.Intn(100)}) } _, err := collection.InsertMany(ctx, documents) if err != nil { log.Fatalf("Failed to insert documents: %v", err) } var results []bson.M cur, err := collection.Find(ctx, bson.M{}) if err != nil { log.Fatalf("Failed to read documents: %v", err) } defer cur.Close(ctx) for cur.Next(ctx) { var elem bson.M err := cur.Decode(&elem) if err != nil { log.Fatal(err) } results = append(results, elem) } if err := cur.Err(); err != nil { log.Fatal(err) } fmt.Println("Documents read from database:", results) } func insertAndRemoveRandomDocuments(client *mongo.Client, num int) { collection := client.Database("testdb").Collection("testdata") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() var documents []interface{} for i := 0; i < num; i++ { documents = append(documents, bson.M{"name": fmt.Sprintf("hahahaa%d", i), "age": rand.Intn(100)}) } _, err := collection.InsertMany(ctx, documents) if err != nil { log.Fatalf("Failed to insert documents: %v", err) } filter := bson.M{"name": bson.M{"$in": []string{"hahahaa0"}}} deleteResult, err := collection.DeleteMany(ctx, filter) if err != nil { log.Fatalf("Failed to delete documents: %v", err) } fmt.Printf("Deleted %d documents\n", deleteResult.DeletedCount) } 编译二进制 编译命令 编译单个文件:go build -o myapp myapp.go。 编译模块:go build -mod=readonly -o myapp ./...。 编译交叉平台:GOOS=windows GOARCH=amd64 go build -o myapp_windows_amd64 myapp.go。 编译优化 优化代码:避免不必要的包依赖,使用 Go 的性能分析工具(如 pprof),优化数据结构和算法。 优化编译选项:使用-gcflags="all=-N -l"禁用垃圾回收和内联优化,使用-buildmode=pie生成位置无关可执行文件。 优化构建工具:使用 Makefile 或构建系统(如 Bazel、Maven)自动化编译过程,使用并行编译提高编译速度。 测试效果 使用上一篇博客部署的集群测试, 拿到mongos的ip: 10.244.2.2: ...

十一月 29, 2024 · 7 分钟 · 1336 字 · zhu733756

MongoDB分片集群在k8s上的部署实战(二)

前戏 小白:你好,老花!前面我们已经用kind创建了一个k8s集群, 接下来我们怎么部署sharded cluster? 老花:当然可以,小白!我们先从 Helm 的安装开始,然后详细介绍 Helm 中的每个角色配置,最后解释 Helm 应用是如何运行起来的。 helm 简介 安装 helm 老花:Helm 是 Kubernetes 的包管理器,它帮助我们管理 Kubernetes 应用。安装 Helm 的步骤如下: 下载 Helm:访问 Helm 的官方 GitHub 页面,下载最新版本的 Helm。 解压并移动 Helm 到你的 PATH 中: tar -zxvf helm-v3.0.0-linux-amd64.tar.gz cd linux-amd64 sudo mv helm /usr/local/bin/ 验证 Helm 是否安装成功: helm version 老花:Helm也安装好了,我们可以开始部署 MongoDB Sharded 集群了。再安装之前, 我们先了解 Helm 的基本命令。 Helm 常见命令汇总 helm init:初始化 Tiller(在 Helm 3 中不再需要)。 helm repo add <repo_name> <repository_url>:添加一个新的 Helm 仓库。 helm repo list:列出所有已添加的 Helm 仓库。 helm repo update:更新本地仓库的缓存。 helm search repo <keyword>:在所有仓库中搜索 Chart。 helm search hub <keyword>:在 Helm Hub 中搜索 Chart。 helm install <release_name> <chart>:安装一个 Helm Chart,并给它一个发布名称。 helm install --namespace <namespace> <release_name> <chart>:指定命名空间安装 Helm Chart。 helm list:列出所有的 Helm 发布。 helm status <release_name>:获取指定 Helm 发布的状态信息。 helm upgrade <release_name> <new_chart>:升级一个现有的 Helm 发布到新版本的 Chart。 helm rollback <release_name> <revision>:将 Helm 发布回滚到指定的版本。 helm uninstall <release_name>:卸载一个 Helm 发布。 helm history <release_name>:查看一个 Helm 发布的更新历史。 helm package <chart_dir>:将 Helm Chart 打包成 tgz 文件。 helm show chart <chart>:查看一个 Helm Chart 的详细信息。 helm get values <release_name>:查看指定 Helm 发布的配置值。 helm get all <release_name>:查看指定 Helm 发布的所有配置和值。 helm dependency list <chart>:列出一个 Helm Chart 的依赖。 helm dependency update <chart_dir>:更新一个 Helm Chart 目录中的依赖。 helm lint <chart_dir>:对一个 Helm Chart 进行 lint 检查。 MongoDB Helm Chart 中的每个角色配置 前文提到过, MongoDB Sharded 集群主要包括以下几个角色: ...

十一月 28, 2024 · 4 分钟 · 661 字 · zhu733756

MongoDB分片集群在k8s上的部署实战(一)

前戏 小白:你好,老花!我对在 Kubernetes 上使用 Helm 部署 MongoDB Sharded 集群很感兴趣,但我对 Kind 和 Helm 不太熟悉,你能详细教我一下吗? 老花:当然可以,小白!我们先从 Kind 和 Helm 的安装开始,然后详细介绍 Helm 中的每个角色配置,最后解释 Helm 应用是如何运行起来的。 Kind 快速构建集群 Kind 是一个使用 Docker 容器作为节点来运行本地 Kubernetes 集群的工具。可以通过以下步骤安装 Kind: 安装 Docker Kind 需要 Docker 来运行 Kubernetes 集群,所以首先确保你已经安装了 Docker。 安装 Kind 可以使用以下两种方式中一种下载: > sudo apt-get install kind > go install sigs.k8s.io/kind@v0.25.0 Tip: 如果镜像无法拉取, 可以配置一些国内源: cat /etc/docker/daemon.json { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.mirrors.ustc.edu.cn", "https://docker.nju.edu.cn" ] } 或者使用代理: { "proxies": { "default": { "httpProxy": "http://xxxx:xx", "httpsProxy": "https://xxxx:xx", "noProxy": "docker.m.daocloud.io,127.0.0.0/8" } } } 创建Kind集群: 配置docker和环境 下面的命令修改了docker运行时, 并配置了ulimits上限: ...

十一月 27, 2024 · 3 分钟 · 563 字 · zhu733756

MongoDB分片集群Sharded Cluster容器化部署实战

前戏 上回说了 MongoDB 的高可用架构有两种, 副本集合分片集群, 这一回我们来探索,分片集群怎么部署吧! 小白: 你好,老花!我想学习如何部署 MongoDB 的分片集群,你能帮帮我吗? 老花: 当然可以!MongoDB 的分片集群(Sharded Cluster)是一种分布式数据库架构,可以帮你处理大量数据和高吞吐量请求。接下来,我会一步步带你完成部署。 docker-compose 部署实战 第一步:准备工作 在我们开始之前,你需要确保已经克隆了这个 GitHub 仓库,并且切换到了with-keyfile-auth这个文件夹。这个文件夹包含了我们需要的带认证的 docker-compose 配置文件。 git clone https://github.com/minhhungit/mongodb-cluster-docker-compose.git cd mongodb-cluster-docker-compose/with-keyfile-auth 第二步:创建密钥文件 为了设置认证,我们需要一个密钥文件。我已经帮你创建了一个,但如果你自己想要创建一个,可以按照以下步骤操作: 在 Linux 上,你可以使用以下命令: openssl rand -base64 756 > mongodb-keyfile chmod 400 mongodb-keyfile 创建好mongodb-keyfile后,记得替换文件夹with-keyfile-auth/mongodb-build/auth/中的文件,然后进行下一步。 第三步:启动所有容器 更新或者安装 docker-compose: pip install docker-compose -U 现在,我们来启动所有的 Docker 容器。切换到with-keyfile-auth目录下,然后运行: docker-compose up -d Tip: 如果镜像无法拉取, 可以配置一些国内源: cat /etc/docker/daemon.json { "registry-mirrors": [ "https://docker.m.daocloud.io", "https://dockerproxy.com", "https://docker.mirrors.ustc.edu.cn", "https://docker.nju.edu.cn" ] } docker pull docker.m.daocloud.io/mongo:6.0.2 docker tag docker.m.daocloud.io/mongo:6.0.2 mongo:6.0.2 docker-compose up -d 预期的输出如下, 可以看到目前我们已经启动三个分片副本集合, ...

十一月 25, 2024 · 3 分钟 · 430 字 · zhu733756