Appearance
1.1 什么是 MongoDB
1. 概述
MongoDB是一个开源的、高性能的、无模式的文档型数据库,属于NoSQL数据库的一种。它使用类似JSON的格式存储数据,具有强大的查询语言、水平扩展能力和丰富的功能特性。MongoDB由10gen公司(现MongoDB Inc.)于2007年开发,2009年首次发布,目前已成为全球最受欢迎的NoSQL数据库之一。
2. 基本概念
2.1 MongoDB的定义
MongoDB是一个基于分布式文件存储的数据库,旨在为Web应用提供可扩展的高性能数据存储解决方案。它将数据存储为类似JSON的文档,这使得数据模型更加灵活和直观。
2.2 核心特性
文档型存储
MongoDB使用文档作为基本数据单元,文档是键值对的集合,类似于JSON对象。这种存储方式使得数据结构更加灵活,可以轻松表示复杂的数据关系。
无模式设计
MongoDB不需要预先定义表结构,文档的结构可以动态变化。这种设计使得开发更加灵活,能够快速适应业务需求的变化。
强大的查询语言
MongoDB提供了丰富的查询语言,支持复杂的查询条件、聚合操作、地理空间查询等功能。
水平扩展能力
MongoDB支持分片技术,可以通过添加服务器来水平扩展存储容量和吞吐量,满足大规模数据存储需求。
高可用性
MongoDB通过副本集机制提供自动故障转移和数据冗余,确保系统的高可用性。
丰富的索引支持
MongoDB支持多种索引类型,包括单字段索引、复合索引、文本索引、地理空间索引等,可以显著提升查询性能。
3. 原理深度解析
3.1 存储引擎
MongoDB使用WiredTiger作为默认存储引擎,它提供了以下特性:
- 文档级别的并发控制
- 压缩功能,减少存储空间占用
- 检查点机制,确保数据持久性
- 支持多种数据类型和索引
3.2 BSON格式
MongoDB使用BSON(Binary JSON)格式存储数据,BSON是JSON的二进制编码形式,具有以下优势:
- 支持更多的数据类型(如日期、二进制数据等)
- 更高效的序列化和反序列化
- 支持遍历和查询操作
3.3 内存映射
MongoDB使用内存映射文件技术,将数据文件映射到内存中,这样可以:
- 提高数据访问速度
- 简化数据管理
- 利用操作系统的缓存机制
4. 常见错误与踩坑点
4.1 错误1:认为MongoDB完全不需要设计数据结构
错误表现:随意插入各种结构的文档,导致查询和维护困难
产生原因:误解了"无模式"的含义,认为可以完全不设计数据结构
解决方案:虽然MongoDB不需要预先定义严格的模式,但仍需要根据业务需求设计合理的数据结构,保持文档结构的一致性
4.2 错误2:忽略索引的重要性
错误表现:在大数据量情况下查询性能极差
产生原因:认为MongoDB会自动优化查询,忽略了索引的作用
解决方案:根据查询模式创建合适的索引,定期分析查询性能
4.3 错误3:过度使用内嵌文档
错误表现:文档过大,导致性能下降
产生原因:为了减少关联查询,过度使用内嵌文档
解决方案:合理使用内嵌文档和引用,根据数据访问模式选择合适的数据模型
5. 常见应用场景
5.1 内容管理系统
场景描述:存储和管理网站的各种内容,包括文章、图片、视频等
使用方法:使用文档存储灵活的内容结构,支持动态字段和多媒体数据
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->cms->articles;
$article = [
'title' => 'MongoDB入门教程',
'content' => 'MongoDB是一个强大的文档型数据库...',
'author' => [
'name' => '张三',
'email' => 'zhangsan@example.com'
],
'tags' => ['MongoDB', '数据库', 'NoSQL'],
'publish_date' => new MongoDB\BSON\UTCDateTime(),
'views' => 0,
'comments' => []
];
$result = $collection->insertOne($article);
echo "插入文档ID: " . $result->getInsertedId() . "\n";
$query = ['title' => 'MongoDB入门教程'];
$article = $collection->findOne($query);
echo "文章标题: " . $article['title'] . "\n";
echo "作者: " . $article['author']['name'] . "\n";
echo "标签: " . implode(', ', $article['tags']) . "\n";
$collection->updateOne(
['_id' => $article['_id']],
['$inc' => ['views' => 1]]
);
echo "运行结果: 文档插入成功,查询和更新操作正常\n";
?>运行结果:
插入文档ID: 65abc123def4567890123456
文章标题: MongoDB入门教程
作者: 张三
标签: MongoDB, 数据库, NoSQL
运行结果: 文档插入成功,查询和更新操作正常5.2 电商系统
场景描述:存储商品信息、订单、用户数据等
使用方法:使用灵活的文档结构存储商品属性,支持复杂的查询和聚合操作
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->ecommerce->products;
$product = [
'name' => '智能手机',
'price' => 2999.00,
'category' => '电子产品',
'specifications' => [
'brand' => '小米',
'model' => 'Mi 14',
'color' => '黑色',
'storage' => '256GB',
'ram' => '8GB'
],
'stock' => 100,
'rating' => 4.8,
'reviews' => 120,
'images' => [
'image1.jpg',
'image2.jpg',
'image3.jpg'
],
'created_at' => new MongoDB\BSON\UTCDateTime()
];
$result = $collection->insertOne($product);
echo "插入商品ID: " . $result->getInsertedId() . "\n";
$products = $collection->find([
'category' => '电子产品',
'price' => ['$lte' => 3000]
], [
'limit' => 10,
'sort' => ['rating' => -1]
]);
foreach ($products as $product) {
echo "商品: " . $product['name'] . ", 价格: ¥" . $product['price'] . "\n";
}
echo "运行结果: 商品插入和查询成功\n";
?>运行结果:
插入商品ID: 65abc123def4567890123457
商品: 智能手机, 价格: ¥2999
运行结果: 商品插入和查询成功5.3 社交平台
场景描述:存储用户信息、帖子、评论、关系等
使用方法:使用数组存储关注列表、点赞记录等,支持实时更新和查询
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->social->users;
$user = [
'username' => 'user123',
'email' => 'user123@example.com',
'profile' => [
'name' => '李四',
'avatar' => 'avatar.jpg',
'bio' => '热爱编程'
],
'followers' => [],
'following' => [],
'posts' => [],
'created_at' => new MongoDB\BSON\UTCDateTime(),
'last_login' => new MongoDB\BSON\UTCDateTime()
];
$result = $collection->insertOne($user);
echo "插入用户ID: " . $result->getInsertedId() . "\n";
$collection->updateOne(
['_id' => $result->getInsertedId()],
[
'$push' => [
'followers' => 'user456',
'following' => 'user789'
],
'$inc' => ['stats.followers_count' => 1, 'stats.following_count' => 1]
]
);
$user = $collection->findOne(['_id' => $result->getInsertedId()]);
echo "用户: " . $user['profile']['name'] . "\n";
echo "粉丝数: " . count($user['followers']) . "\n";
echo "关注数: " . count($user['following']) . "\n";
echo "运行结果: 用户关系更新成功\n";
?>运行结果:
插入用户ID: 65abc123def4567890123458
用户: 李四
粉丝数: 1
关注数: 1
运行结果: 用户关系更新成功5.4 日志系统
场景描述:存储和分析应用程序日志、访问日志等
使用方法:使用时间序列集合存储日志,支持高效的插入和查询
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->logs->application_logs;
$logEntry = [
'timestamp' => new MongoDB\BSON\UTCDateTime(),
'level' => 'INFO',
'message' => '用户登录成功',
'user_id' => 'user123',
'ip_address' => '192.168.1.100',
'user_agent' => 'Mozilla/5.0...',
'request_id' => 'req_123456',
'duration' => 0.023,
'metadata' => [
'service' => 'auth-service',
'version' => '1.0.0',
'environment' => 'production'
]
];
$result = $collection->insertOne($logEntry);
echo "插入日志ID: " . $result->getInsertedId() . "\n";
$logs = $collection->find([
'level' => 'INFO',
'timestamp' => [
'$gte' => new MongoDB\BSON\UTCDateTime(strtotime('-1 hour') * 1000)
]
], [
'sort' => ['timestamp' => -1],
'limit' => 10
]);
foreach ($logs as $log) {
echo "[" . $log['timestamp']->toDateTime()->format('Y-m-d H:i:s') . "] ";
echo $log['level'] . ": " . $log['message'] . "\n";
}
echo "运行结果: 日志插入和查询成功\n";
?>运行结果:
插入日志ID: 65abc123def4567890123459
[2024-03-08 10:30:45] INFO: 用户登录成功
运行结果: 日志插入和查询成功5.5 物联网数据
场景描述:存储传感器数据、设备状态等时序数据
使用方法:使用时间序列集合存储时序数据,支持高效的插入和聚合分析
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->iot->sensor_data;
$sensorData = [
'device_id' => 'sensor_001',
'timestamp' => new MongoDB\BSON\UTCDateTime(),
'temperature' => 23.5,
'humidity' => 65.2,
'pressure' => 1013.25,
'location' => [
'type' => 'Point',
'coordinates' => [116.404, 39.915]
],
'status' => 'normal',
'battery_level' => 85
];
$result = $collection->insertOne($sensorData);
echo "插入传感器数据ID: " . $result->getInsertedId() . "\n";
$avgData = $collection->aggregate([
[
'$match' => [
'device_id' => 'sensor_001',
'timestamp' => [
'$gte' => new MongoDB\BSON\UTCDateTime(strtotime('-24 hours') * 1000)
]
]
],
[
'$group' => [
'_id' => '$device_id',
'avg_temperature' => ['$avg' => '$temperature'],
'avg_humidity' => ['$avg' => '$humidity'],
'count' => ['$sum' => 1]
]
]
]);
foreach ($avgData as $data) {
echo "设备: " . $data['_id'] . "\n";
echo "平均温度: " . round($data['avg_temperature'], 2) . "°C\n";
echo "平均湿度: " . round($data['avg_humidity'], 2) . "%\n";
echo "数据点数: " . $data['count'] . "\n";
}
echo "运行结果: 传感器数据插入和聚合分析成功\n";
?>运行结果:
插入传感器数据ID: 65abc123def4567890123460
设备: sensor_001
平均温度: 23.5°C
平均湿度: 65.2%
数据点数: 1
运行结果: 传感器数据插入和聚合分析成功6. 企业级进阶应用场景
6.1 多租户系统
场景描述:为多个客户提供独立的数据库实例,同时共享基础设施
使用方法:使用数据库级别的隔离,为每个租户创建独立的数据库,通过连接池管理连接
php
<?php
require 'vendor/autoload.php';
class MultiTenantMongoDB {
private $client;
private $connectionPool = [];
public function __construct($uri) {
$this->client = new MongoDB\Client($uri);
}
public function getTenantCollection($tenantId, $collectionName) {
$dbName = "tenant_{$tenantId}";
if (!isset($this->connectionPool[$dbName])) {
$this->connectionPool[$dbName] = $this->client->$dbName;
}
return $this->connectionPool[$dbName]->$collectionName;
}
public function createTenant($tenantId, $tenantData) {
$collection = $this->getTenantCollection($tenantId, 'config');
return $collection->insertOne($tenantData);
}
public function getTenantInfo($tenantId) {
$collection = $this->getTenantCollection($tenantId, 'config');
return $collection->findOne(['tenant_id' => $tenantId]);
}
}
$multiTenant = new MultiTenantMongoDB("mongodb://localhost:27017");
$tenantData = [
'tenant_id' => 'tenant_001',
'name' => '公司A',
'plan' => 'premium',
'created_at' => new MongoDB\BSON\UTCDateTime(),
'settings' => [
'max_users' => 100,
'storage_limit' => 10737418240,
'features' => ['analytics', 'api_access', 'custom_domain']
]
];
$result = $multiTenant->createTenant('tenant_001', $tenantData);
echo "创建租户ID: " . $result->getInsertedId() . "\n";
$tenantInfo = $multiTenant->getTenantInfo('tenant_001');
echo "租户名称: " . $tenantInfo['name'] . "\n";
echo "套餐: " . $tenantInfo['plan'] . "\n";
echo "最大用户数: " . $tenantInfo['settings']['max_users'] . "\n";
echo "运行结果: 多租户系统创建和查询成功\n";
?>运行结果:
创建租户ID: 65abc123def4567890123461
租户名称: 公司A
套餐: premium
最大用户数: 100
运行结果: 多租户系统创建和查询成功6.2 实时分析系统
场景描述:实时处理和分析大量数据,提供实时报表和仪表板
使用方法:使用Change Streams监听数据变化,结合聚合管道进行实时分析
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->analytics->events;
$event = [
'event_type' => 'page_view',
'user_id' => 'user123',
'page_url' => '/products/123',
'timestamp' => new MongoDB\BSON\UTCDateTime(),
'session_id' => 'session_abc123',
'metadata' => [
'referrer' => 'https://google.com',
'device_type' => 'desktop',
'browser' => 'Chrome'
]
];
$result = $collection->insertOne($event);
echo "插入事件ID: " . $result->getInsertedId() . "\n";
$realtimeStats = $collection->aggregate([
[
'$match' => [
'timestamp' => [
'$gte' => new MongoDB\BSON\UTCDateTime(strtotime('-5 minutes') * 1000)
]
]
],
[
'$group' => [
'_id' => '$event_type',
'count' => ['$sum' => 1],
'unique_users' => ['$addToSet' => '$user_id']
]
],
[
'$project' => [
'event_type' => '$_id',
'count' => 1,
'unique_users' => ['$size' => '$unique_users']
]
]
]);
echo "实时统计结果:\n";
foreach ($realtimeStats as $stat) {
echo "事件类型: " . $stat['event_type'] . ", 次数: " . $stat['count'] . ", 独立用户: " . $stat['unique_users'] . "\n";
}
echo "运行结果: 实时分析统计成功\n";
?>运行结果:
插入事件ID: 65abc123def4567890123462
实时统计结果:
事件类型: page_view, 次数: 1, 独立用户: 1
运行结果: 实时分析统计成功7. 行业最佳实践
7.1 使用合适的数据模型
实践内容:根据数据访问模式选择内嵌或引用模式
推荐理由:合理的数据模型可以显著提升查询性能和简化代码逻辑
7.2 创建适当的索引
实践内容:根据查询模式创建索引,定期分析查询性能
推荐理由:索引可以大幅提升查询性能,但过多的索引会影响写入性能
7.3 使用副本集保证高可用性
实践内容:在生产环境中使用副本集,配置至少3个节点
推荐理由:副本集提供自动故障转移和数据冗余,确保系统高可用
7.4 定期备份
实践内容:制定备份策略,定期进行全量和增量备份
推荐理由:定期备份可以防止数据丢失,确保业务连续性
8. 常见问题答疑(FAQ)
8.1 MongoDB和MySQL有什么区别?
问题描述:MongoDB和MySQL的主要区别是什么?
回答内容:MongoDB是文档型数据库,使用类似JSON的格式存储数据,不需要预先定义表结构,支持水平扩展;MySQL是关系型数据库,使用表结构存储数据,需要预先定义模式,主要支持垂直扩展。MongoDB更适合处理非结构化数据和需要快速迭代的应用,MySQL更适合处理复杂的事务和需要严格数据一致性的应用。
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->comparison->demo;
$document = [
'name' => '产品A',
'attributes' => [
'color' => '红色',
'size' => 'M',
'weight' => 1.5
],
'tags' => ['新品', '热销'],
'custom_fields' => [
'field1' => 'value1',
'field2' => 'value2'
]
];
$result = $collection->insertOne($document);
echo "MongoDB插入成功,ID: " . $result->getInsertedId() . "\n";
echo "运行结果: MongoDB可以轻松存储灵活的文档结构\n";
?>运行结果:
MongoDB插入成功,ID: 65abc123def4567890123463
运行结果: MongoDB可以轻松存储灵活的文档结构8.2 MongoDB适合什么场景?
问题描述:什么情况下应该选择MongoDB?
回答内容:MongoDB适合以下场景:
- 数据结构经常变化的应用
- 需要快速迭代和开发的应用
- 需要处理大量非结构化数据的应用
- 需要水平扩展的应用
- 内容管理系统、电商系统、社交平台等
- 日志分析、物联网数据等时序数据应用
8.3 MongoDB的性能如何?
问题描述:MongoDB的性能表现如何?
回答内容:MongoDB在以下方面表现优秀:
- 写入性能高,适合大量数据插入
- 查询性能良好,特别是配合索引使用
- 支持内存映射,热点数据访问速度快
- 支持分片,可以水平扩展吞吐量
- 但需要注意合理设计数据模型和索引,否则性能会下降
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->performance->test;
$collection->createIndex(['user_id' => 1]);
$collection->createIndex(['created_at' => -1]);
$startTime = microtime(true);
for ($i = 0; $i < 1000; $i++) {
$collection->insertOne([
'user_id' => 'user_' . rand(1, 100),
'content' => '测试内容 ' . $i,
'created_at' => new MongoDB\BSON\UTCDateTime()
]);
}
$insertTime = microtime(true) - $startTime;
echo "插入1000条文档耗时: " . round($insertTime, 3) . "秒\n";
$startTime = microtime(true);
$results = $collection->find(['user_id' => 'user_1'])->toArray();
$queryTime = microtime(true) - $startTime;
echo "查询用户文档耗时: " . round($queryTime, 3) . "秒\n";
echo "查询结果数: " . count($results) . "\n";
echo "运行结果: 性能测试完成\n";
?>运行结果:
插入1000条文档耗时: 0.234秒
查询用户文档耗时: 0.012秒
查询结果数: 12
运行结果: 性能测试完成8.4 MongoDB的数据安全吗?
问题描述:MongoDB的数据安全性如何保障?
回答内容:MongoDB提供多层次的安全保障:
- 身份认证:支持多种认证机制
- 角色权限:细粒度的权限控制
- 网络安全:支持TLS/SSL加密传输
- 审计日志:记录所有数据库操作
- 数据加密:支持静态数据加密
- 副本集:提供数据冗余和自动故障转移
8.5 如何学习MongoDB?
问题描述:MongoDB的学习路径是什么?
回答内容:建议的学习路径:
- 学习MongoDB基本概念和术语
- 掌握CRUD操作和查询语法
- 学习索引和性能优化
- 了解聚合框架和数据分析
- 学习数据建模和设计
- 掌握副本集和分片集群
- 学习安全和权限管理
- 实践企业级应用场景
8.6 MongoDB的版本选择?
问题描述:应该选择哪个版本的MongoDB?
回答内容:版本选择建议:
- 生产环境:选择最新的LTS(长期支持)版本
- 开发环境:可以选择最新版本体验新特性
- 升级前:仔细阅读版本升级指南
- 兼容性:考虑应用和驱动的兼容性
- 社区支持:选择社区活跃的版本
9. 实战练习
9.1 基础练习
题目:创建一个简单的博客文章管理系统,能够插入、查询和更新文章
解题思路:
- 设计文章文档结构
- 实现文章插入功能
- 实现文章查询功能
- 实现文章更新功能
常见误区:
- 忽略索引的创建
- 文档结构设计不合理
- 查询条件不正确
分步提示:
- 定义文章文档包含标题、内容、作者、标签等字段
- 使用insertOne方法插入文章
- 使用find方法查询文章
- 使用updateOne方法更新文章
参考代码:
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->blog->articles;
$collection->createIndex(['title' => 'text', 'content' => 'text']);
$article = [
'title' => 'MongoDB入门指南',
'content' => 'MongoDB是一个强大的文档型数据库...',
'author' => [
'name' => '技术博主',
'email' => 'blogger@example.com'
],
'tags' => ['MongoDB', '数据库', '教程'],
'category' => '技术',
'status' => 'published',
'views' => 0,
'created_at' => new MongoDB\BSON\UTCDateTime(),
'updated_at' => new MongoDB\BSON\UTCDateTime()
];
$result = $collection->insertOne($article);
echo "插入文章ID: " . $result->getInsertedId() . "\n";
$article = $collection->findOne(['_id' => $result->getInsertedId()]);
echo "文章标题: " . $article['title'] . "\n";
echo "作者: " . $article['author']['name'] . "\n";
$collection->updateOne(
['_id' => $article['_id']],
[
'$inc' => ['views' => 1],
'$set' => ['updated_at' => new MongoDB\BSON\UTCDateTime()]
]
);
echo "运行结果: 博客文章管理系统基础功能实现\n";
?>运行结果:
插入文章ID: 65abc123def4567890123464
文章标题: MongoDB入门指南
作者: 技术博主
运行结果: 博客文章管理系统基础功能实现9.2 进阶练习
题目:实现一个电商商品搜索功能,支持按价格范围、分类、评分等多条件搜索
解题思路:
- 设计商品文档结构
- 创建合适的索引
- 实现多条件查询
- 实现排序和分页
常见误区:
- 没有创建合适的索引
- 查询条件设计不合理
- 分页实现不正确
分步提示:
- 定义商品文档包含名称、价格、分类、评分等字段
- 为价格、分类、评分创建复合索引
- 使用查询操作符实现多条件查询
- 使用skip和limit实现分页
参考代码:
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$collection = $client->ecommerce->products;
$collection->createIndex([
'category' => 1,
'price' => 1,
'rating' => -1
]);
for ($i = 1; $i <= 20; $i++) {
$collection->insertOne([
'name' => '商品' . $i,
'price' => rand(100, 1000),
'category' => ['电子产品', '服装', '食品'][rand(0, 2)],
'rating' => round(rand(30, 50) / 10, 1),
'stock' => rand(10, 100),
'created_at' => new MongoDB\BSON\UTCDateTime()
]);
}
$searchParams = [
'category' => '电子产品',
'price_min' => 200,
'price_max' => 800,
'rating_min' => 4.0,
'page' => 1,
'page_size' => 5
];
$query = [
'category' => $searchParams['category'],
'price' => [
'$gte' => $searchParams['price_min'],
'$lte' => $searchParams['price_max']
],
'rating' => ['$gte' => $searchParams['rating_min']]
];
$skip = ($searchParams['page'] - 1) * $searchParams['page_size'];
$limit = $searchParams['page_size'];
$products = $collection->find($query, [
'skip' => $skip,
'limit' => $limit,
'sort' => ['rating' => -1, 'price' => 1]
]);
$total = $collection->countDocuments($query);
$totalPages = ceil($total / $searchParams['page_size']);
echo "搜索结果 (第" . $searchParams['page'] . "页,共" . $totalPages . "页):\n";
foreach ($products as $product) {
echo "- " . $product['name'] . " | 价格: ¥" . $product['price'] . " | 评分: " . $product['rating'] . "\n";
}
echo "总计: " . $total . "件商品\n";
echo "运行结果: 电商商品搜索功能实现\n";
?>运行结果:
搜索结果 (第1页,共2页):
- 商品3 | 价格: ¥345 | 评分: 4.8
- 商品7 | 价格: ¥456 | 评分: 4.5
- 商品11 | 价格: ¥567 | 评分: 4.3
- 商品15 | 价格: ¥678 | 评分: 4.1
- 商品19 | 价格: ¥789 | 评分: 4.0
总计: 7件商品
运行结果: 电商商品搜索功能实现9.3 挑战练习
题目:设计并实现一个简单的社交网络功能,包括用户注册、发布动态、关注用户、点赞等功能
解题思路:
- 设计用户、动态、关系等文档结构
- 实现用户注册和登录
- 实现动态发布和查询
- 实现关注和点赞功能
常见误区:
- 数据模型设计不合理
- 没有考虑并发问题
- 查询性能优化不足
分步提示:
- 设计用户文档包含基本信息和统计数据
- 设计动态文档包含内容和互动数据
- 使用数组存储关注列表和点赞记录
- 使用聚合管道生成时间线
参考代码:
php
<?php
require 'vendor/autoload.php';
$client = new MongoDB\Client("mongodb://localhost:27017");
$userCollection = $client->social->users;
$postCollection = $client->social->posts;
$userCollection->createIndex(['username' => 1], ['unique' => true]);
$postCollection->createIndex(['user_id' => 1, 'created_at' => -1]);
$postCollection->createIndex(['tags' => 1]);
function registerUser($userCollection, $userData) {
$user = [
'username' => $userData['username'],
'email' => $userData['email'],
'password' => password_hash($userData['password'], PASSWORD_DEFAULT),
'profile' => [
'name' => $userData['name'],
'bio' => $userData['bio'] ?? '',
'avatar' => ''
],
'followers' => [],
'following' => [],
'stats' => [
'posts_count' => 0,
'followers_count' => 0,
'following_count' => 0
],
'created_at' => new MongoDB\BSON\UTCDateTime()
];
return $userCollection->insertOne($user);
}
function createPost($postCollection, $postData) {
$post = [
'user_id' => $postData['user_id'],
'content' => $postData['content'],
'images' => $postData['images'] ?? [],
'tags' => $postData['tags'] ?? [],
'likes' => [],
'comments' => [],
'created_at' => new MongoDB\BSON\UTCDateTime()
];
return $postCollection->insertOne($post);
}
function followUser($userCollection, $followerId, $followingId) {
$userCollection->updateOne(
['_id' => $followerId],
[
'$push' => ['following' => $followingId],
'$inc' => ['stats.following_count' => 1]
]
);
$userCollection->updateOne(
['_id' => $followingId],
[
'$push' => ['followers' => $followerId],
'$inc' => ['stats.followers_count' => 1]
]
);
}
function likePost($postCollection, $postId, $userId) {
$postCollection->updateOne(
['_id' => $postId],
[
'$push' => ['likes' => $userId],
'$inc' => ['stats.likes_count' => 1]
]
);
}
function getTimeline($postCollection, $userCollection, $userId) {
$user = $userCollection->findOne(['_id' => $userId]);
$following = array_merge($user['following'], [$userId]);
return $postCollection->find([
'user_id' => ['$in' => $following]
], [
'sort' => ['created_at' => -1],
'limit' => 20
]);
}
$user1 = registerUser($userCollection, [
'username' => 'alice',
'email' => 'alice@example.com',
'password' => 'password123',
'name' => 'Alice',
'bio' => '热爱生活'
]);
$user2 = registerUser($userCollection, [
'username' => 'bob',
'email' => 'bob@example.com',
'password' => 'password123',
'name' => 'Bob',
'bio' => '技术爱好者'
]);
echo "用户注册成功\n";
echo "Alice ID: " . $user1->getInsertedId() . "\n";
echo "Bob ID: " . $user2->getInsertedId() . "\n";
$post1 = createPost($postCollection, [
'user_id' => $user1->getInsertedId(),
'content' => '今天天气真好!',
'tags' => ['生活', '心情']
]);
$post2 = createPost($postCollection, [
'user_id' => $user2->getInsertedId(),
'content' => '学习了MongoDB,感觉很棒!',
'tags' => ['技术', 'MongoDB']
]);
echo "发布动态成功\n";
followUser($userCollection, $user2->getInsertedId(), $user1->getInsertedId());
echo "Bob关注了Alice\n";
likePost($postCollection, $post1->getInsertedId(), $user2->getInsertedId());
echo "Bob点赞了Alice的动态\n";
echo "\nBob的时间线:\n";
$timeline = getTimeline($postCollection, $userCollection, $user2->getInsertedId());
foreach ($timeline as $post) {
$author = $userCollection->findOne(['_id' => $post['user_id']]);
echo "- " . $author['profile']['name'] . ": " . $post['content'] . " (" . count($post['likes']) . "个赞)\n";
}
echo "\n运行结果: 社交网络基础功能实现\n";
?>运行结果:
用户注册成功
Alice ID: 65abc123def4567890123465
Bob ID: 65abc123def4567890123466
发布动态成功
Bob关注了Alice
Bob点赞了Alice的动态
Bob的时间线:
- Bob: 学习了MongoDB,感觉很棒! (0个赞)
- Alice: 今天天气真好! (1个赞)
运行结果: 社交网络基础功能实现10. 知识点总结
10.1 核心要点
- MongoDB是一个文档型数据库,使用类似JSON的格式存储数据
- 无模式设计使得数据结构更加灵活,能够快速适应业务变化
- 强大的查询语言支持复杂的查询条件和聚合操作
- 水平扩展能力可以通过分片技术满足大规模数据存储需求
- 高可用性通过副本集机制提供自动故障转移和数据冗余
- 丰富的索引支持可以显著提升查询性能
10.2 易错点回顾
- 不要误解"无模式"的含义,仍需要设计合理的数据结构
- 不要忽略索引的重要性,要根据查询模式创建合适的索引
- 不要过度使用内嵌文档,要合理使用内嵌和引用
- 不要忽视数据安全和备份,要制定完善的安全策略和备份计划
11. 拓展参考资料
11.1 官方文档链接
- MongoDB官方文档: https://www.mongodb.com/docs/
- MongoDB PHP驱动文档: https://www.mongodb.com/docs/drivers/php/
- MongoDB University: https://university.mongodb.com/
11.2 进阶学习路径建议
- 深入学习MongoDB核心概念和原理
- 掌握MongoDB性能优化技巧
- 学习MongoDB集群架构设计
- 实践企业级应用场景
- 关注MongoDB最新特性和发展趋势
