当前位置: 首页 > news >正文

【mongodb】mongodb副本集的搭建和使用

 本站以分享各种运维经验和运维所需要的技能为主

《python零基础入门》:python零基础入门学习

《python运维脚本》: python运维脚本实践

《shell》:shell学习

《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战

《k8》暂未更新

《docker学习》暂未更新

《ceph学习》ceph日常问题解决分享

《日志收集》ELK+各种中间件

《运维日常》运维日常

《linux》运维面试100问

《DBA》db的介绍使用(mysql、redis、mongodb...)

一、副本集的搭建

0.介绍副本集

#官网的参考地址
https://docs.mongodb.com/manual/replication/
​
# Mongodb副本集介绍:
Mongodb复制集由一组Mongod实例(进程)组成,包含一个Primary节点和多个Secondary节点。
Mongodb Driver(客户端)的所有数据都写入Primary,Secondary从Primary同步写入的数据,以保持复制集内所有成员存储相同的数据集,实现数据的高可用。
​
# 副本集作用:
数据冗余,用做故障恢复使用,当发生硬件故障或者其它原因造成的宕机时,可以使用副本进行恢复。
读写分离,读的请求分流到副本上,减轻主节点的读压力。
​
# 副本集角色介绍:
主节点(Primary)
接收所有的写请求,然后把修改同步到所有Secondary。一个Replica Set只能有一个Primary节点,当Primary挂掉后,其他Secondary或者Arbiter节点会重新选举出来一个主节点。
默认读请求也是发到Primary节点处理的,可以通过修改客户端连接配置以支持读取Secondary节点。
​
副本节点(Secondary)
与主节点保持同样的数据集。当主节点挂掉的时候,参与选主。
​
仲裁者(Arbiter)
不保有数据,不参与选主,只进行选主投票。使用Arbiter可以减轻数据存储的硬件需求,Arbiter几乎没什么大的硬件资源需求,但重要的一点是,在生产环境下它和其他数据节点不要部署在同一台机器上。
​
# 注意:
在PSS典型架构中(Replica Set)节点数必须为奇数,目的是选主投票的时候要出现大多数才能进行选主决策。
在PSA架构中,需要偶数个数据节点,加一个Arbiter构成的Replica Set
  • PSS 典型副本集架构(读写操作都由主处理,从只负责同步)

img

  • PSA(Primary + Secondary + Arbiter模式,使用Arbiter搭建Replica Set)

img

1.创建多实例目录

[root@redis03 ~]# mkdir /server/mongodb/2801{7,8,9}/{conf,logs,pid,data} -p

2.编辑多实例配置文件

[root@redis03 ~]# vim /server/mongodb/28017/conf/mongo.conf
systemLog:destination: filelogAppend: truepath: /server/mongodb/28017/logs/mongodb.log#path: /server/mongodb/28018/logs/mongodb.log#path: /server/mongodb/28019/logs/mongodb.log
​
storage:journal:enabled: truedbPath: /server/mongodb/28017/data#dbPath: /server/mongodb/28018/data#dbPath: /server/mongodb/28019/datadirectoryPerDB: truewiredTiger:engineConfig:cacheSizeGB: 1directoryForIndexes: truecollectionConfig:blockCompressor: zlibindexConfig:prefixCompression: true
​
processManagement:fork: truepidFilePath: /server/mongodb/28017/pid/mongod.pid#pidFilePath: /server/mongodb/28018/pid/mongod.pid#pidFilePath: /server/mongodb/28019/pid/mongod.pid
​
net:port: 28017#port: 28018#port: 28019bindIp: 127.0.0.1,10.0.0.93replication:#类似于binlog,指定大小oplogSizeMB: 1024#副本记得名称,集群名称replSetName: dba

3.启动多实例

