在MongoDB建模1对N关系的基本方法

news2024/11/24 2:26:09

请添加图片描述
“我在 SQL 和规范化数据库方面拥有丰富的经验,但我只是 MongoDB 的初学者。如何建立一对 N 关系模型?” 这是我从参加 MongoDB 分享日活动的用户那里得到的最常见问题之一。

我对这个问题没有简短的答案,因为方法不只有一种,还有很多种方法。MongoDB 拥有丰富而细致的词汇来表达 SQL 中的内容,这些词汇被扁平化为术语“One-to-N”。让我带您了解一下您在建模一对 N 关系时的选择。

这里有很多值得讨论的内容,在这篇文章中,我将讨论建模一对 N 关系的三种基本方法。我还将介绍更复杂的模式设计,包括非规范化双向引用。我将回顾所有的选择,并为您提供一些建议,以便您在建模单个一对 N 关系时可能会考虑的数千个选择中进行选择。想了解什么是**「数据库非规范化」**,以及何时不对数据进行非规范化,您可以直接跳转到文章末尾。

许多初学者认为在 MongoDB 中进行“一对多”建模的唯一方法是将子文档数组嵌入到父文档中,但事实并非如此。仅仅因为您可以嵌入文档,并不意味着您应该嵌入文档。

在设计 MongoDB 模式时,您需要从一个在使用 SQL 和规范化表时永远不会考虑的问题开始:**关系的基数是什么?**不那么正式地说:**您需要更细致地描述您的“一对多”关系:是“一对几个”、“一对多”还是“一对数”?**根据具体情况,您可以使用不同的格式来建模关系。

基础知识:一对多建模

“一对多”的一个例子可能是一个人的地址。这是嵌入的一个很好的用例。
您可以将地址放入 Person 对象内部的数组中:

db.person.findOne()
{
name: ‘Kate Monster’,
ssn: ‘123-456-7890’,
addresses : [
{ street: ‘123 Sesame St’, city: ‘Anytown’, cc: ‘USA’ },
{ street: ‘123 Avenue Q’, city: ‘New York’, cc: ‘USA’ }
]
}

这种设计具有嵌入的所有优点和缺点。主要的优点是你不必执行单独的查询来获取嵌入的细节;主要的缺点是你无法以独立的实体方式访问嵌入的细节。

例如,如果你正在建模一个任务跟踪系统,每个人都会被分配一些任务。将任务嵌入到人员文档中会使像“显示明天到期的所有任务”这样的查询变得比必要的复杂。我将在本文的后面部分介绍一种更适合检索这种用例数据的设计。

基础知识:一对多

“一对多”的一个例子可能是一个备件订购系统中的产品零件。
每个产品可能有多达数百个备件,但绝不会超过几千个左右(所有那些不同尺寸的螺栓、垫圈和垫片都会相加)。这是引用的一个很好的使用案例。你可以将零件的ObjectID放在产品文档的数组中。
*对于这些示例,我使用2字节的ObjectID,因为它们更容易阅读。实际的代码将使用12字节的ObjectID。

每个零件都有自己的文档:

