Skip to content

数据库基础:从数据模型到选型 ​

先看数据关系与访问方式,再选择存储产品。这里比较职责和入门路径,不为每个产品重复一套“安装、建表、增删改查”。

先明确要解决的问题 ​

  1. 数据如何关联:表之间的约束、文档聚合、键值访问,还是图遍历?
  2. 主要工作负载:事务写入、按键读取、范围扫描、统计分析或时间窗口聚合?
  3. 能接受什么故障:短暂旧值、丢失窗口、恢复时间与跨节点一致性分别是多少?
  4. 如何运维:数据量、热点、扩展方式、备份恢复及团队已有经验?

“关系型”“内存”“分布式”不是互斥分类:前者是数据模型,内存描述存放与访问方式,分布式描述部署与协调方式。Redis 可同时属于键值存储与内存数据系统。

按数据模型区分职责 ​

模型常见用途需要先确认的边界
关系模型有关联与事务约束的业务记录表设计、事务、索引和查询计划
文档模型以聚合文档为单位的读写文档边界、嵌套增长、跨文档更新
键值及集合缓存、会话、计数、排序集合过期、淘汰、持久化、热点
宽列模型围绕分区键的大规模已知查询按查询设计表、分区大小与一致性设置
图模型多跳关系、路径与邻居查询遍历深度、边规模和查询代价
时间序列按时间写入、窗口聚合与保留标签基数、采样、压缩与保留策略

NoSQL 是一组不同模型的统称,不意味着所有产品都没有事务,也不意味着表模型天然不能扩展。不能只凭一个分类判断系统能力。

关系型数据库:MySQL、PostgreSQL 与 Oracle ​

共通学习顺序 ​

建模与约束 → 基本查询 → 连接与聚合 → 事务与隔离 → 索引与执行计划 → 备份恢复。SQL 基础可以迁移,但类型、分页、函数、账号与存储程序语法有产品差异。

产品先掌握的入口不应直接照搬的内容
MySQLmysql 客户端、表结构、事务引擎与 EXPLAINMySQL 的 LIMIT、客户端 DELIMITER
PostgreSQLpsql、schema、事务与查询计划类型、函数/过程及服务端编程语言
Oracle DatabaseSQL*Plus/SQL Developer、schema、SQL 与 PL/SQLVARCHAR2、序列/身份列、分页与过程调用

Oracle 不使用 MySQL 的 LIMIT。SQL 中给参数取名时应与列区分,避免出现 WHERE employee_id = employee_id 这类没有按传入 ID 筛选的条件。

连接后先验证目标 ​

sh
mysql -h 127.0.0.1 -P 3306 -u study_user -p
psql -h 127.0.0.1 -p 5432 -d study_db -U study_user
sqlplus study_user@study_service

密码通过交互或既定凭据管理流程提供。进入后确认实例、数据库/schema、账号及版本,再执行练习;客户端安装不等于服务端已初始化。

文档与宽列:MongoDB、Cassandra ​

MongoDB:按文档聚合业务数据 ​

集合保存 BSON 文档。先决定哪些字段随对象一起读取和更新,再决定嵌入还是引用;“结构灵活”不等于无需约束、索引或迁移设计。

在已连接的 shell 中,use study_db 只是切换数据库上下文,数据库通常在真正写入数据后建立。不要把旧 mongo shell 的安装与登录命令当成所有版本通用入口。

javascript
use study_db
db.users.insertOne({name: 'Lin', age: 25})
db.users.find({name: 'Lin'})
db.users.updateOne({name: 'Lin'}, {$set: {age: 26}})
db.users.createIndex({name: 1})

使用固定名字筛选仅用于小型练习,业务更新需使用能唯一标识目标的条件。更多内容见 MongoDB 专题。

Cassandra:先定查询,再定分区键 ​

表结构围绕读写路径设计。分区键决定数据放置及常见访问边界,聚簇列决定分区内排序;不能把关系数据库中任意列过滤和更新的习惯直接套进 CQL。

原笔记 id uuid PRIMARY KEY 的表,用 WHERE name = ... 更新或删除并不满足主键条件。应记录写入的 ID,再按该 ID 操作,而不是靠补一个索引就假定所有修改都成立。

需要进一步动手时,先在隔离环境确定拓扑和复制策略;单机示例 replication_factor: 1 不能直接当作生产高可用部署。

内存缓存:Redis 与 Memcached ​

Redis:选择数据结构并定义可靠性 ​

String、Hash、List、Set、Sorted Set 分别服务不同访问方式;还要确定 TTL、内存淘汰、持久化和故障转移。主从、Sentinel 与 Cluster 解决的问题不同,不能统称“开集群就不丢数据”。

text
SET study:token sample EX 60
GET study:token
HSET study:user:1 name Lin age 25
HGET study:user:1 name
ZADD study:rank 100 player-1 90 player-2
ZRANGE study:rank 0 -1 WITHSCORES

完整类型、复制和缓存异常统一见 Redis 知识复盘。

Memcached:简单对象缓存与失效 ​

先定义 key、序列化和过期策略。缓存可能失效或被淘汰,应用必须能正确回源;不要把它作为唯一数据来源。

下面是已经连接到隔离实例后发送的文本协议,3 表示值 abc 的字节数,不是字符数量参数占位符:

text
set study:example 0 60 3
abc
get study:example
touch study:example 120
delete study:example
stats

客户端通常负责连接池、协议编码和错误处理,真实业务不应依赖手工 telnet 输入。

图与时间序列:由查询形状决定 ​

社交邻居、关系路径等多跳查询可评估图数据库,例如 Neo4j、ArangoDB;监控、传感器等按时间追加和聚合的数据可评估时间序列系统,例如 InfluxDB、OpenTSDB。产品名称只作为原笔记中的模型例子,不代表一份当前产品排名。

先用代表性数据和查询做验证:路径查询是否可限制深度?时间序列标签是否导致高基数?保留与降采样策略能否满足业务查询?不要只比较插入速度。

用一个业务场景验收选型 ​

以订单服务为例:订单、支付与约束由权威业务存储维护,缓存只保存可重建的读模型,监控指标进入适合的时间序列系统。是否拆出这些组件,要由实际需求与运行成本决定。

比较方案时记录同一组条件:数据规模、查询分布、延迟分位数、并发写入、故障与恢复过程、容量成本。评估结果比“某数据库功能更强大”更有参考价值。

继续阅读