数据仓储处介绍_小白版

📅 2026/7/9 4:43:57
数据仓储处介绍_小白版
数据仓储层详解——小白也能看懂 文档位置D:\AI0409\Day22\数据仓储处介绍.md这篇文档讲的是程序中数据存取这一层怎么设计——不靠现成的 ORM 框架自己封装。开篇问题为什么不用 ORM 框架ORM 框架像传译员——你用 Python 对象操作它帮你翻译成 SQL。但是企业不一定会全部用 ORM原因对比ORM 框架自己封装学习成本低开箱即用高自己写一套灵活性复杂查询难优化完全可控 ✅性能优化黑盒难干预可精细调优 ✅团队掌控度依赖第三方自己写的最懂 ✅本项目就是自己封装的在Base/Repository/里。核心概念 1事务Transaction 事务是什么事务是数据库层面处理某个业务的最小单元。翻译成人话一个业务要拆成好几步数据库操作要么全部成功要么全部失败不能只成功一半。 经典案例退货业务退货不是一步操作而是三步连环用户点击退货 │ ├── 第1步把订单状态改成退货中 │ UPDATE orders SET status退货中 WHERE order_id1001 │ ├── 第2步把库存加回来货退回来了 │ UPDATE inventory SET stockstock1 WHERE product_id2001 │ └── 第3步退款钱退给用户 UPDATE payment SET refund_amount100, status已退款 WHERE order_id1001问题来了——如果第2步成功了第3步失败了✅ 第1步成功订单变成退货中 ✅ 第2步成功库存加回来了 ❌ 第3步失败钱没退给用户 → 结果货退回来了钱没扣库存也对不上 —— 数据完全乱了 事务怎么解决用事务包起来保证要么三句话全是真话要么全是假话# ❌ 没事务——每一步独立执行orderDao.update_order()# ✅ 改了 → 但不知道后面会不会失败kucunDao.update_kucun()# ✅ 改了 →payDao.update_pay()# ❌ 炸了 → 前两步白改了数据脏了# ✅ 有事务——三个操作同生共死# 开启一个事务orderDao.update_order()# 先记在草稿上kucunDao.update_kucun()# 也记在草稿上payDao.update_pay()# 也记在草稿上# 结束事务commit-提交# 只有全部成功了才真正写进去# 只要其中一个失败了全部回滚——就当作什么都没发生过 生活类比超市退货退货不是你直接拿回货架就走而是有固定流程收银员在系统里标记此单已退 ✅拿回商品放回货架 ✅从收银柜里拿钱退给你 ✅经理说这三件事必须要么全部做完要么全部不做——如果系统标记了但货没放回 ✅❌→ 库存对不上如果货放回了但没退钱 ✅❌→ 顾客不答应只有三件事全部完成才算正确处理 ✅✅✅下面是文档中的第1张图用文字描述出来┌──────────────────────────────────────┐ │ Service 层 │ ← 业务逻辑层 │ ┌──────────┐ ┌──────────────────┐│ │ │ │ │ 退货 service ││ ← 负责处理退货这个业务 │ │订单service│ │ ││ │ │ │ │ 开启一个事务 ││ ← 关键 │ │ │ │ orderDao. ││ 告知数据库我要开始 │ │ │ │ update_order() ││ 执行第1步改订单状态 │ │ │ │ kucunDao. ││ 执行第2步更新库存 │ │ │ │ update_kucun() ││ │ │ │ │ payDao. ││ 执行第3步退款 │ │ │ │ update_pay() ││ │ │ │ │ 结束事务 ││ ← 要么全成功提交 │ └──────────┘ └──────────────────┘│ 要么全失败回滚 └──────────────────┬───────────────────┘ │ 调用 ▼ ┌──────────────────────────────────────┐ │ Dao 层 │ ← 数据访问层 │ │ │ ┌──────────┐ ┌──────────┐ ┌──────┐│ │ │ 订单Dao │ │ 库存Dao │ │支付Dao││ │ │ │ │ │ │ ││ │ │update_ │ │update_ │ │update││ │ │order() │ │kucun() │ │_pay()││ │ │ │ │ │ │ ││ │ │add_order()│ │add_kucun()│ │add_ ││ │ │ │ │ │ │pay() ││ │ │delete_ │ │delete_ │ │delete││ │ │order() │ │kucun() │ │_pay()││ │ └──────────┘ └──────────┘ └──────┘│ │ │ │ 每个 Dao 只负责一个数据表的操作 │ │ 订单Dao → 只操作订单表 │ │ 库存Dao → 只操作库存表 │ │ 支付Dao → 只操作支付表 │ └───────────────────────────────────────┘这张图的要点总结层角色做了什么Service 层 业务调度员说我要退货→开事务→调3个Dao→结束事务Dao 层 具体执行者每个Dao只管自己的表不关心整体业务分层的好处退货Service 不关心update_order的SQL怎么写 ↓ 它只负责调用正确的Dao 订单Dao 不关心退货的整体流程怎么走 ↓ 它只负责执行完交给我的SQL就像餐厅服务员不负责炒菜厨师不管谁在哪个桌点菜——各司其职。核心概念 2分库分表 分库分表是什么当数据量太大上亿条数据、访问太频繁时一个数据库扛不住了——就像春运只有1个售票窗口排队排到天荒地老。解决方案把数据拆开方案做法类比分库不同业务放到不同数据库京沪高铁去A楼买长途汽车去B楼买分表同一张表拆成多张子表用户表拆成 user_0 ~ user_9 共10张分库分表同时分库分表多个楼每楼多个窗口️ 第二张图片内容转文字左侧分库分表╔═══════════════════════════╗ ║ 分库分表 ║ ╚═══════════════════════════╝ ┌──────────────────┐ ┌──────────────────┐ │ Order(BaseModel) │ │ User(BaseModel) │ │ │ │ │ │ Order.set_ │ │ User.set_conn( │ │ connection( │ │ 192.168.12.91) │ │ 192.168.12.90) │ │ 增删改查操作 │ │ │ │ │ └────────┬─────────┘ └────────┬──────────┘ │ │ │ 路由到 │ 路由到 ▼ ▼ ┌─────────────────────────────────────────────┐ │ 数据库集群 │ │ │ │ ┌──────────────┐ ┌──────────────────────┐ │ │ │ 订单相关数据库│ │ 用户服务的数据库 │ │ │ │ 192.168.12.90│ │ 192.168.12.91 │ │ │ └──────────────┘ └──────────────────────┘ │ └─────────────────────────────────────────────┘这段代码在做什么Order.set_connection(192.168.12.90)# 说所有订单相关操作去找 IP 为 192.168.12.90 的那个数据库User.set_conn(192.168.12.91)# 说所有用户相关操作去找 IP 为 192.168.12.91 的那个数据库分库的类比图书馆把书按类别分到不同楼层 1楼 文学类对应订单数据库 192.168.12.90 2楼 科学类对应用户数据库 192.168.12.91 你想借小说 → 直接上1楼不用去2楼 查询订单 → 直接连192.168.12.90不用去91结果两个数据库互不干扰各自分担一部分流量。压力减半核心概念 3读写分离 读写分离是什么读操作SELECT和写操作INSERT/UPDATE/DELETE分开走不同数据库——就像高铁站出发层和到达层分层互不干扰。为什么需要读写分离一个订单库如果所有操作都扛 写操作插入新订单、更新状态占总操作~20% 读操作查订单列表、查某个订单详情占总操作~80% 80% 的操作都是 SELECT如果读写不分流 → 一个复杂的 SELECT 查询跑很久 → 堵住其他操作的通道 → 用户在付款写操作 → 也得等到 SELECT 跑完️ 第二张图片内容转文字右侧读写分离╔══════════════════════════╗ ║ 实现读写分离 ║ ╚══════════════════════════╝ Order (Model) ┌──────────────────────────────────┐ │ add () │ │ update () │ │ delete() │ │ select()... │ └───┬──────────────────────┬───────┘ │ │ ▼ ▼ ┌──────────────┐ ┌──────────────┐ │ 写操作 │ │ 读操作 │ │ │ │ │ │ order new │ │ order.set_ │ │ Order() │ │ connection │ │ order.set_ │ │ (192.168. │ │ connection │ │ 12.13) │ │ (192.168. │ │ order. │ │ 12.12) │ │ select() │ │ order.add() │ │ │ │ order. │ │ │ │ update() │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ ▼ ▼ ┌──────────────────┐ ┌─────────────────┐ │ 192.168.12.12 │ │ 192.168.12.13 │ │ Order 写库 │ │ Order 读库 │ │ │ │ │ │ add_order │ │ select_by_id │ │ update │ │ select_by_user │ │ delete │ │ select_by_prod │ └────────┬─────────┘ └────────┬────────┘ │ │ └─────── binlog ────┘ 主 → 从 同步 读写分离的三步拆解第1步建立主从关系有两个数据库实例角色IP 地址负责什么主库写库192.168.12.12INSERT / UPDATE / DELETE从库读库192.168.12.13SELECT第2步binlog 同步主库做完一个写入操作比如 UPDATE orders SET status已付款 WHERE id1001 主库会自动把这条操作记到 binlog二进制日志里 → binlog 就像流水账本 从库不断读取主库的 binlog → 哦主库改了 id1001 的状态 → 从库也跟着改 → 保持数据一致类比主库是正本笔记从库是复印本——你在正本上写字写操作在主库复印机binlog自动把新内容复制到复印本从库别人来看的时候看复印本就行第3步代码层分流# 写操作 → 走主库 192.168.12.12orderOrder()order.set_connection(192.168.12.12)# 连主库order.add()# 插入 → 在主库执行# 读操作 → 走从库 192.168.12.13order.set_connection(192.168.12.13)# 连从库order_listorder.select()# 查询 → 在从库执行# 关键写走主库、读走从库互不干扰⚖️ 不做读写分离 vs 做了场景不分一台机器扛分了主写从读用户下订单和SELECT们抢通道独占主库 ✅用户查历史订单和其他查询挤一起从库专用 ✅生成报表大量SELECT拖慢所有操作从库慢慢查不影响主库 ✅主库挂了全挂了 ❌从库还能查数据 ✅分库分表 读写分离合在一起看从第二张图可以看到整个架构是分库 读写分离结合┌──────┐ ┌──────┐ │ 订单 │ │ 用户 │ │ 模型 │ │ 模型 │ └──┬───┘ └──┬───┘ │ │ ┌─────────────┼───────────┼──────────────┐ │ │ 分库 │ │ │ ▼ ▼ │ │ ┌───────────────────────────────┐ │ │ │ 数据库集群 │ │ │ │ │ │ │ │ 订单库 (192.168.12.90) │ │ │ │ ├── 主库 (12.12) ← 写入 │ │ │ │ ├── 从库 (12.13) ← 读取 │ │ │ │ └── binlog 同步 → │ │ │ │ │ │ │ │ 用户库 (192.168.12.91) │ │ │ │ ├── 主库 (?) ← 写入 │ │ │ │ ├── 从库 (?) ← 读取 │ │ │ │ └── binlog 同步 → │ │ │ └───────────────────────────────┘ │ └──────────────────────────────────────┘ 第一层分库 订单 ↔ 192.168.12.90 用户 ↔ 192.168.12.91 第二层读写分离 写操作 ↔ 12.12 读操作 ↔ 12.13你可能会问Q1Service 和 Dao 有什么区别层类比职责Service‍ 餐厅经理管流程、管事务、管协调多个 DaoDao‍ 厨师只管一个表CRUD增删改查Dao 层长什么样# 每个 Dao 只负责一个数据表classOrderDao:defupdate_order(self,order_id,status):只负责改订单状态 sqlUPDATE orders SET status%s WHERE order_id%sdb.execute(sql,(status,order_id))defadd_order(self,order):只负责新增订单 sqlINSERT INTO orders (...) VALUES (...)db.execute(sql,...)defdelete_order(self,order_id):只负责删除订单 sqlDELETE FROM orders WHERE order_id%sdb.execute(sql,(order_id,))Q2自己封装 vs ORM 的区别# ORM 方式如 SQLAlchemyuserUser(name张三,age25)db.session.add(user)db.session.commit()# → ORM 自动生成INSERT INTO users (name, age) VALUES (张三, 25)# 自己封装方式daoUserDao()dao.add_user(name张三,age25)# → 自己写# def add_user(self, name, age):# db.execute(INSERT INTO users (name, age) VALUES (%s, %s), (name, age))对比ORM自己封装代码量少 ✅多灵活控制难优化每一句SQL都可控 ✅学习成本学框架语法相当于学 SQL ✅性能优化黑盒可针对慢查询精细优化 ✅Q3分库后跨库查询怎么办答案跨库查询在应用层做代码层面拼接。# 两个数据库各自查代码拼接orderOrderDao().get_order(order_id)# 在订单库查userUserDao().get_user(order.user_id)# 在用户库查result{order_id:order.id,status:order.status,user_name:user.name# 手动拼起来}文档中的两张图总结图1退货事务处理Service层退货Service ← 业务调度者 │ └── 开启事务 │ ├── orderDao.update_order() ← 订单表操作 │ ├── kucunDao.update_kucun() ← 库存表操作 │ └── payDao.update_pay() ← 支付表操作 │ └── 结束事务全部成功 → 提交 / 任一失败 → 回滚 │ Dao层 订单Dao 库存Dao 支付Dao ← 数据执行者 每个Dao只管自己的表核心思想事务保证数据一致性——要么三部曲全成功要么全都不发生。图2分库分表 读写分离分库 ┌───────────────────┐ │ 订单 → 库A (90) │ │ 用户 → 库B (91) │ ← 把不同业务的数据库拆分 └───────────────────┘ 读写分离 ┌───────────────────┐ │ 写 → 主库 (12.12) │ │ 读 → 从库 (12.13) │ ← 同一个库读写走不同机器 │ binlog 自动同步 │ └───────────────────┘核心思想分库分表 压力分散到多个数据库每个库分担一部分读写分离 压力分散到同一个库的多台机器写不堵读binlog 主库和从库之间的自动抄写员一句话总结数据仓储层的设计本质就是三件事事务管理️保证多个操作同生共死不出脏数据分库分表️数据太多拆到不同数据库里去读写分离读和写分开走不互相拖累而这一切都封装在Base/Repository/这一层里——你写的业务代码只需要告诉它存什么、取什么数据库怎么分、事务怎么管、读写往哪走底层已经帮你做好了。