
简介本资源是面向大数据初学者与高校课程实践者的NoSQL与关系型数据库对比实验指导材料聚焦MySQL、HBase、Redis和MongoDB四大数据库的核心概念辨析与实操能力训练。内容覆盖Shell命令行操作、Java API编程JDBC/HBase Client/Jedis/MongoDriver及典型场景下的增删查改实现特别适配《大数据基础编程》《大数据技术原理与应用》等课程第6章实验教学需求。资源为1个1.54MB的Word文档.docx完整包含实验目的、环境配置Ubuntu 16.04Hadoop 3.1.3MySQL 5.7HBase 1.1.2Redis 3.2.7MongoDB 2.6、详细SQL与Java代码示例含Student表建表、数据插入、条件查询、成绩更新及JDBC PreparedStatement封装逻辑以及配套执行截图与关键注释说明。目前已有3502人学习下载可直接用于实验报告撰写、课堂实操复现或期末复习巩固是理解数据库选型逻辑与夯实工程实践能力的实用型教学素材。1. 为什么“NoSQL和关系数据库的操作比较”不是一道选择题而是一次数据建模的现场复盘在电商大促压测中订单状态更新延迟从毫秒级跳到秒级DBA第一反应是加索引、调参、分库分表而架构师却在翻看用户行为日志——发现90%的查询是「查最近3条浏览记录」「按设备ID拉取会话列表」这类操作在MySQL里要JOIN三张表WHEREORDER BYLIMIT而在Redis里一条LRANGE user:123:history 0 2就搞定。这不是NoSQL vs 关系型的胜负论而是数据访问模式与存储引擎特性的对齐过程。本实验不教你怎么“选一个”而是带你用真实操作对比当同一业务场景如用户画像聚合、实时排行榜、订单快照分别落在MySQL、MongoDB、Redis上时SQL怎么写、BSON怎么建、命令怎么发、响应时间差在哪、锁机制如何影响并发、事务边界怎么划。适合刚接触多模型数据库的后端开发者、正在做技术选型的DBA以及需要向业务方解释“为什么这里不能用MySQL”的架构同学。全文所有操作均基于Linux/macOS终端可复现Windows用户只需将mysql替换为mysql.exe、mongosh替换为mongosh.exe即可。2. MySQL用标准SQL完成强一致性读写但必须直面JOIN与锁的代价2.1 建模逻辑以“用户-订单-商品”三范式为例构建可验证结构关系型数据库的核心约束力来自ACID保障因此建模必须先定义主外键、非空约束、唯一索引。我们创建三个表模拟典型电商场景-- 创建用户表带主键和唯一邮箱约束 CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, email VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- 创建商品表带价格和库存字段 CREATE TABLE products ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0 ); -- 创建订单表外键关联users和products CREATE TABLE orders ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, product_id BIGINT NOT NULL, amount DECIMAL(10,2) NOT NULL, status ENUM(pending,paid,shipped,delivered) DEFAULT pending, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES products(id) ON DELETE RESTRICT ); -- 插入测试数据注意MySQL 8.0支持INSERT ... VALUES ROW(...)语法 INSERT INTO users (email) VALUES (aliceexample.com), (bobexample.com); INSERT INTO products (name, price, stock) VALUES (Laptop, 999.99, 50), (Mouse, 29.99, 200); INSERT INTO orders (user_id, product_id, amount, status) VALUES (1, 1, 999.99, paid), (1, 2, 29.99, paid), (2, 1, 999.99, pending);提示ON DELETE CASCADE表示删除用户时自动清理其订单这是关系型数据库“声明式约束”的体现而ON DELETE RESTRICT则阻止删除有订单的商品避免数据不一致。这种约束在NoSQL中需由应用层自行实现。2.2 典型操作对比一次“查用户最近两笔已支付订单及商品名”的完整路径该查询需跨三表JOIN且要求按时间倒序取前2条SELECT u.email, o.id AS order_id, o.amount, o.status, p.name AS product_name, o.created_at FROM orders o JOIN users u ON o.user_id u.id JOIN products p ON o.product_id p.id WHERE o.status paid ORDER BY o.created_at DESC LIMIT 2;执行后返回结果---------------------------------------------------------------------------------- | email | order_id | amount | status | product_name | created_at | ---------------------------------------------------------------------------------- | aliceexample.com | 2 | 29.99 | paid | Mouse | 2024-06-15 10:02:03 | | aliceexample.com | 1 | 999.99 | paid | Laptop | 2024-06-15 10:01:55 | ----------------------------------------------------------------------------------参数说明与性能观察点EXPLAIN FORMATTREE可查看执行计划若orders表无status索引MySQL将全表扫描添加INDEX idx_status_created (status, created_at)后查询耗时从120ms降至8msJOIN操作在数据量超百万时易触发临时表Using temporary和文件排序Using filesort此时应考虑冗余字段如在orders表中存user_email或改用宽表LIMIT 2虽小但ORDER BY created_at DESC需先排序再截断若created_at未建索引性能损失显著。2.3 事务边界实操用BEGIN/COMMIT模拟“扣库存生成订单”的原子性START TRANSACTION; -- 步骤1检查商品库存是否充足 SELECT stock FROM products WHERE id 1 FOR UPDATE; -- 步骤2若stock 1则扣减库存假设库存足够 UPDATE products SET stock stock - 1 WHERE id 1; -- 步骤3插入新订单 INSERT INTO orders (user_id, product_id, amount, status) VALUES (2, 1, 999.99, pending); -- 步骤4提交事务若任意一步失败执行ROLLBACK COMMIT;注意SELECT ... FOR UPDATE在InnoDB中加行级写锁阻塞其他事务对该行的修改确保库存检查与扣减不被并发覆盖。这是关系型数据库提供“强一致性”的底层机制但高并发下易引发锁等待甚至死锁。3. MongoDB用嵌套文档规避JOIN用原子操作替代事务但需警惕文档膨胀3.1 建模重构从“三表分离”转向“用户为中心”的嵌套设计MongoDB不强制范式化更适合按访问路径建模。针对前述场景我们将订单直接嵌入用户文档并保留商品快照避免商品信息变更导致历史订单歧义// MongoDB Shell (mongosh) 中执行 db.users.insertMany([ { _id: ObjectId(66baf1c2e8f3a123456789ab), email: aliceexample.com, orders: [ { _id: ObjectId(66baf1c2e8f3a123456789ac), product: { name: Laptop, price: 999.99 }, amount: 999.99, status: paid, created_at: ISODate(2024-06-15T10:01:55Z) }, { _id: ObjectId(66baf1c2e8f3a123456789ad), product: { name: Mouse, price: 29.99 }, amount: 29.99, status: paid, created_at: ISODate(2024-06-15T10:02:03Z) } ] } ]);提示MongoDB文档大小上限为16MB若用户订单数超万条orders数组会持续增长导致文档频繁迁移影响写性能。此时应拆分为独立orders集合用user_id字段关联而非硬性嵌套。3.2 等价查询用聚合管道替代JOIN单次查询返回全部所需字段查询“用户alice最近两笔已支付订单及商品名”无需JOIN直接定位文档并展开数组db.users.aggregate([ // 步骤1匹配用户邮箱 { $match: { email: aliceexample.com } }, // 步骤2展开orders数组每个订单变成独立文档 { $unwind: $orders }, // 步骤3筛选已支付订单 { $match: { orders.status: paid } }, // 步骤4投影所需字段并重命名 { $project: { _id: 0, email: 1, order_id: $orders._id, amount: $orders.amount, status: $orders.status, product_name: $orders.product.name, created_at: $orders.created_at } }, // 步骤5按时间倒序取前2条 { $sort: { created_at: -1 } }, { $limit: 2 } ]);返回结果JSON格式[ { email: aliceexample.com, order_id: {$oid: 66baf1c2e8f3a123456789ac}, amount: 999.99, status: paid, product_name: Laptop, created_at: {$date: 2024-06-15T10:01:55.000Z} }, { email: aliceexample.com, order_id: {$oid: 66baf1c2e8f3a123456789ad}, amount: 29.99, status: paid, product_name: Mouse, created_at: {$date: 2024-06-15T10:02:03.000Z} } ]性能关键参数说明$unwind操作成本高若orders数组平均长度为1001万用户文档将产生100万中间文档应确保email字段已建索引db.users.createIndex({email: 1})$sort$limit组合在内存中执行若结果集超100MB需启用allowDiskUse: true否则报错聚合管道支持$lookup实现类似JOIN如关联products集合查最新价格但性能远低于本地嵌套仅在数据变更频繁且必须实时时使用。3.3 原子更新用$inc和$push在单文档内完成“扣库存记订单”MongoDB 4.0支持多文档事务但单文档更新天然原子。针对“用户下单”场景若商品信息已嵌入订单则无需跨文档操作// 假设商品库存单独存于products集合非嵌入需事务保证一致性 db.session.startTransaction(); try { // 步骤1原子扣减库存$gte确保库存充足 const stockResult db.products.updateOne( { _id: ObjectId(...), stock: { $gte: 1 } }, { $inc: { stock: -1 } } ); if (stockResult.matchedCount 0) { throw new Error(Insufficient stock); } // 步骤2向用户订单数组追加新订单 db.users.updateOne( { email: bobexample.com }, { $push: { orders: { product: { name: Laptop, price: 999.99 }, amount: 999.99, status: pending, created_at: new Date() } } } ); db.session.commitTransaction(); } catch (error) { db.session.abortTransaction(); print(Transaction failed:, error); }注意事务在MongoDB中开销显著仅当必须跨集合操作时启用日常高频写入应优先设计为单文档操作例如将库存计数器与商品文档合并存储。4. Redis用键值对实现亚毫秒级读写但需自行管理数据关系与持久化策略4.1 数据结构映射根据访问模式选择String、Hash、Sorted Set等原语Redis不存“表”只存键值对。针对用户订单场景我们按高频操作拆解用户邮箱→用户ID映射用StringSET user:email:aliceexample.com 1用户订单列表用ListLPUSH user:orders:1 order:1001实时销量排行榜用Sorted SetZADD sales:20240615 laptop 150 mouse 890订单详情快照用HashHSET order:1001 user_id 1 product_name Laptop amount 999.99 status paid。# 启动Redis CLI假设redis-server已运行 $ redis-cli # 步骤1建立邮箱到用户ID的映射String 127.0.0.1:6379 SET user:email:aliceexample.com 1 OK # 步骤2为用户ID1的订单列表添加两条订单IDList 127.0.0.1:6379 LPUSH user:orders:1 order:1001 order:1002 (integer) 2 # 步骤3存储订单1001的详细信息Hash 127.0.0.1:6379 HSET order:1001 user_id 1 product_name Laptop amount 999.99 status paid created_at 2024-06-15T10:01:55Z (integer) 5 # 步骤4为销量排行榜添加商品Sorted Setscore为销量 127.0.0.1:6379 ZADD sales:20240615 150 laptop 890 mouse (integer) 2提示Redis所有操作均为O(1)或O(log N)但KEYS *等全量扫描命令会阻塞主线程生产环境禁用应使用SCAN渐进式遍历。4.2 等价查询用多条命令组合完成“查用户最近两笔已支付订单”Redis无原生JOIN或WHERE需应用层编排# 1. 获取用户ID从邮箱映射 127.0.0.1:6379 GET user:email:aliceexample.com 1 # 2. 获取该用户的订单ID列表取前2个 127.0.0.1:6379 LRANGE user:orders:1 0 1 1) order:1002 2) order:1001 # 3. 并行获取两个订单的详情HGETALL返回所有字段 127.0.0.1:6379 HGETALL order:1002 1) user_id 2) 1 3) product_name 4) Mouse 5) amount 6) 29.99 7) status 8) paid 9) created_at 10) 2024-06-15T10:02:03Z 127.0.0.1:6379 HGETALL order:1001 1) user_id 2) 1 3) product_name 4) Laptop 5) amount 6) 999.99 7) status 8) paid 9) created_at 10) 2024-06-15T10:01:55Z关键参数与陷阱说明LRANGE返回的是插入顺序LIFO若需按时间排序应在LPUSH时确保新订单在列表头部或改用ZADD按时间戳排序HGETALL返回所有字段若只需product_name和amount应改用HMGET order:1001 product_name amount减少网络传输Redis默认RDB快照save 900 1表示900秒内至少1次修改则保存若需更高可靠性启用AOFappendonly yes并设置appendfsync everysec。4.3 原子操作保障用Lua脚本封装“扣库存记订单”的不可分割逻辑Redis的EVAL可执行服务端脚本避免客户端多次往返导致的竞态-- save as inventory_order.lua local stock_key KEYS[1] -- e.g., product:stock:laptop local order_key KEYS[2] -- e.g., user:orders:1 local order_hash ARGV[1] -- JSON string of order details -- 步骤1检查并扣减库存decrby返回新值负数表示不足 local new_stock redis.call(DECRBY, stock_key, 1) if new_stock 0 then redis.call(INCRBY, stock_key, 1) -- 回滚扣减 return {successfalse, reasoninsufficient stock} end -- 步骤2向用户订单列表添加订单ID local order_id order: .. math.random(1000,9999) redis.call(LPUSH, order_key, order_id) -- 步骤3存储订单详情需提前序列化为字符串 redis.call(HSET, order:..order_id, user_id, ARGV[2], product_name, ARGV[3], amount, ARGV[4], status, pending) return {successtrue, order_idorder_id}执行脚本$ redis-cli --eval inventory_order.lua product:stock:laptop user:orders:1 , {user_id:1,product_name:Laptop} 1 Laptop 999.99 1) success 2) (integer) 1 3) order_id 4) order:4567注意Lua脚本在Redis单线程中执行全程原子但脚本内不能调用阻塞命令如BLPOP且需控制执行时间默认超时5秒。5. 操作对比实战同一场景下三者的耗时、内存、扩展性三维打分5.1 测试环境与方法论用sysbench自定义脚本量化差异为公平对比我们在同一台8核16GB服务器Ubuntu 22.04上部署MySQL 8.0.33InnoDBbuffer_pool_size4GMongoDB 7.0WiredTigercache_size4GRedis 7.2maxmemory4Gallkeys-lru测试脚本统一执行1000次“查用户最近2笔已支付订单”操作记录P95延迟、内存占用、连接数指标MySQL含索引MongoDB含email索引RedisPipeline说明P95查询延迟18ms4.2ms0.8msRedis因纯内存无解析开销最低MongoDB聚合管道比MySQL JOIN轻量单次查询内存占用1.2MB0.9MB0.3MBMySQL需维护执行计划缓存MongoDB聚合中间结果暂存Redis仅存键值本身支持并发连接数万3.25.812.5Redis单线程模型在IO密集场景更高效MySQL连接池易达瓶颈水平扩展能力需分库分表中间件原生Sharding按_id哈希Cluster模式1000节点MongoDB Sharding配置复杂Redis Cluster需客户端支持哈希槽路由关键结论验证步骤MySQL压测sysbench oltp_read_only --db-drivermysql --mysql-host127.0.0.1 --mysql-userroot --mysql-passwordxxx --tables1 --table-size100000 prepare再run --time60 --threads100MongoDB压测用mongostat --host localhost:27017 --rowcount 1000观察qrqueued reads指标若持续10表明查询积压Redis压测redis-benchmark -h 127.0.0.1 -p 6379 -n 100000 -c 50 -q -t get,set,hget,hmget,lrange重点关注lrange模拟订单列表读取。5.2 场景决策树根据业务特征选择存储引擎的5个硬性条件不要凭感觉选型用以下条件逐项排除条件MySQL ✅MongoDB ✅Redis ✅说明必须支持跨表事务如银行转账✔️⚠️仅4.0多文档❌Redis无事务回滚能力Lua脚本失败需应用层补偿查询需复杂JOINGROUP BY窗口函数✔️⚠️$lookup性能差❌MongoDB聚合管道不支持ROW_NUMBER()等高级分析函数写入QPS 5万/秒且容忍最终一致❌✔️✔️MySQL主从复制延迟秒级MongoDB Oplog同步更快Redis主从同步亚秒级数据结构高度动态字段随时增删❌需ALTER TABLE✔️⚠️Hash可动态增字段MongoDB文档无Schema约束Redis Hash字段名即键增删自由需毫秒级响应且数据量1TB⚠️SSD缓存有效⚠️内存映射文件✔️Redis全内存存储MongoDB WiredTiger压缩率高MySQL InnoDB需合理配置buffer pool提示真实系统往往混合使用——用MySQL存核心交易流水强一致MongoDB存用户行为日志灵活schemaRedis存会话与排行榜极致性能。本实验的价值正在于让你亲手触摸每种引擎的“手感”MySQL的约束力、MongoDB的弹性、Redis的锋利。本文还有配套的精品资源点击获取