MongoDB部署的10个最佳实践

news2024/11/25 20:43:21

MongoDB是一个非关系文档数据库,支持类似JSON的存储。其灵活的数据模型使您可以轻松存储非结构化数据。它于2009年首次发布,是最常用的NoSQL数据库。它的下载量已超过325亿次。

 MongoDB在开发人员中很受欢迎,因为它很容易上手。多年来,MongoDB引入了许多功能,这些功能将数据库变成了一个强大的解决方案,能够为应用程序存储TB级的数据。

与任何数据库一样,使用MongoDB的开发人员和DBA应该考虑如何优化其数据库的性能,尤其是在云服务中,处理,传输和存储的每个字节都需要花钱。能够如此快速地开始使用MongoDB意味着很容易忽略潜在的问题或错过简单的性能改进。

在本文中,我们将介绍10种基本技术,您可以应用这些技术为您的应用程序充分利用MongoDB。 

MongoDB最佳实践#1:从一开始就在数据库上启用授权和身份验证

数据库越大,泄漏造成的损害就越大。由于首次部署MongoDB时默认禁用授权和身份验证的简单事实,因此发生了大量数据泄漏。虽然它不是性能提示,但从一开始就启用授权和身份验证至关重要,因为它将随着时间的推移为您节省由于未经授权的访问或数据泄漏而导致的任何潜在痛苦。

部署 MongoDB 的新实例时,默认情况下该实例没有用户、密码或访问控制。在最近的MongoDB版本中,默认IP绑定更改为127.0.0.1,并添加了localhost异常,这减少了安装数据库时数据库暴露的可能性。

但是,从安全角度来看,这仍然不理想。

第一条建议是创建管理员用户,然后在启用授权选项的情况下重新启动实例。这可以防止对实例进行任何未经授权的访问。

创建管理员:

> use adminswitched to db admin> db.createUser({...   user: "zelmar",...   pwd: "password",...   roles : [ "root" ]... })Successfully added user: { "user" : "zelmar", "roles" : [ "root" ] }

然后,您需要启用授权并重启实例。如果要从命令行部署MongoDB:

mongod --port 27017 --dbpath /data/db --auth

或者,如果您使用配置文件部署MongoDB,则需要包括:

security:    authorization: "enabled"

▌MongoDB最佳实践#2:不要在生产实例中使用“不推荐版本”或“生命周期结束版本”并保持更新

这看起来应该很明显,但我们在生产实例中看到的最常见的问题之一是由于开发人员运行的MongoDB版本实际上首先不适合生产。这可能是由于版本已过时,例如应更新为包含所有必要错误修复的较新版本的已停用版本。

或者可能是由于版本太早,尚未经过足够的测试以供生产使用。作为开发人员,我们通常热衷于使用我们工具的最新和最好的版本。我们还希望在开发的所有阶段保持一致,从初始构建和测试到生产,因为这减少了我们必须支持的变量数量、问题的可能性以及管理所有实例的成本。

对于某些人来说,这可能意味着使用尚未签署生产部署的版本。对于其他人来说,这可能意味着坚持使用经过尝试和信任的特定版本。从故障排除的角度来看,当问题在已批准用于生产但尚未部署的更高版本的 MongoDB 中修复时,这是一个问题。或者,您可能会忘记在后台“正常工作”的数据库实例,并在需要实现补丁时错过。

为此,您应该使用每个版本的发行说明定期检查您的版本是否适合生产。例如,MongoDB 5.0在其发行说明中提供了以下指导: 

https://www.mongodb.com/docs/upcoming/release-notes/5.0/

此处的指导是使用 MongoDB 5.0.11,因为此版本具有所需的更新。如果不更新到此版本,将面临丢失数据的风险。

虽然坚持使用一个版本可能很诱人,但跟上升级对于防止生产中出现问题至关重要。您可能希望利用新添加的功能,但应首先通过测试过程对这些功能进行测试。在将它们投入生产之前,您希望查看它们是否会带来任何可能影响整体性能的问题。

最后,您应该检查MongoDB软件生命周期计划,并在每个版本的生命周期结束之前预测集群的升级:

https://www.mongodb.com/support-policy/lifecycles