[root@redis03 ~]# chown -R mongo.mongo /server/mongodb/
[root@redis03 ~]# su - mongo
​
[mongo@redis03 ~]$ mongod -f /server/mongodb/28017/conf/mongo.conf
[mongo@redis03 ~]$ mongod -f /server/mongodb/28018/conf/mongo.conf
[mongo@redis03 ~]$ mongod -f /server/mongodb/28019/conf/mongo.conf
​
#验证
[mongo@redis03 ~]$ netstat -lntp       
tcp        0      0 10.0.0.93:28017         0.0.0.0:*               LISTEN      32893/mongod        
tcp        0      0 127.0.0.1:28017         0.0.0.0:*               LISTEN      32893/mongod        
tcp        0      0 10.0.0.93:28018         0.0.0.0:*               LISTEN      32938/mongod        
tcp        0      0 127.0.0.1:28018         0.0.0.0:*               LISTEN      32938/mongod        
tcp        0      0 10.0.0.93:28019         0.0.0.0:*               LISTEN      32981/mongod        
tcp        0      0 127.0.0.1:28019         0.0.0.0:*               LISTEN      32981/mongod

4.登录多实例

[mongo@redis03 ~]$ mongo 10.0.0.93:28017
[mongo@redis03 ~]$ mongo 10.0.0.93:28018
[mongo@redis03 ~]$ mongo 10.0.0.93:28019

5.初始化副本集

#配置副本集
config = {_id : "dba", members : [{_id:0, host:"10.0.0.93:28017"},{_id:1, host:"10.0.0.93:28018"},{_id:2, host:"10.0.0.93:28019"},]
}
​
复制集通过 replSetInitiate 命令或 rs.initiate() 命令进行初始化。
初始化后各个成员间开始发送心跳消息,并发起 Primary 选举操作,获得大多数成员投票支持的节点,会成为 Primary,其余节点成为 Secondary。
#读取副本集
rs.initiate(config) 

6.查看副本集状态

dba:PRIMARY> rs.status()#健康状态 1表示正常 0表示故障"health" : 1,#表示状态 1是主库 2是从库 3表示恢复数据中 7表示投票者 8表示down机"state" : 1,#标注是主库还是从库"stateStr" : "PRIMARY",#集群启动时间"uptime" : 579,#另一种格式的时间"optime" : {"ts" : Timestamp(1590593779, 1),"t" : NumberLong(1)},#上一次心跳传过来数据的时间"optimeDate" : ISODate("2020-05-27T15:36:19Z"),#检测上一次心跳时间"lastHeartbeat" : ISODate("2020-05-27T15:36:25.815Z"),#查看集群与主节点
dba:PRIMARY> rs.isMaster()
​
#oplog信息
dba:PRIMARY> rs.printReplicationInfo()
configured oplog size:   1024MB
log length start to end: 1543secs (0.43hrs)
oplog first event time:  Wed May 27 2020 23:26:46 GMT+0800 (CST)
oplog last event time:   Wed May 27 2020 23:52:29 GMT+0800 (CST)
now:                     Wed May 27 2020 23:52:38 GMT+0800 (CST)
​
#查看延时从库信息
dba:PRIMARY> rs.printSlaveReplicationInfo()
source: 10.0.0.93:28018syncedTo: Wed May 27 2020 23:54:19 GMT+0800 (CST)0 secs (0 hrs) behind the primary 
source: 10.0.0.93:28019syncedTo: Wed May 27 2020 23:54:19 GMT+0800 (CST)0 secs (0 hrs) behind the primary #打印副本集配置文件
dba:PRIMARY> rs.config()

7.主库创建数据,从库查看数据

