数据库在后端开发中的角色
在应用程序开发领域,数据库在数据的持久化存储、检索和操作过程中扮演着至关重要的角色。 因此,数据库为应用程序执行的大多数(若非全部)功能提供了基础支撑。 无论是简单还是复杂的操作,数据库都不可或缺;例如注册新用户是简单任务,而分析用户行为模式以实现个性化推荐系统则是复杂操作。
在开发网站或应用程序的后端时,数据库通常是架构中最重要的组成部分。 这里是业务数据、用户信息、应用程序状态等内容被管理和存储的地方。 后端应用程序需要与数据库进行交互,以执行四项基本的 CRUD 操作(创建、读取、更新和删除)。 这些操作对于任何数据驱动的应用程序都是必需的。
● 创建:该操作涉及向数据库添加新数据。 包括注册新用户、创建新产品或插入新博客文章等操作。 这些操作通常需要后端应用程序接收来自客户端应用的数据,执行验证后向数据库发出 INSERT 命令。
● 读取:从数据库读取数据是最常见的操作。 每当用户登录、搜索产品或浏览网页应用的不同页面时,后端都会执行读取操作。 这涉及向数据库发出 SELECT 命令,通常附带特定条件来筛选数据。
● 更新:更新操作会修改数据库中的现有数据。 例如当用户更新个人资料、更改设置,或电商应用在购买后更新产品库存时。 这涉及后端向数据库发出 UPDATE 命令。
● 删除:最后,删除操作会从数据库中移除现有数据。 这可能发生在用户注销账户,或管理员从电商平台下架停售商品时。 后端会向数据库发出 DELETE 命令来执行此操作。
除了这些基本操作外,数据库还支持更复杂的操作,如聚合(计算总和、平均值等类似值)、多表数据连接以及事务(将多个操作组合成一个工作单元)。 应用程序的具体需求应指导数据库的选择,可以是传统的 SQL 数据库(如 MySQL 或 PostgreSQL),也可以是 NoSQL 数据库(如 MongoDB 或 Cassandra)。 两种类型的数据库都以关系格式存储数据。 为了满足各种需求,某些应用程序甚至可能使用多种不同的数据库类型。
无论选择哪种方案,后端开发人员都必须扎实掌握数据库操作的基础知识,包括如何高效设计数据库模式、编写查询语句以及处理潜在的错误和异常。 若具备这些技能,开发者就能确保应用数据层的健壮性与高效性,进而保障整个应用的性能与可靠性。
探索 MongoDB
MongoDB 是一种流行的 NoSQL 数据库,具有高性能、高可用性和易于扩展的特点。 与传统关系型数据库不同,MongoDB 以灵活的类 JSON 文档格式存储数据,这意味着不同文档可以包含不同字段,数据结构也能随时间变化。 正是这种灵活性,加上其他特性,使其成为许多应用中比传统关系型数据库更具吸引力的选择。
灵活的数据模型
MongoDB 相较于关系型数据库的首要优势在于其灵活的架构。 这种灵活性使得应用开发更快速,因为开发者无需预先定义数据结构,或在添加新字段时修改结构。 这也意味着您可以存储复杂的数据结构,如嵌套数组和文档,这在关系型数据库中往往难以实现。
可扩展性
MongoDB 的水平扩展架构使其更适合处理不断增长的大型数据集。 与 SQL 数据库常见的通过增强单台服务器性能进行纵向扩展不同,MongoDB 通过向网络添加更多服务器实现横向扩展。 这种通过添加机器实现扩展的能力(称为分片)是 MongoDB 的强大特性,为大数据挑战提供了有效解决方案。
速度
对于多种类型的操作,MongoDB 通常比关系型数据库更快。 这种速度优势源于其处理大量非结构化数据的能力,以及索引等能显著加快数据访问速度的特性。
复制与高可用性
MongoDB 通过内置的复制功能提高数据可用性,在不同服务器上维护数据的多个副本。 自动故障转移意味着如果主服务器宕机,新的主服务器将自动启动并运行。 此特性有助于确保应用程序持续运行,数据始终可用。
丰富的查询语言
MongoDB 支持丰富且富有表现力的面向对象查询语言。 开发者可以通过单键查询、范围查询、文本搜索、图处理以及通过实时聚合实现的地理空间查询等多种方式,构建能够灵活查询分析数据的应用程序,提供强大的数据访问与分析能力。
必须牢记的是,尽管 MongoDB 具有多种优势,但它并非放之四海而皆准的解决方案。 它并不适合在所有情况下都作为关系型数据库的替代品使用。 对于某些使用场景和应用而言,关系型数据库提供的更结构化方法可能更为有利。
安装和配置 MongoDB
要将 MongoDB 集成到 C++开发环境中,首先需要在系统上安装 MongoDB。 从 MongoDB 官网下载 MongoDB 社区版服务器,并按照安装说明进行操作。 接着安装 MongoDB C++驱动程序,该驱动能让您的 C++应用程序与 MongoDB 交互。 您可以使用 Homebrew 等包管理器安装,或通过 GitHub 从源码编译安装。 安装完成后,在 C++代码中包含必要的头文件。 现在,您就可以使用 MongoDB URI 建立与 MongoDB 服务器的连接,并开始通过 C++应用程序在 MongoDB 数据库中创建、读取、更新和删除数据。
以下是分步操作指南:
安装 MongoDB
根据您使用的操作系统,安装 MongoDB 的过程可能有所不同。 您可以在 MongoDB 文档中找到针对您特定系统的详细说明。 以下是在基于 Unix 的系统上的安装步骤:
更新系统的软件包数据库。
sudo apt-get update
安装 MongoDB。
sudo apt-get install -y mongodb
为确保 MongoDB 能随服务器自动启动和重启,可执行以下命令:
sudo systemctl enable mongodb
安装 MongoDB C++驱动
MongoDB C++驱动程序使您的 C++应用程序能够与 MongoDB 进行交互。BSON 是类 JSON 文档的二进制表示形式,在存储和访问数据时使用。
以下是具体步骤:
首先,安装构建驱动程序所需的软件包:
sudo apt-get install build-essential libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libboost-thread-dev
接下来,克隆 MongoDB C++驱动仓库:
git clone https://github.com/mongodb/mongo-cxx-driver.git
进入目录并编译驱动:
cd mongo-cxx-driver/build
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local ..
make EP_mnmlstc_core
sudo make install
最后,您就可以在 C++程序中使用 MongoDB 了。
连接 MongoDB 的示例程序
以下是一个简单的 C++程序,用于连接同一台机器上运行的 MongoDB 实例:
#include <bsoncxx/json.hpp>
#include <mongocxx/client.hpp>
#include <mongocxx/stdx.hpp>
#include <mongocxx/uri.hpp>
using bsoncxx::builder::stream::close_document;
using bsoncxx::builder::stream::open_document;
using bsoncxx::builder::stream::document;
int main(int, char**) {
mongocxx::instance inst{};
mongocxx::client conn{mongocxx::uri{}};
auto collection = conn["testdb"]["testcollection"];
document builder{};
builder << "name" << "MongoDB" << "type" << "database" << "count" << 1
<< "versions" << bsoncxx::builder::stream::open_array
<< "v3.2" << "v3.0" << "v2.6"
<< close_array
<< "info" << open_document << "x" << 203 << "y" << 102 << close_document;
collection.insert_one(builder.view());
auto cursor = collection.find({});
for (auto&& doc : cursor) {
std::cout << bsoncxx::to_json(doc) << std::endl;
}
}
该程序连接至 MongoDB 实例,向集合中插入文档,随后检索并打印该文档。 需注意,此代码要正常运行,MongoDB 必须在同一机器(localhost)的标准端口(27017)上运行。 若您的 MongoDB 实例运行于不同位置,则需在创建客户端时提供正确的连接 URI。
这些是在 C++环境中运行 MongoDB 必须采取的关键主要步骤。 从此刻起,您将能够开始构建更复杂的应用程序,这些程序可以读取、写入、更新和删除 MongoDB 数据库中的数据。
全栈应用的数据库架构
MongoDB 使用 BSON(一种类似 JSON 文档的二进制表示形式)来存储数据。 与 SQL 数据库不同,MongoDB 是无模式的。 这意味着同一集合中的文档不需要具有相同的字段集,并且字段的数据类型在集合内的不同文档间可以有所不同。
尽管如此,为了获得最佳性能,精心设计数据模型仍然至关重要。 在 MongoDB 中,您应该根据应用程序的数据访问模式,在嵌入子文档和创建新集合之间寻求平衡。
以下是一个假设的博客应用的 MongoDB 模式示例:
{
"user": {
"username": "john_doe",
"email": "john@myblogapp.com",
"password": "hashed_password",
"profiles": [
{
"platform": "twitter",
"handle": "@johndoe"
},
{
"platform": "instagram",
"handle": "@johndoeig"
}
]
},
"posts": [
{
"title": "My first blog post",
"content": "This is my first blog post",
"user": "john_doe",
"comments": [
{
"user": "commenter1",
"content": "Great post!",
"timestamp": "2023-07-01T14:00:00Z"
},
{
"user": "commenter2",
"content": "Thanks for sharing!",
"timestamp": "2023-07-01T15:00:00Z"
}
],
"tags": ["intro", "first", "blog"],
"published_date": "2023-07-01T12:00:00Z"
}
]
}
在此架构中:
● 每个用户拥有用户名、电子邮箱、哈希密码以及包含社交媒体账号的 profiles 数组。
● 帖子存储在数组中,每个帖子包含标题、内容、作者用户名、评论、标签和发布日期。
● 评论存储在每个帖子的数组中,包含评论者用户名、评论内容和评论时间戳。
这种设计将相关数据嵌入到单一文档中,可通过一次查询获取。 这种方式特别适合需要频繁同时访问的数据(如用户资料或博客评论),且适用于不会导致数组无限增长的一对多关系。 若关系会导致数据无限增长,则更适合使用引用和独立集合。 具体决策需根据您的使用场景及应用程序与数据的交互方式而定。
执行 CRUD 操作
CRUD 操作是任何涉及持久化存储的应用程序的基础。CRUD 代表创建(Create)、读取(Read)、更新(Update)和删除(Delete)——这是对任何持久化存储的四种基本操作。
让我们以博客应用为例,看看如何使用 C++ MongoDB 驱动在 MongoDB 中执行这些操作:
图 5.1 MongoDB 的 CRUD 操作
创建
在 MongoDB 中插入新文档使用 insert_one 或 insert_many 函数实现。
创建新用户:
mongocxx::collection user_collection = db["users"];
bsoncxx::builder::stream::document user_doc{};
user_doc << "username" << "new_user"
<< "email" << "new_user@myblogapp.com"
<< "password" << "hashed_password";
user_collection.insert_one(user_doc.view());
上述代码片段展示了如何向'users'集合添加新用户。 我们使用构建器 API 创建 BSON 文档,然后通过 insert_one 函数插入该文档。
读取
要从 MongoDB 集合中读取文档,我们使用 find 函数。
按用户名查找用户:
bsoncxx::builder::stream::document filter_builder;
filter_builder << "username" << "new_user";
bsoncxx::stdx::optional<bsoncxx::document::value> maybe_result = user_collection.find_one(filter_builder.view());
if(maybe_result) {
std::cout << bsoncxx::to_json(*maybe_result) << "\\n";
}
在上述代码片段中,我们构建了一个用户名为"new_user"的筛选文档,然后将该筛选条件传递给 find_one 函数以查找匹配的文档。
更新
在 MongoDB 中更新文档,我们可以使用 update_one 或 update_many 函数。
要更改用户的电子邮件:
bsoncxx::builder::stream::document update_doc;
update_doc << "$set" << bsoncxx::builder::stream::open_document
<< "email" << "new_email@myblogapp.com"
<< bsoncxx::builder::stream::close_document;
user_collection.update_one(filter_builder.view(), update_doc.view());
我们正在创建一个更新文档,使用$set 操作符来更改匹配文档的 email 字段。
删除
要删除文档,我们可以使用 delete_one 或 delete_many 函数。
删除用户:
user_collection.delete_one(filter_builder.view());
这行代码会从'users'集合中删除用户名为"new_user"的用户。
上述所有操作中,为简洁起见省略了错误检查,但在实际应用中,您应该始终检查这些操作的结果是否存在错误或异常。 这些操作都是异步的,C++ MongoDB 驱动提供了多种处理这种异步性的方法。 对于复杂应用,通常会使用聚合函数,它允许在服务器端进行复杂的数据操作和转换。
执行复杂查询:聚合与索引
聚合与索引是 MongoDB 的两大强大功能,使我们能够执行复杂查询、筛选和排序数据,并提高数据库操作效率。
执行聚合操作
MongoDB 中的聚合是对集合中文档执行复杂数据分析的一种方式。 它提供了处理数据记录并返回计算结果的方法。 聚合操作可以将多个文档中的值分组,并对分组后的数据执行各种操作以返回单一结果。
例如,在一个博客应用中,您可能想知道每个用户发表了多少篇文章。 这可以通过 MongoDB 的聚合框架实现。$group 阶段会按照指定的标识表达式对输入文档进行分组,并为每个唯一分组输出一个文档。 然后使用$sum 操作符为每个组递增计数。
具体实现方式如下:
mongocxx::collection posts_collection = db["posts"];
bsoncxx::builder::stream::document group_stage;
group_stage << "$group" << bsoncxx::builder::stream::open_document
<< "_id" << "$username"
<< "count" << bsoncxx::builder::stream::open_document << "$sum" << 1
<< bsoncxx::builder::stream::close_document
<< bsoncxx::builder::stream::close_document;
mongocxx::pipeline p{};
p.group(group_stage.view());
auto cursor = posts_collection.aggregate(p, mongocxx::options::aggregate{});
for(auto doc : cursor) {
std::cout << bsoncxx::to_json(doc) << "\\n";
}
上述代码片段中,我们使用了 MongoDB 的聚合框架。$group 阶段通过指定的_id 表达式(本例中为 username 字段)对输入文档进行分组,并为每个不同的分组输出一个文档。$sum 表达式用于递增每个组的计数。
创建索引
MongoDB 中的索引与其他数据库系统中的索引具有相同的作用。 索引是一种数据结构,它以易于遍历的形式保存集合数据的子集。 该索引存储特定字段或字段集的值,并按索引中指定的字段值排序。
创建索引可以显著提高搜索操作的性能。 例如,在"username"字段上创建升序索引意味着 MongoDB 可以使用该索引来筛选 username 字段的查询,从而加快这些查询速度。
让我们在 users 集合的 username 字段上创建如下索引:
bsoncxx::builder::stream::document index_builder;
index_builder << "username" << 1;
user_collection.create_index(index_builder.view(), {});
在上述代码片段中,我们正在"username"字段上创建一个升序索引。 这意味着 MongoDB 可以使用该索引来筛选 username 字段的查询,从而显著提高这些查询的速度。
索引存在一些权衡,它们可以极大提高读取速度,但会降低写入速度,因为每次插入或更新文档时 MongoDB 都需要更新索引。 重要的是找到合适的平衡点,只为经常查询的字段创建索引。
总结
本章我们深入探讨了数据库的世界,特别聚焦于 MongoDB 及其如何与我们用 C++编写的后端编程进行集成。 首先,我们着重理解了数据库在应用开发中的基础作用,以及在后端执行的各种操作。 数据库管理系统,尤其是 MongoDB,是现代全栈应用不可或缺的核心组件。 这一组件之所以必要,是因为它能够存储、检索和操作驱动用户界面所需的数据。
我们以兼容 C++开发环境的方式继续了 MongoDB 的安装和配置过程。 您已经了解到 MongoDB 作为 NoSQL 数据库,在其使用的数据结构方面具有灵活性,同时具备可扩展性和数据库查询能力。 我们讨论了 MongoDB 中的模式设计,重点探讨了在可能情况下优先采用非规范化或文档嵌入的方式。 我们认识到 MongoDB 模式在性能和可扩展性方面相比传统关系型数据库所具有的优势。 通过一个博客应用示例,我们探讨了模式设计的考量因素,这为后续的数据库交互奠定了坚实基础。
在 MongoDB 实现的最后一步中,我们深入探讨了 CRUD 操作(即创建、读取、更新和删除)。 为了帮助您理解后端与数据库的交互,我们分解了这些基础操作,并提供了分步示例及代码片段。 随后我们讨论了包含聚合与索引的复杂查询。 通过多个实际案例,我们掌握了如何对数据进行分组和操作,以及索引如何通过不同应用场景显著提升查询性能。 这些高级数据库操作的应用为后端开发流程开辟了新途径,能更高效地实现数据操作与检索。 本章所获知识为开发涉及数据库交互与操作的后端应用奠定了坚实基础。
评论前必须登录!
注册