生命周期结束版本不会收到补丁、错误修复或任何类型的改进。这可能会使您的数据库实例暴露并容易受到攻击。

从性能的角度来看,为您的生产应用程序获得正确版本的MongoDB需要“恰到好处” :不要超前,以至于您会遇到错误或其他问题,但也不要落后到会错过重要的更新。

MongoDB最佳实践#3:使用MongoDB复制来确保HA并经常检查副本的状态

副本集是一组 MongoDB 进程,它在用于应用程序的所有节点上维护相同的数据。它为您的数据提供冗余和数据可用性。当您在不同的数据库服务器上(甚至更好的是,在世界各地的不同数据中心)上拥有多个数据副本时,复制可在发生灾难时提供高级别的容错能力。

MongoDB副本集使用一个写入器节点(也称为主服务器)。最佳做法建议是始终具有奇数个成员。传统上,副本集至少具有三个实例:

· 主(写入器节点)

· 辅助(读取器节点)

· 辅助(读取器节点)

副本集的所有节点将协同工作,因为主节点将从应用服务器接收写入,然后将数据复制到辅助节点。如果主节点出现问题,副本集将选择辅助节点作为新的主节点。为了使此过程更高效地工作并确保顺利故障转移,副本集的所有节点必须具有相同的硬件配置。副本集的另一个优点是可以将读取操作发送到辅助服务器,从而提高数据库的读取可伸缩性。

将副本集部署到生产环境后,检查副本和节点的运行状况非常重要。MongoDB有两个重要的命令用于此目的:

  1. rs.status()使用从副本集的其他成员发送的检测信号数据包派生的数据,提供有关副本集当前状态的信息。这是一个非常有用的工具,用于检查副本集中所有节点的状态。

  2. rs.printSecondaryReplicationInfo()提供副本集状态的格式化报告。在数据复制时检查是否有任何辅助数据库位于主数据库后面非常有用,因为这会影响您在出现问题时恢复所有数据的能力。如果辅助数据库远远落后于主数据库,那么最终丢失的数据可能会比您满意的要多得多。

但是,请注意,这些命令提供的是时间点信息,而不是持续监视副本集的运行状况。在实际的生产环境中,或者如果要检查许多集群,运行这些命令可能会非常耗时且令人厌烦。因此,我们建议使用像Percona PMM这样的监控系统来监控您的集群。

MongoDB最佳实践#4:仅在必要时使用$regex查询,并在可能的情况下选择文本搜索

有时在数据库中搜索内容的最简单方法是使用正则表达式或$regex操作。许多开发人员选择这个选项,但实际上使用正则表达式会大规模地损害您的搜索操作。应避免使用$regex查询,尤其是当你的数据库很大的时候。

$regex查询会消耗大量的CPU时间,通常会非常缓慢和低效。创建索引并没有太大帮助,有时有索引的性能还不如没有索引的性能。

例如,让我们对1000万个文档的集合运行一个$regex查询,并使用.explain(true)来查看该查询需要多少毫秒。

没有索引:​​​​​​​