db.parts.findOne()
{
_id : ObjectID(‘AAAA’),
partno : ‘123-aff-456’,
name : ‘#4 grommet’,
qty: 94,
cost: 0.94,
price: 3.99

每个产品都有自己的文档,其中包含了构成该产品的零件的ObjectID引用数组:

db.products.findOne()
{
name : ‘left-handed smoke shifter’,
manufacturer : ‘Acme Corp’,
catalog_number: 1234,
parts : [ // array of references to Part documents
ObjectID(‘AAAA’), // reference to the #4 grommet above
ObjectID(‘F17C’), // reference to a different Part
ObjectID(‘D2AA’),
// etc
]

然后,你将使用应用程序级联接来检索特定产品的零件:

 // Fetch the Product document identified by this catalog number
> product = db.products.findOne({catalog_number: 1234});
   // Fetch all the Parts that are linked to this Product
> product_parts = db.parts.find({_id: { $in : product.parts } } ).toArray() ;

为了高效操作,您需要在“products.catalog_number”上有一个索引。请注意,“parts._id”上始终有一个索引,因此查询始终高效。

这种引用方式与嵌入具有互补的优点和缺点。**每个部分都是一个独立的文档,因此可以轻松地独立搜索和更新它们。**使用此模式的一个权衡是必须执行第二个查询才能获取有关产品零件的详细信息。(但是在我们讨论非规范化之前请保持这个想法。)

作为额外的好处,此模式允许您拥有多个产品使用的单独部件,因此您的一对 N 模式就变成了 N 对 N 模式,而不需要连接表!

基础知识:一到百千万
“one-to-squillions”的一个例子可能是收集不同机器的日志消息的事件日志系统。
任何给定的主机都可以生成足够的消息来溢出 16 MB 文档大小,即使您在数组中存储的只是 ObjectID。这是“父引用”的经典用例。您将有一个主机文档,然后将主机的 ObjectID 存储在日志消息的文档中。

db.hosts.findOne()
{
_id : ObjectID(‘AAAB’),
name : ‘goofy.example.com’,
ipaddr : ‘127.66.66.66’
}

db.logmsg.findOne()
{
time : ISODate(“2014-03-28T09:42:41.382Z”),
message : ‘cpu is on fire!’,
host: ObjectID(‘AAAB’) // Reference to the Host document
}
您可以使用(略有不同)应用程序级联接来查找主机的最新 5,000 条消息:
// find the parent ‘host’ document
host = db.hosts.findOne({ipaddr : ‘127.66.66.66’}); // assumes unique index
// find the most recent 5000 log message documents linked to that host
last_5k_msg = db.logmsg.find({host: host._id}).sort({time : -1}).limit(5000).toArray()

回顾

因此,即使在这个基本级别,设计 MongoDB 模式时也比为规范化数据库设计类似的关系数据库模式时需要考虑更多。您需要考虑两个因素:
● 一对 N 的“N”端的实体是否需要独立?
● 关系的基数是多少:是一对多吗?一对多;还是一比十呢?
基于这些因素,您可以选择三种基本的一对 N 模式设计之一:
● 如果基数是一对多,并且不需要在父对象的上下文之外访问嵌入的对象,则嵌入 N 端。
● 如果基数是一对多或者 N 端对象由于任何原因应该独立,则使用对 N 端对象的引用数组。
● 如果基数为 1 到 squillions,请使用对 N 端对象中的 One-side 的引用。

中级:双向参考

如果您想变得更奇特一点,您可以结合两种技术,并在模式中包含两种引用样式,既具有从“一”侧到“多”侧的引用,又具有从“多”侧到“一”侧的引用。

举个例子,让我们回到任务跟踪系统。有一个保存“人员”文档的“人员”集合,一个保存任务文档的“任务”集合,以及从人员到任务的一对 N 关系。应用程序需要跟踪人员拥有的所有任务,因此我们需要将人员引用到任务。

通过对 Task 文档的引用数组,单个 Person 文档可能如下所示:

db.person.findOne()
{
    _id: ObjectID("AAF1"),
    name: "Kate Monster",
    tasks [     // array of references to Task documents
        ObjectID("ADF9"), 
        ObjectID("AE02"),
        ObjectID("AE73") 
        // etc
    ]
}

另一方面,在某些其他上下文中,此应用程序将显示任务列表(例如,多人项目中的所有任务),并且需要快速找到负责每个任务的人员。为此,您可以通过在任务文档中添加对人员的附加引用来优化数据检索。

db.tasks.findOne()
{
    _id: ObjectID("ADF9"), 
    description: "Write lesson plan",
    due_date:  ISODate("2014-04-01"),
    owner: ObjectID("AAF1")     // Reference to Person document
}

此设计具有“一对多”模式的所有优点和缺点,但还添加了一些内容。将额外的“所有者”引用放入任务文档中意味着可以快速轻松地找到任务的所有者,但这也意味着如果您需要将任务重新分配给另一个人,则需要执行两次更新,而不仅仅是一次。具体来说,您必须更新从人员到任务文档的引用以及从任务到人员的引用。
*对于正在阅读本文的关系数据库专家来说,您是对的;在规范化数据库模型上使用此模式设计意味着不再可能通过单个原子更新将任务重新分配给新人员。这对于我们的任务跟踪系统是可以的;您需要考虑这是否适合您的特定用例。

中级:具有一对多关系的数据库非规范化

除了对各种关系进行建模之外,您还可以将非规范化添加到模式中。这可以消除在某些情况下执行应用程序级联接的需要,但代价是执行更新时会增加一些复杂性。一个例子将有助于阐明这一点。

数据库非规范化从多到一

对于零件示例,您可以将零件名称非规范化到“parts[]”数组中。作为参考,这里是没有非规范化的产品文档版本。

db.products.findOne()
{
name : ‘left-handed smoke shifter’,
manufacturer : ‘Acme Corp’,
catalog_number: 1234,
parts : [ // array of references to Part documents
ObjectID(‘AAAA’), // reference to the #4 grommet above
ObjectID(‘F17C’), // reference to a different Part
ObjectID(‘D2AA’),
// etc
]
}

非规范化意味着您在显示产品的所有部件名称时不必执行应用程序级联接,但如果您需要有关部件的任何其他信息,则必须执行该联接。

db.products.findOne()
{
name : ‘left-handed smoke shifter’,
manufacturer : ‘Acme Corp’,
catalog_number: 1234,
parts : [
{ id : ObjectID(‘AAAA’), name : ‘#4 grommet’ }, // Part name is denormalized
{ id: ObjectID(‘F17C’), name : ‘fan blade assembly’ },
{ id: ObjectID(‘D2AA’), name : ‘power switch’ },
// etc
]
}

虽然可以更轻松地获取部件名称,但这只会在应用程序级连接中添加一些客户端工作:

// Fetch the product document
> product = db.products.findOne({catalog_number: 1234});  
  // Create an array of ObjectID()s containing *just* the part numbers
> part_ids = product.parts.map( function(doc) { return doc.id } );
  // Fetch all the Parts that are linked to this Product
> product_parts = db.parts.find({_id: { $in : part_ids } } ).toArray() ;

**非规范化可以为您节省非规范化数据的查找,但代价是更新成本更高,**因为您向数据库添加了一些数据冗余:如果您已将部件名称非规范化到产品文档中,那么当您更新部件名称时您还必须更新“产品”集合中出现的每个位置。

**仅当读取与更新比率较高时,非规范化才有意义。**如果您经常读取非规范化数据,但很少更新它,那么为了获得更高效的查询性能,付出较慢的写入性能和更复杂的冗余数据更新的代价通常是有意义的。随着更新相对于查询变得更加频繁,非规范化带来的节省会减少。

例如,假设零件名称很少变化,但现有数量经常变化。这意味着,虽然出于数据完整性的目的,将部件名称非规范化到产品文档中是有意义的,但对现有数量进行非规范化是没有意义的。

**另请注意,如果对字段进行非规范化,您将失去对该字段执行原子和独立更新的能力。**就像双向引用一样,如果您先更新零件文档中的零件名称,然后再更新产品文档中的零件名称,则可能会出现数据异常,因为将存在一个亚秒间隔,其中产品文档中的非规范化名称不会反映零件文档中新的、更新的值。

数据库从一到多的非规范化

您还可以将字段从“一”侧非规范化为“多”侧:

> db.parts.findOne()
{
    _id : ObjectID('AAAA'),
    partno : '123-aff-456',
    name : '#4 grommet',
    product_name : 'left-handed smoke shifter',   // Denormalized from the ‘Product’ document
    product_catalog_number: 1234,                     // Ditto
    qty: 94,
    cost: 0.94,
    price: 3.99
}

但是,如果您已将产品名称非规范化到零件文档中,那么当您更新产品名称时,您还必须更新它在“零件”集合中出现的每个位置,以避免数据异常。这可能是一个更昂贵的更新,因为您要更新多个部件而不是单个产品。因此,以这种方式进行非规范化时,考虑读写比率就显得尤为重要。

中级:具有一对一关系的数据库反规范化

您还可以将一对一的关系非规范化。这可以通过以下两种方式之一进行:您可以将有关“一”侧的信息(来自“hosts”文档)放入“squillions”侧(日志条目),或者可以将“squillions”中的摘要信息放入“一”侧。

下面是“squillions”方面的非规范化示例。我将把主机的 IP 地址(从“一侧”)添加到单独的日志消息中:

db.logmsg.findOne()
{
time : ISODate(“2014-03-28T09:42:41.382Z”),
message : ‘cpu is on fire!’,
ipaddr : ‘127.66.66.66’,
host: ObjectID(‘AAAB’)
}

您对来自特定 IP 地址的最新消息的查询变得更加容易:现在只需一个查询,而不是两个。

last_5k_msg = db.logmsg.find({ipaddr : ‘127.66.66.66’}).sort({time :
-1}).limit(5000).toArray()

事实上,如果您只想在“one”一侧存储有限数量的信息,则可以将其全部非规范化到“squillions”一侧,并完全摆脱“一”侧集合:

db.logmsg.findOne()
{
time : ISODate(“2014-03-28T09:42:41.382Z”),
message : ‘cpu is on fire!’,
ipaddr : ‘127.66.66.66’,
hostname : ‘goofy.example.com’,
}

另一方面,您也可以非规范化为“一”侧。假设您希望在“主机”文档中保留来自某个主机的最后 1,000 条消息。您可以使用 MongoDB 2.4 中引入的 $each / $slice 功能来保持该列表排序,并且仅保留最后 1,000 条消息:
日志消息保存在“logmsg”集合以及“hosts”文档中的非规范化列表中。这样,当消息超出“hosts.logmsgs”数组时,该消息就不会丢失。

//  Get log message from monitoring system
logmsg = get_log_msg();
log_message_here = logmsg.msg;
log_ip = logmsg.ipaddr;
  // Get current timestamp
now = new Date()
  // Find the _id for the host I’m updating
host_doc = db.hosts.findOne({ipaddr : log_ip },{_id:1});  // Don’t return the whole document
host_id = host_doc._id;
  // Insert the log message, the parent reference, and the denormalized data into the ‘many’ side
db.logmsg.save({time : now, message : log_message_here, ipaddr : log_ip, host : host_id ) });
  // Push the denormalized log message onto the ‘one’ side
db.hosts.update( {_id: host_id }, 
        {$push : {logmsgs : { $each:  [ { time : now, message : log_message_here } ],
                           $sort:  { time : 1 },  // Only keep the latest ones 
                           $slice: -1000 }        // Only keep the latest 1000
         }} );

请注意,使用投影规范 ( {_id:1} ) 可以防止 MongoDB 通过网络传输整个“hosts”文档。通过告诉 MongoDB 仅返回 _id 字段,您可以将网络开销减少到存储该字段所需的几个字节(加上有线协议开销)。

正如“一对多”情况下的非规范化一样,您需要考虑读取与更新的比率。仅当日志消息相对于应用程序需要查看单个主机的所有消息的次数来说并不频繁时,将日志消息非规范化到“主机”文档中才有意义。如果您希望查看数据的频率低于更新数据的频率,那么这种特殊的非规范化是一个坏主意。

回顾

在本节中,我介绍了您在嵌入、子引用或父引用基础知识之后所拥有的其他选择。
● 如果双向引用可以优化您的架构,并且您愿意付出没有原子更新的代价,那么您可以使用双向引用
● 如果您正在引用,则可以将数据从“一”侧反规范化 到“N”侧,或者从“N”侧反规范化到“一”侧。
在决定数据库非规范化时,请考虑以下因素:
● 您无法对非规范化数据执行原子更新。
● 仅当读写比率较高时,非规范化才有意义。

看看所有这些数据库非规范化选择!
特别是数据库反规范化,为您提供了很多选择:如果关系中有 8 个反规范化候选者,则有 2 8 (1,024) 种不同的反规范化方式(包括根本不进行反规范化)。将其乘以三种不同的引用方式,您就有超过 3,000 种不同的方式来建模关系。

你猜怎么了?你现在陷入了“选择悖论”。因为你有很多潜在的方法来建模“一对N”关系,所以你选择如何建模它变得更加困难。

数据库非规范化经验法则:您的彩虹指南

以下是一些“经验法则”,可以指导您完成这些无数(但不是无限)的选择:
1、赞成嵌入,除非有令人信服的理由不这样做。
2、需要单独访问一个对象是不嵌入它的一个令人信服的理由。
3、数组不应该无限增长。如果“多”端有超过几百个文档,则不要嵌入它们;如果“多”端有超过几千个文档,请不要使用 ObjectID 引用数组。高基数数组是不嵌入的一个令人信服的理由。
4、不要害怕应用程序级联接:如果正确索引并使用投影说明符,那么应用程序级联接几乎不会比关系数据库中的服务器端联接贵。
5、考虑非规范化的读写比。大部分被读取且很少更新的字段是非规范化的良好候选者。如果您对经常更新的字段进行非规范化,那么查找和更新所有冗余数据实例的额外工作可能会淹没您从非规范化中获得的节省。
6、与 MongoDB 一样,如何建模数据完全取决于特定应用程序的数据访问模式。您希望构建数据以匹配应用程序查询和更新数据的方式。

你的彩虹指南

在 MongoDB 中建模“一对多”关系时,您有多种选择,因此您必须仔细考虑数据的结构。您需要考虑的主要标准是:
● 关系的基数是什么?是“一对多”、“一对多”还是“一对几”?
● 您需要单独访问“N”侧的对象,还是仅在父对象的上下文中访问?
● 特定字段的更新与读取之比是多少?
您构建数据的主要选择是:
● 对于“一对多”,您可以使用一系列嵌入文档。
● 对于“一对多”,或者“N”侧必须独立的情况,您应该使用引用数组。如果可以优化您的数据访问模式,您还可以在“N”侧使用“父引用”。
● 对于“one-to-squillions”,您应该在存储“N”侧的文档中使用“parent-reference”。
一旦您决定了数据库设计中数据的总体结构,您就可以(如果您选择)跨多个文档对数据进行非规范化,方法是将数据从“One”侧非规范化到“N”侧,或者从“N”侧非规范化数据。将“N”侧插入“One”侧。您只需对经常读取、读取次数远多于更新次数以及不需要强一致性的字段执行此操作,因为更新非规范化值更慢、更昂贵,并且不是原子的。

生产力和灵活性

所有这一切的结果是 MongoDB 使您能够设计数据库模式以满足应用程序的需求。您可以在 MongoDB 中构建数据,使其轻松适应变化,并支持充分利用应用程序所需的查询和更新。

附录一:什么是数据库非规范化?

数据库非规范化技术背后有一个非常简单的原则:一起访问的数据应该存储在一起。非规范化是复制字段或从现有字段派生新字段的过程。非规范化数据库可以在多种情况下提高读取性能和查询性能,例如:
● 重复查询需要另一个集合中的大型文档中的一些字段。您可以选择在重复查询目标集合的嵌入文档中维护这些字段的副本,以避免合并两个不同的集合或执行频繁的 $lookup 操作。
● 经常需要集合中某些字段的平均值。您可以选择在单独的集合中创建派生字段,该集合作为写入的一部分进行更新,并维护该字段的运行平均值。
虽然嵌入没有重复数据的文档或数组是对相关数据进行分组的首选,但当必须维护单独的集合时,非规范化可以提高读取性能。

单个文档可以代表整个客户订单或特定太阳能电池板一天的能源产量。一些来自关系数据库、更熟悉规范化数据库模型世界的用户将文档视为表中的一行或分布在多个表中。虽然没有什么可以阻止您以这种方式构建架构,但这并不是存储数据或查询大量数据(尤其是 IoT 数据)的更有效方法。

与关系数据库的规范化数据库模型相比,非规范化使您能够提高数据库性能,同时减少连接。
尽管 MongoDB 支持副本集(从版本 4.0 开始)和分片集群(从版本 4.2 开始)的多文档事务,但对于许多场景,非规范化数据库模型将继续最适合您的数据和用例。

请注意,对于非规范化数据库**,保持一致的重复数据非常重要。**然而,在大多数情况下,数据检索性能和查询执行的提高将超过数据冗余副本的存在以及避免数据不一致的需要。

附录二:数据库非规范化与数据库规范化相比何时有意义?

**当读写比率较高时,非规范化就有意义。**通过非规范化,您可以避免昂贵的连接,但代价是进行更复杂和更昂贵的更新。因此,您应该仅对那些最常读取且很少更新的字段进行非规范化,因为数据冗余不是什么问题。

l o o k u p 操作根据指定字段连接同一数据库中两个集合的数据。当您的数据结构类似于关系数据库并且您需要对通常分布在多个表中的大型分层数据集进行建模时, lookup 操作根据指定字段连接同一数据库中两个集合的数据。当您的数据结构类似于关系数据库并且您需要对通常分布在多个表中的大型分层数据集进行建模时, lookup操作根据指定字段连接同一数据库中两个集合的数据。当您的数据结构类似于关系数据库并且您需要对通常分布在多个表中的大型分层数据集进行建模时,lookup 操作可能会很有用。但是,这些操作可能很慢并且占用大量资源,因为它们需要在两个集合而不是单个集合上读取和执行逻辑。

如果您经常运行 $lookup 操作,请考虑通过非规范化重组您的架构,以便您的应用程序可以查询单个集合来获取它需要的所有信息。使用嵌入文档和数组来捕获单个文档结构中数据之间的关系。使用数据库非规范化来利用 MongoDB 的丰富文档模型,这允许您的应用程序在单个查询执行中检索和操作相关数据。

通常,对于操作数据库来说,采用数据库非规范化是最有利的——在单个操作中读取或写入整个记录的效率超过了存储需求的任何适度增加。

标准化数据模型使用文档之间的引用来描述关系。一般来说,在以下场景中使用标准化数据模型:
● 当嵌入会导致数据重复但无法提供足够的读取性能优势来抵消数据重复的影响时。
● 表示更复杂的多对多关系。
● 对大型分层数据集进行建模。

如今,市场上有各种各样的数据库设计选项。关系数据库模型和数据库规范化的实践有其优点和局限性。跨表执行联接操作的需要会影响性能、抑制扩展并带来技术和认知开销。开发人员经常在数据库中创建变通方法以实现效率优势。那些基于高性能关系数据库的应用程序通常会结合临时非规范化、物化视图和外部缓存层,以克服规范化关系数据库的限制。
请添加图片描述

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

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

相关文章

HarmonyOS(鸿蒙)ArkUI组件

方舟开发框架(简称ArkUI)为HarmonyOS应用的UI开发提供了完整的基础设施,包括简洁的UI语法、丰富的UI功能(组件、布局、动画以及交互事件),以及实时界面预览工具等,可以支持开发者进行可视化界面…

风速预测(八)VMD-CNN-Transformer预测模型

往期精彩内容: 时序预测:LSTM、ARIMA、Holt-Winters、SARIMA模型的分析与比较-CSDN博客 风速预测(一)数据集介绍和预处理-CSDN博客 风速预测(二)基于Pytorch的EMD-LSTM模型-CSDN博客 风速预测&#xff…

负数,小数转换二进制

负数转换二进制 例:在带符号整数signed char的情况下,-57如何被表示成负数呢?在计算机中又是如何计算66-57呢? 解析 考虑int占有32位太长,因此使用只占8位的signed char类型来举例。57用二进制表示位00111001&#…

【Mysql数据库基础04】连接查询、内连接、外连接

Mysql数据库基础04 0 该博客所要用的数据库表的属性1 SQL92 内连接1.1 等值连接1.1.1 两个表的顺序可以调换1.1.2 加筛选1.1.3 加分组1.1.4 加排序1.1.5 三表连接 1.2 非等值连接1.3 自连接 2 SQL99 内连接2.1 等值连接2.2 非等值连接2.3 自连接 3 外连接3.1 左外和右外连接 4 …

大模型主流微调训练方法总结 LoRA、Adapter、Prefix-tuning、P-tuning、Prompt-tuning 并训练自己的数据集

大模型主流微调训练方法总结 LoRA、Adapter、Prefix-tuning、P-tuning、Prompt-tuning 概述 大模型微调(finetuning)以适应特定任务是一个复杂且计算密集型的过程。本文训练测试主要是基于主流的的微调方法:LoRA、Adapter、Prefix-tuning、P-tuning和Prompt-tuning,并对…

网络原理(3)——TCP协议

目录 一、连接管理 二、三次握手 1、何为三次握手? 2、三次握手有何意义? 三、四次挥手 三次握手和四次挥手的相似之处和不同之处 (1)相似之处 (2)不同之处 四、TCP的状态 建立连接: 断开…

Matlab中inv()函数的使用

在Matlab中,inv()函数是用来求解矩阵的逆矩阵的函数。逆矩阵是一个与原矩阵相乘后得到单位矩阵的矩阵。在数学中,矩阵A的逆矩阵通常用A^-1表示。 什么是逆矩阵 在数学中,对于一个n阶方阵A,如果存在一个n阶方阵B,使得…

华为综合案例-普通WLAN全覆盖配置(1)

适用范围和业务需求 适用范围 本案例适用于大多数场景,如办公室、普通教室、会议室等普通非高密场景。 业务需求 主要业务需求如下: 接入需求 随时、随地无线业务接入。无线覆盖需要做到覆盖均匀、无盲区。 无线漫游需求 多层网络、快速切换、网络…

P1143 进制转换题解

题目 请你编一程序实现两种不同进制之间的数据转换。 输入输出格式 输入格式 共三行,第一行是一个正整数,表示需要转换的数的进制n (2≤n≤16),第二行是一个n进制数,若n>10则用大写字母A∼F表示数码10∼15,并且…

使用 GTSAM 进行曲线拟合的示例

GTSAM介绍 GTSAM(通用因子图优化库)是一种用于状态估计和传感器数据融合的开源C++库。它提供了强大的工具,用于在机器人和自主系统领域进行感知、决策和控制。 功能和特点 状态估计与优化: GTSAM 提供了灵活且高效的状态估计框架,能够处理从传感器获取的数据,并…

接口测试系列 —— 转转交易业务场景接口测试实践

01 Why接口测试 一、提高效率 关键词:QA 职责保质保量的完成需求测试工作在保证质量的前提下提高效率,要保证质量,首先需要先弄清楚这次需求的测试范围,针对性的使用不同的测试方法,而接口测试就是其中的一种&#x…

JMeter 并发测试和持续性压测详解

并发测试和持续性压测都是评估系统性能的常用方法,它们可以帮助开发人员发现并解决系统中的性能问题。本文来详细介绍下。 概念 并发测试: 旨在评估系统在同时处理多个用户请求时的性能。在这种 测试 中,系统会暴露于一定数量的用户负载下&…

Bito插件

此文档只作用于指导性工作,更多资料请自行探索。 1、插件安装与介绍 1.1 插件下载与安装 在idea中搜索:Bito Bito is also available for:​编辑VSCode​编辑JetBrains​编辑CLI 1.2 官方介绍 插件:ChatGPT GPT-4 - Bito AI Code Assista…

SQLiteC/C++接口详细介绍sqlite3_stmt类(五)

返回:SQLite—系列文章目录 上一篇:SQLiteC/C接口详细介绍sqlite3_stmt类(四)- 下一篇: 无 12. sqlite3_bind_text16函数 sqlite3_bind_text16函数用于将UTF-16编码的文本数据(字符串)绑定…

推荐一款管理hosts文件的利器

程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一份大厂面试资料《史上最全大厂面试题》,Springboot、微服务、算法、数据结构、Zookeeper、Mybatis、Dubbo、linux、Kafka、Elasticsearch、数据库等等 …

windows下不同python版本切换

一、简介 有时候在windows环境下会安装很多个不同的Python版本,但是在运行一些特定环境时,又需要特定的python版本。于是就需要切换Python版本。 二、实操 右键“我的电脑”-》属性 右侧“高级系统设置” “高级”-》“环境变量” 双击“Path” 可以看到…

计算机视觉之三维重建(2)---摄像机标定

文章目录 一、回顾线代1.1 线性方程组的解1.2 齐次线性方程组的解 二、透镜摄像机的标定2.1 标定过程2.2 提取摄像机参数2.3 参数总结 三、径向畸变的摄像机标定3.1 建模3.2 求解 四、变换4.1 2D平面上的欧式变换4.2 2D平面上的相似变换和仿射变换4.3 2D平面上的透射变换4.4 3D…

深入浅出前端本地储存(1)

引言 2021 年,如果你的前端应用,需要在浏览器上保存数据,有三个主流方案: CookieWeb Storage (LocalStorage)IndexedDB 这些方案就是如今应用最广、浏览器兼容性最高的三种前端储存方案 今天这篇文章就聊一聊这三种方案的历史…

全球首例AI软件工程师Devin:Cognition AI引领智能编程新纪元

近日,初创企业Cognition AI震撼发布了全球首位AI软件工程师——Devin,这一开创性的突破标志着人工智能在编程与软件开发领域的应用迈上了全新的台阶。Devin以其卓越的计算机推理与规划能力,正在重新定义我们对软件工程实践的理解。 Devin&am…

软件设计师:03 - 数据库系统

一、数据模型的分类 1.1、概念数据模型 1.2、结构数据模型 1.3 真题 二、三级模式 概念模式对应的是基本表,概念模式也称为模式 外模式对应的是视图,也称用户模式或者子模式 内模式对应的是数据库里面的存储文件,也称存储模式 真题 三、两级…