#主库插入数据
db.table.insertMany([{"name":"gcc","age":10},{"name":"zzy","age":9},{"name":"hxh","age":11}])
#主库查看数据
dba:PRIMARY> show tables
table
dba:PRIMARY> db.table.find()
​
#从库查看数据
dba:SECONDARY> show databases
2020-05-27T23:43:40.020+0800 E QUERY    [thread1] Error: listDatabases failed:{"operationTime" : Timestamp(1590594219, 1),"ok" : 0,"errmsg" : "not master and slaveOk=false","code" : 13435,"codeName" : "NotMasterNoSlaveOk","$clusterTime" : {"clusterTime" : Timestamp(1590594219, 1),"signature" : {"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),"keyId" : NumberLong(0)}}
} 
#连查看库都会被拒绝,因为从库不提供读写
​
#执行命令(从库都要执行)
dba:SECONDARY> rs.slaveOk()
dba:SECONDARY> show databases
admin    0.000GB
cluster  0.000GB
config   0.000GB
local    0.000GB
​
#每次重新连接都要执行以上命令才能读取
#可以配置永久生效
[root@redis03 ~]# vim ~/.mongorc.js
rs.slaveOk()

二、副本集实现高可用

1.故障切换测试

#主库使用 localhost 连接,执行关闭数据库的操作(使用ip连接是不能执行的)
[root@db01 ~]# mongod -f /server/mongodb/28018/conf/mongo.conf --shutdown#查看其他从库中会有一台从库,变成主库#故障转移实现了,但是我的程序连接mongodb的配置还需要修改怎么办??

2.程序怎么实现连接切换的

1.如果使用的是单节点,那么程序里面直接配置写死mongodb的ip和端口即可2.如果是副本集集群的形式,在程序里面写的就是一个列表,列表里面写mongo_reip=[10.0.0.91:28017,10.0.0.92:28018,10.0.0.93:29019]程序会去使用命令询问谁是主节点,得到结果后在写入数据

3.恢复主库

#重新启动主库,他会自动判断谁是主库,自动成为新的从库#注意:三台节点,只能坏一台,坏两台就有问题了

4.指定节点提升优先级

#原来的主库配置高,性能好,想恢复之后还让他是主库怎么办#查看优先级
dba:PRIMARY> rs.conf()#权重值"priority" : 1,#临时修改配置文件
dba:PRIMARY> config=rs.conf()
#修改配置文件中 id 为0 的priority值为10
dba:PRIMARY> config.members[0].priority=10
#配置文件生效
dba:PRIMARY> rs.reconfig(config)#新版本调整完直接切换主库,旧版本需要主动降级
dba:PRIMARY> rs.stepDown()#恢复权重
dba:PRIMARY> config=rs.conf()
dba:PRIMARY> config.members[0].priority=1
dba:PRIMARY> rs.reconfig(config)

三、扩容与删减节点

1.配置一台新的节点

#创建目录
[root@redis03 ~]# mkdir /server/mongodb/28016/{conf,logs,pid,data} -p#配置新节点
[root@redis03 ~]# cp /server/mongodb/28017/conf/mongo.conf /server/mongodb/28016/conf/
[root@redis03 ~]# sed -i 's#28017#28016#g' /server/mongodb/28016/conf/mongo.conf #启动新节点
[root@redis03 ~]# chown -R mongo.mongo /server/mongodb/
[root@redis03 ~]# su - mongo
[mongo@redis03 ~]$ mongod -f /server/mongodb/28016/conf/mongo.conf

2.将新节点加入集群

#主库操作
dba:PRIMARY> rs.add("10.0.0.93:28016")
{"ok" : 1,"operationTime" : Timestamp(1590597530, 1),"$clusterTime" : {"clusterTime" : Timestamp(1590597530, 1),"signature" : {"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),"keyId" : NumberLong(0)}}
}#查看集群状态
dba:PRIMARY> rs.status()#注意:四个节点也不能坏两台机器

3.删除节点

#主库操作
dba:PRIMARY> rs.remove("10.0.0.93:28016")
{"ok" : 1,"operationTime" : Timestamp(1590597842, 1),"$clusterTime" : {"clusterTime" : Timestamp(1590597842, 1),"signature" : {"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),"keyId" : NumberLong(0)}}
}#查看集群状态
dba:PRIMARY> rs.status()

4.添加仲裁节点

# 仲裁节点说明:
不存储数据,只进行选主。#创建目录
[root@redis03 ~]# mkdir /server/mongodb/28015/{conf,logs,pid,data} -p#配置新节点
[root@redis03 ~]# cp /server/mongodb/28017/conf/mongo.conf /server/mongodb/28015/conf/
[root@redis03 ~]# sed -i 's#28017#28015#g' /server/mongodb/28015/conf/mongo.conf #启动新节点
[root@redis03 ~]# chown -R mongo.mongo /server/mongodb/
[root@redis03 ~]# su - mongo
[mongo@redis03 ~]$ mongod -f /server/mongodb/28015/conf/mongo.conf #主库操作加入仲裁节点
dba:PRIMARY> rs.addArb(("10.0.0.93:28015")#查看该库是否有数据#注意,五个节点时,可以坏两个节点

相关文章:

【mongodb】mongodb副本集的搭建和使用

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》:python零基础入门学习 《python运维脚本》: python运维脚本实践 《shell》:shell学习 《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战 《k8…...

Java后端面试复习7.24

lock加锁解锁尝试获取锁方法lock底层基于什么实现lock和lock的底层实现分别面向什么用户lock和synchronized异同如何选择合适的锁ReentrantLock如何实现冲入内部类三个公平和非公平获取锁怎么实现的RL默认公平还是非公平,构造参数ReentrantRedaWriteLock的特性什么是…...

前端 HTML 概述

目录 1. HTML概述 1.1 超文本标记语言 1.2 标签 2. HTML 解析与编辑 2.1 解析与访问 2.2 编辑 html文件 1. HTML概述 HTML( Hyper Text Markup Language:超文本标记语言 ):主要用于网页主体结构的搭建,在网页上…...

探索Thymeleaf:用动态Web模板引擎打造吸引人的用户界面(SpringBoot的html详解)

什么是Thymeleaf? Thymeleaf是一个用于Web和独立环境的现代服务器端Java模板引擎,用于处理XML/XHTML/HTML5内容。它特别适合基于Spring框架的Web应用程序,因为它提供了与Spring MVC的出色集成。Thymeleaf以其自然的模板语法和强大的数据绑定…...

视频教程 - 自研Vue3 Tree组件高级功能:虚拟滚动新增节点实现自动滚动

感谢小伙伴们对本套自研vue3 tree组件教程的关注,在前一篇媲美Element Plus JuanTree终极实战:虚拟滚动的功能演示中发现了小bug,特地整理了相关录屏来说明怎么一步步解决bug的,来回馈小伙伴们的支持。 Tree组件高级功能&#xff…...

职业生涯阶段总结3:转眼毕业三年

不知不觉,科班毕业三年多了,也换了三个单位了; 个人软件开发的理论和技术能力确实比以前刚出来的时候,强了不少; 在行情越发下滑的形势,似乎只有进大厂才能拿到不错的收入,但是大厂的压力也是比…...

项目经理面试总结

先上结论:每个公司问的问题侧重点都不太一样,五花八门,评判标准也不一样,目前我能感觉到的就是自己需要很了解项目,也就是工作过程中经常做出总结,需要你经常去思考,包括对内和对外的思考。 自我…...

(免费领源码)java#springboot#mysql大学校园旧物捐赠网站 25109-计算机毕业设计项目选题推荐

摘 要 在网络信息的时代,众多的软件被开发出来,给用户带来了很大的选择余地,而且人们越来越追求更个性的需求。在这种时代背景下,企业只能以用户为导向,按品种分类规划,以产品的持续创新作为企业最重要的竞…...

Java 设计模式之单例模式

Java 设计模式之单例模式 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一种访问该实例的全局方法。这种模式有助于确保系统中的某些组件只有一个实例,并提供了一种方便的方法来访问该实例。 更多设计模式请参考&#x…...

Linux系统驱动(二)字符设备驱动

文章目录 一、概念(一)相关概念(二)字符设备框架结构(三)用户空间和内核空间数据传输1. 函数的参数对应关系 (四)字符设备相关的API1. 字符设备驱动(1)注册字…...

Day29 | 动态规划 509. 斐波那契数 70. 爬楼梯 746. 使用最小花费爬楼梯

语言 Java 509. 斐波那契数 斐波那契数 题目 斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是: F(0) 0,F(1) 1 F(n) F(n -…...

【开源移植】MultiButton_小型按键驱动模块移植

MultiButton 简介 MultiButton 是一个小巧简单易用的事件驱动型按键驱动模块,可无限量扩展按键,按键事件的回调异步处理方式可以简化你的程序结构,去除冗余的按键处理硬编码,让你的按键业务逻辑更清晰。 使用方法 1.先申请一个…...

【Python系列】Python 字典合并

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

C# 设计模式之装饰器模式

总目录 前言 装饰器模式的主要作用就是扩展一个类的功能,或给一个类添加多个变化的情况。学习面向对象的都知道,如果想单纯的给某个类增加一些功能,可以直接继承该类生成一个子类就可以。应对一些简单的业务场景继承也就够了,但是…...

【uniapp离线打包】(基于Android studio)

文章目录 uniapp打包官方教程入口一、准备工作(工具三大件)Android Studio版本推荐 二、准备工作(Android壳和uniapp包)导入Android壳生成uniapp包将uniapp包导入android壳降低jdk版本 三、准备工作(证书)准备Android平台离线签名…...

稳稳的年化10%,多任务时序动量策略——基于pytorch的深度学习策略(附python代码)

原创文章第608篇,专注“AI量化投资、世界运行的规律、个人成长与财富自由"。 做因子挖掘这段时间,有一个观感。 传统的因子挖掘,尤其是手工构造因子,到遗传算法因子挖掘。——本身也是一种”拟合“,或者说试图”…...

C++分析AVL树

目录 AVL树介绍 AVL树平衡因子更新分析 AVL树插入时旋转与平衡因子更新 左单旋 右单旋 左右单旋 右左单旋 AVL旋转可行性 AVL树节点删除(待补充) AVL树分析 AVL树介绍 二叉搜索树在某些极端情况下可能会退化,为了解决这个问题&…...

aurora8b10b ip的使用(framing接口下的数据回环测试)

文章目录 一、Aurora8B/10B协议二、时钟、复位与状态指示1、时钟2、复位3、状态指示 三、数据发送、接受接口(1)AXI4-Stream位排序(2)Streaming接口(3)Framing接口(帧传输接口) 四、…...

如何通过OpenCV判断图片是否包含在视频内?

要判断图片是否包含在视频内,可以使用计算机视觉技术和图像处理方法。这通常涉及特征匹配或模板匹配。以下是一个基于OpenCV的解决方案,通过特征匹配的方法来实现这一目标。 步骤概述 读取视频和图片: 使用OpenCV读取视频文件和图片文件。 …...

大数据基础:Spark重要知识汇总

文章目录 Spark重要知识汇总 一、Spark 是什么 二、Spark 四大特点 三、Spark框架模块介绍 3.1、Spark Core的RDD详解 3.1.1、什么是RDD 3.1.2、RDD是怎么理解的 四、Spark 运行模式 4.1、Spark本地模式介绍 4.2、Spark集群模式 Standalone 4.3、Spark集群模式 Stan…...

Executable Code Actions Elicit Better LLM Agents

Executable Code Actions Elicit Better LLM Agents Github: https://github.com/xingyaoww/code-act 一、动机 大语言模型展现出很强的推理能力。但是现如今大模型作为Agent的时候,在执行Action时依然还是通过text-based(文本模态)后者JSO…...

循环结构(三)——do-while语句

目录 🍁引言 🍁一、语句格式 🚀格式1 🚀格式2 🍁二、语句执行过程 🍁三、实例 🚀【例1】 🚀【例2】 🚀【例3】 🍁总结 🍁备注 &am…...

pandas 或筛选

pandas 或筛选 在Pandas中,可以使用DataFrame.loc方法结合逻辑运算符来实现或筛选。这里提供一个简单的例子: import pandas as pd 创建示例DataFrame df pd.DataFrame({ ‘A’: [1, 2, 3, 4], ‘B’: [5, 6, 7, 8], ‘C’: [9, 10, 11, 12] }) 设定…...

工具(1)—截屏和贴图工具snipaste

演示和写代码文档的时候,总是需要用到截图。在之前的流程里面,一般是打开WX或者QQ,找到截图工具。但是尴尬的是,有时候,微信没登录,而你这个时候就在写文档。为了截个图,还需要启动微信&#xf…...

【从零开始一步步学习VSOA开发】快速体验SylixOS

快速体验SylixOS 安装完毕RealEvo-IDE 后,同时也安装了RealEvo-Simulator。RealEvo-Simulator 是一个虚拟运行环境,可以模拟各种体系结构并在其上运行 SylixOS。相比于物理板卡,在 RealEvo-Simulator 进行运行调测更加的方便快捷且成本低廉。…...

Ansible自动化:简化IT基础设施管理的艺术

目录 一.前言 二.Ansible简介 2.1什么是Ansible? 2.2Ansible的主要特点 2.3Ansible的应用场景 三.探索Ansible的高级功能 3.1 高级Playbook特性 3.2 Ansible Vault 3.3 动态Inventory 3.4Ansible Tower(AWX) 3.5模块开发 3.6 Ans…...

【Rust光年纪】探索Rust语言中的WebSocket库和框架:优劣一览

Rust语言中的实时通信利器:WebSocket库与框架全面解析 前言 随着Rust语言的不断发展,其在Web开发领域也变得越来越受欢迎。WebSocket作为实现实时通信的重要技术,在Rust的生态系统中也有多个库和框架提供了支持。本文将介绍几个主流的Rust …...

HTML 基础结构

目录 1. 文档声明 2. 根标签 3. 头部元素 4. 主题元素 5. 注释 6. 演示 1. 文档声明 <!DOCTYPE html>&#xff1a;声明文档类型&#xff0c;表示该文档是 html 文档&#xff0c; 2. 根标签 &#xff08;1&#xff09;所有的其他标签都要放在一对根标签中&#…...

多页合同怎么盖骑缝章_电子合同怎么盖骑缝章?

多页合同怎么盖骑缝章&#xff1f;电子合同怎么盖骑缝章&#xff1f; 对于纸质多页合同&#xff0c;盖骑缝章是一种常见的做法&#xff0c;用于确保合同的完整性&#xff0c;防止任何页面被替换或篡改。以下是盖骑缝章的基本步骤&#xff1a; 将所有合同页面平铺在桌面上。用…...

GD 32 IIC通信协议

前言&#xff1a; ... 通信方式 通信方式分为串行通信和并行通信。常见的串口就是串行通信的方式 常用的串行通信接口 常用的串行通信方式有USART,IIC,USB,CAN总线 同步与异步 同步通信&#xff1a;IIC是同步通信&#xff0c;有两个线一个是时钟信号线&#xff0c;一个数数据…...

discuz上传wordpress/建网站软件

Java多线程 多线程 ​ 现实生活中&#xff0c;边吃饭边玩手机&#xff0c;大脑同时处理吃饭与玩手机两件事。 进程 ​ 程序是指令和数据的有序集合&#xff0c;静态概念。而进程就是一个程序的一次执行过程&#xff0c;是一个动态的概念。 线程 ​ 一个进程中包含若干个线…...

江阴网站设计/网站流量排名查询工具

文章目录继承的特点重写Super关键字调用父类构造器使用继承&#xff0c;人们可以基于已存在的类构造一个新类。继承已存在的类就是复用&#xff08;继承&#xff09;这些类的方法和域。在此基础上&#xff0c;还可以添加一些新的方法和域&#xff0c; 以满足新的需求。这是 Jav…...

苏州本地网站/站长之家查询工具

注释掉pom文件里面的 &#xff0c;因为provided表明该包只在编译和测试的时候用的。...

做毕业设计网站需要的工具/推荐友情链接

之前在知乎上关注过一个问题&#xff0c;题目是&#xff1a;《软件测试真的干到35就干不动了吗&#xff1f;》 楼主的问题详细描述是&#xff1a;作为一个已经34岁但仍奋战在测试一线的老测试员&#xff0c;被人无数次问到这个问题&#xff0c;也回答过无数次&#xff0c;刚看…...

类似织梦cms/百度搜索引擎优化案例

实验题目&#xff1a;平面图对偶图的求解 实验目的&#xff1a; 1、掌握平面图的定义&#xff1b; 2、掌握平面图对偶图的求解方法&#xff1b; 3、掌握平面图与其对偶图之间顶点数、边数和面数的关系。 实验要求&#xff1a; 1、给定一平面图的面矩阵R和连通分支数p 2、输…...

东莞网站排名优化/seo技术培训岳阳

VC界面库分两大类&#xff1a;一&#xff0c;Direct UI&#xff0c;优点&#xff1a;界面绚丽。二&#xff0c;MFC扩展库。优点&#xff1a;1&#xff0c;方便用户。2&#xff0c;减少开发成本。3&#xff0c;界面相对好看。BCG属于后者&#xff0c;非常是否适合优化办公软件界…...