> db.people.find({"name":{$regex: "Zelmar"}}).explain(true)- -   Output omitted  - -"executionStats" : {"nReturned" : 19851,"executionTimeMillis" : 4171,"totalKeysExamined" : 0,"totalDocsExamined" : 10000000,- -   Output omitted  - -

如果我们在“名称”上创建了一个索引:​​​​​​​

db.people.find({"name":{$regex: "Zelmar"}}).explain(true)- -   Output omitted  - -"executionStats" : {"nReturned" : 19851,"executionTimeMillis" : 4283,"totalKeysExamined" : 10000000,"totalDocsExamined" : 19851,- -   Output omitted  - -

在这个例子中,我们可以看到索引并没有帮助提高$regex的性能。

新应用程序对搜索请求使用$regex操作是很常见的。这是因为当集合的大小很小且应用程序的用户很少时,开发人员和DBA在一开始都不会注意到任何性能问题。

然而,当集合变大,应用程序聚集更多用户时,$regex操作开始降低集群速度,成为团队的噩梦。随着时间的推移,随着应用程序的扩展,越来越多的用户想要执行搜索请求,性能水平可能会显著下降。

与其使用$regex查询,不如使用文本索引来支持文本搜索。文本搜索比$regex更有效,但需要提前向数据集添加文本索引。索引可以包括值为字符串或字符串元素数组的任何字段。一个集合只能有一个文本搜索索引,但该索引可以覆盖多个字段。

使用与上面示例相同的集合,我们可以使用文本搜索测试相同查询的执行时间:​​​​​​​

> db.people.find({$text:{$search: "Zelmar"}}).explain(true)- -   Output omitted  - -"executionStages" : {"nReturned" : 19851,"executionTimeMillisEstimate" : 445,"works" : 19852,"advanced" : 19851,- -   Output omitted  - -

实际上,同样的查询,使用文本搜索比使用$regex少花了4秒的时间。“数据库时间”中的四秒钟,更不用说在线申请时间。

总之,如果可以使用文本搜索解决查询,那么就这样做。将$regex查询限制在真正需要的用例中。

MongoDB最佳实践#5:明智地考虑您的索引策略

在一开始就对查询进行一些思考,随着时间的推移,会对性能产生巨大影响。首先,您需要了解您的应用程序以及您希望作为服务的一部分处理的查询类型。在此基础上,您可以创建一个支持它们的索引。

索引可以帮助加快读查询,但它会带来额外的存储成本,并且会降低写操作的速度。因此,您需要考虑应该为哪些字段建立索引,以避免创建过多的索引。

例如,如果您正在创建一个复合索引,那么必须遵循ESR (equal, Sort, Range)规则,并且使用索引对结果进行排序可以提高查询的速度。

同样,您总是可以检查您的查询是否真的使用了用.explain()创建的索引。有时我们会看到创建了索引的集合,但是查询要么不使用索引,要么完全使用了错误的索引。请务必仅创建实际用于读取查询的索引。拥有永远不会使用的索引是对存储空间的浪费,并且会降低写操作的速度。

在查看.explain()输出时,有三个重要的字段需要观察。例如:​​​​​​​

keysExamined:0docsExamined:207254nreturned:0

在本例中,没有使用索引。我们知道这一点是因为检查的键的数量是0,而检查的文档的数量是207254。理想情况下,查询的比率应该是nreturned/ keysexamining =1。例如:​​​​​​​

keysExamined:5docsExamined: 0nreturned:5

最后,如果.explain()显示特定查询使用了错误的索引,则可以使用.hint()强制该查询使用特定索引。在查询中调用.hint()方法会覆盖MongoDB的默认索引选择和查询优化过程,允许您指定所使用的索引,或者执行正向收集或反向收集扫描。

MongoDB最佳实践#6:经常检查查询和索引

每个数据库都是独一无二的,并且特定于其应用程序,因此它随时间增长和变化的方式也是如此。没有人知道应用程序在数月和数年内将如何增长,或者查询将如何变化。无论您做出什么假设,您的预测都不可避免地是错误的,因此定期检查数据库和索引至关重要。

例如,您可能计划使用特定的查询优化方法和特定的索引,但一年后意识到很少有查询使用该索引,因此不再需要。继续使用此方法将花费更多的存储成本,同时不会提高应用程序性能。

因此,有必要执行查询优化并经常查看每个集合的索引。

MongoDB有一些工具来进行查询优化,如数据库分析器或.explain()方法。我们建议使用它们来发现哪些查询速度较慢,查询如何使用索引,以及您可能需要在哪些方面改进优化。除了删除没有有效使用的索引外,还要注意不需要运行的重复索引。

在Percona,我们使用脚本来检查是否存在重复的索引,或者是否有任何未使用的索引。您可以在我们在 GitHub 上的存储库中找到它们:

https://github.com/percona/support-snippets/tree/master/mongodb/scripts

同样,您可以考虑要从查询中获得多少结果,因为提供太多结果会影响性能。有时,您只需要查询的前五个结果,而不是数十个或数百个响应。在这些情况下,可以使用.limit()限制查询结果的数量。

另一种有用的方法是使用投影来仅获取必要的数据。如果只需要文档的一个字段,请使用投影而不是检索整个文档,然后在应用程序端进行筛选。

最后,如果需要对查询结果进行排序,请确保使用的是索引并利用它来提高效率。

MongoDB 最佳实践 #7:不要在同一台服务器上运行多个 mongod 或 mongos 实例

即使可以在同一台服务器上使用不同的进程和端口运行多个 mongod 或 mongos 实例,我们也强烈建议不要这样做。

当您在同一台服务器上运行多个 mongod 或 mongos 进程时,很难监控它们以及它们消耗的资源(CPU、RAM、网络等)。因此,当出现问题时,很难找出正在发生的事情并找到问题的根本原因。

我们看到很多情况下,客户在服务器上遇到了资源问题,但由于他们正在运行多个 mongod 或 mongos 实例,即使发现哪个特定进程有问题也很困难。这使得故障排除极具挑战性。

同样,在某些情况下,开发人员已经实现了分片集群来扩展其应用程序数据,我们看到多个分片在同一台服务器上运行。在这些情况下,路由器将向同一节点发送大量查询,使节点过载并导致性能不佳——这与分片策略想要实现的目标完全相反。

此处最坏的情况涉及副本集。想象一下,运行副本集以实现复原能力和可用性,然后发现副本集的两个或多个成员在同一台服务器上运行。这是灾难和数据丢失的秘诀。您不会为复原能力构建应用程序,而是使整个部署更有可能失败。

MongoDB最佳实践#8:经常备份

因此,您有一个具有复制功能的群集,但您想睡得更好吗?经常运行数据备份。如果需要从计划外事件中恢复,频繁备份可以使您从较早的时刻恢复数据。

备份MongoDB数据有许多不同的选项:

Mongodump / Mongorestore

Mongodump从MongoDB读取数据并创建一个BSON文件,Mongorestore可以使用它来填充MongoDB数据库。这些为备份小型MongoDB部署提供了有效的工具。从好的方面来说,您可以选择特定的数据库或集合进行有效备份,并且此方法不需要停止节点上的写入。但是,此方法不会备份已创建的任何索引,因此在还原时,需要再次重新创建这些索引。逻辑备份通常非常缓慢且耗时,因此您必须在还原过程中考虑该时间。最后,对于部署更复杂的分片集群,不建议使用此方法。

Percona Backup for MongoDB

Percona Backup for MongoDB是一种开源,分布式和低影响的解决方案,用于MongoDB分片集群和副本集的一致备份。它支持 MongoDB 服务器、副本集和分片集群的备份。它可以支持逻辑、物理和时间点恢复备份,以及备份到任何位置,包括 AWS S3、Azure 或文件系统存储类型。

但是,它确实需要在要保护的所有节点上进行初始设置和配置。

物理/文件系统备份

您可以通过复制MongoDB的底层数据文件来创建MongoDB部署的备份。您可以对这种类型的备份使用不同的方法,从手动复制数据文件到逻辑卷管理 (LVM) 快照,再到基于云的快照。这些通常比逻辑备份更快,并且可以将它们复制或共享到远程服务器。这种方法特别推荐用于大型数据集,在同一集群上构建新节点时很方便。

不利的一面是,还原时无法选择特定的数据库或集合,并且无法执行增量备份。此外,建议运行专用节点进行备份,因为它需要停止写入,这会影响应用程序性能。

MongoDB最佳实践#9:知道何时对副本集进行分片并仔细选择一个分片键

分片是您可以使用MongoDB部署的最复杂的架构。

随着数据库的增长,您将需要向服务器添加更多容量。这可能涉及添加更多 RAM、更多 I/O 容量,甚至更强大的 CPU 来处理。这称为垂直缩放。

但是,如果数据库增长太多,超过了单台计算机的容量,则可能需要拆分工作负载。有几个原因可能导致这种情况。例如,可能没有足够大的物理服务器来处理工作负载,或者服务器实例的成本太高,以至于运行起来负担不起。在这些情况下,您需要开始考虑水平缩放。

水平扩展涉及将数据库划分到多个服务器上,并根据需要添加其他服务器以增加容量。对于MongoDB,此过程称为分片,它依赖于Shard Key(分片键)来管理如何在机器之间分配工作负载。 

选择分片键可能是您在管理 MongoDB 时面临的最困难的任务。在选择键之前,有必要研究数据集和查询并提前计划,因为一旦执行分片,就很难恢复分片。对于MongoDB 4.2及更早版本,分配分片键是一个无法撤消的单向过程。对于 MongoDB 4.4 及更高版本,可以优化分片键,而 MongoDB 5.0 及更高版本允许您使用reshardCollection命令更改分片键。

如果您选择错误的分片键,那么很大一部分文档可能会转到其中一个分片,而只有少数文档会转到另一个分片。这将使分片集群不平衡,从而随着时间的推移影响性能。当选择单调增长的键来分片集合时,通常会发生不平衡的集群,因为超过给定值的所有文件都将转到一个分片,而不是均匀分布。

除了查看用于分片数据的值外,您还需要考虑将在分片中发生的查询。查询必须使用分片键,以便 mongos 进程在分片集群中分发查询。如果查询不使用分片键,则 mongos 会将查询发送到集群的每个分片,从而影响性能并使分片策略效率低下。

MongoDB最佳实践#10:不要在问题上砸钱

最后但并非最不重要的一点是,经常看到团队在数据库问题上投入资金。但是,与其立即伸手去拿信用卡,不如先尝试横向思考并想象更好的解决方案。

添加更多 RAM、添加更多 CPU 或移动到更大的实例或更大的计算机可以克服性能问题。但是,如果不首先分析根本问题或瓶颈,这样做可能会导致将来出现更多相同类型的问题。在大多数情况下,答案不是在资源上花费更多的钱,而是研究如何优化您的实现,以便在相同级别上获得更好的性能。

尽管云服务可以轻松扩展实例,但效率低下的成本可能会迅速增加。更糟糕的是,这是一项持续的费用,将随着时间的推移而持续下去。通过首先查看查询优化和性能等领域,可以避免额外的支出。对于与我们合作过的一些客户来说,降级其 EC2 实例的能力为他们的公司节省了大量月费。

作为一般建议,请采取节省成本的思维方式,在添加硬件或增强云实例之前,请花时间分析问题并考虑更好的长期解决方案。


本文由作者进行翻译

原文链接:

https://www.infoworld.com/article/3691136/10-best-practices-for-every-mongodb-database.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/417546.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ATTCK v12版本战术实战研究——提权(一)

一、概述 前几期文章中,我们中介绍ATT&CK 14项战术中提权战术(一),包括提权前6项子技术。那么从前文中介绍的相关提权技术来开展测试,进行更深一步的分析。本文主要内容是介绍攻击者在运用提权技术时,…

LeetCode 112 路径总和

题目: 给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。 示例 1…

Spring Boot概述(一)

1. SpringBoot 概述 1.1 SpringBoot 概念 SpringBoot提供了一种快速使用Spring的方式,基于约定优于配置的思想,可以让开发人员不必在配置与逻辑业务之间进行思维的切换,全身心的投入到逻辑业务的代码编写中,从而大大提高了开发的…

数字化时代,如何通过社媒找到外贸客户?

随着全球化的深入发展,外贸客户的开发变得越来越重要。而在如今的数字化时代,社交媒体已经成为了企业开发外贸客户的重要渠道之一。本文好选客小编将从选择社媒找外贸客户的原因和方法等方面,为您介绍如何通过社媒找到外贸客户。 图片出处&am…

洛谷P8706 [蓝桥杯 2020 省 AB1] 解码 C语言/C++

[蓝桥杯 2020 省 AB1] 解码 题目描述 小明有一串很长的英文字母,可能包含大写和小写。 在这串字母中,有很多连续的是重复的。小明想了一个办法将这串字母表达得更短:将连续的几个相同字母写成字母 出现次数的形式。 例如,连续…

【蓝桥杯省赛真题38】Scratch流星坠落 少儿编程scratch编程蓝桥杯省赛比赛真题讲解

目录 scratch流星坠落 一、题目要求 编程实现 二、案例分析 1、角色分析

vue3中使用swiper及遇到的问题

vue3中使用swiper 一、安装swiper二、使用swiper三、echarts+swiper一、安装swiper 使用npm install swiper安装swpier插件 npm install swiper -s // @9.2.0 // 或者安装指定版本 npm install swiper@8.4.7 -s二、使用swiper 直接按照官网的引用方法,项目会报错 解决方法:…

【复现论文】Looking here or there? Gaze Following in 360-Degree Images

【复现论文】Looking here or there? Gaze Following in 360-Degree Images 论文:Looking here or there? Gaze Following in 360-Degree Images 论文链接:Looking here or there? Gaze Following in 360-Degree Images 复现github链接&#xff1a…

List集合以及它的实现类和队列、栈

List集合 Collection层次的结构接口中,一些允许有重复的元素,例如:List接口。而另外一些不允许有重复的元素,例如:Set接口。其中也分为了有序与无序的(存储顺序)。 在JDK中没有提供Collection…

ppt文件太大怎么变小,这6种方式很简单

ppt文件太大怎么变小? ppt文件通常用于公司会议、教育授课和商务演讲等场合,由于需要实现各种文档效果并插入大量图片,因此文件大小要远大于普通文档文件,占用的存储空间也不少。如果电脑中存储了大量PPT文件,并且不方…

不服不行!盘点那些编程界的天才少年,11岁参加国际比赛,靠奖金赚了40万美金

程序员是一项专业性极强的工作,需要很强的思维能力和动手能力,所以大多数程序员要比普通人更聪明,与其他职业相比,程序员应该是最爱学习的一行了。科技网络的发展太快,新的技术层出不穷,时刻都要更新自己的…

L2-041 插松枝 判断量少(PTA 天梯赛)

人造松枝加工场的工人需要将各种尺寸的塑料松针插到松枝干上,做成大大小小的松枝。他们的工作流程(并不)是这样的: 每人手边有一只小盒子,初始状态为空。每人面前有用不完的松枝干和一个推送器,每次推送一…

计算机组成原理第二章——数据的表示与运算(下)

提示:时光清浅处 一步一安然 文章目录前言2.3.1 浮点数的表示2.3.2 IEEE7542.2.3 浮点数的运算前言 本节主要讲三个问题,浮点数的表示,IEEE 754标准,浮点数的加减运算 2.3.1 浮点数的表示 浮点数的作用和基本原理 定点数可表示…

由浅入深了解HashMap源码

由经典面试题引入,讲解一下HashMap的底层数据结构?这个面试题你当然可以只答,HashMap底层的数据结构是由(数组链表红黑树)实现的,但是显然面试官不太满意这个答案,毕竟这里有一个坑需要你去填&a…

Java容器使用注意点

前置:问题 判空集合转map集合遍历集合去重集合转数组数组转集合 一:集合判空 《阿里巴巴 Java 开发手册》的描述如下: 判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()0 的方式。 我们在开发中也…

AI大模型突围战

最近,GPT赛道上争议不断,先是GPT-4的问世,用一些亮眼的功能让人类感叹自己是不是要丢工作?紧接着又有一封联名信刷屏,图灵奖得主、AI三巨头之一的约书亚本吉奥、埃隆马斯克等123个业界大佬,呼吁所有人工智能…

Vcpkg安装指定版本包或自定义安装包

文章目录前言寻找版本安装后话前言 windows一直用着vcpkg作为C跨平台开发的包管理,有些依赖要指定版本库,vcpkg目前最新的openssl版本是3.1.0,我想安装其他版本为例,开始本教程 寻找版本 以openssl举例,在vcpkg目录中…

react3:受控组件(表单默认变成受控)-组件通信 - typescript项目

受控组件:表单 非受控组件表单元素值不受所在组件状态的控制, 我们将这样的表单元素称作: 非受控组件. 受控组件受控组件 : 值受到 React 组件状态控制的表单元素一般是通过 defaultValue 属性, onChange 事件配合将非受控组件变为受控组件. 多表单元素操作 &#…

如果当前node.js版本和项目需要版本不一样,卸载重装其他版本node.js的方法

其实这种node.js版本不一样的问题,可以选择用nvm来管理node.js的不同版本。 此处仅总结卸载当前版本node.js重新安装所需版本node.js的方法 另:现在 用Vite官网里面的 yarn/npm等 的方法,创建Vue3项目——需要12版本的node.js 以下以本人 …

Vue3中readonly 与 shallowReadonly的使用区别?

文章目录前言readonly强行修改readonly:shallowReadonlyshallowReadonly强行修改结果:前言 readonly: 让一个响应式数据变为只读的(深只读)。 shallowReadonly: 让一个响应式数据变为只读的(浅只读&#x…