数据库是用于存储、检索和管理数据的系统。它们可以分为几个不同的类别,包括关系型数据库、非关系型数据库(NoSQL)、列式存储数据库和时间序列数据库等。
数据库类型
关系型数据库 (RDBMS)
- 示例: MySQL, PostgreSQL, Oracle Database, Microsoft SQL Server
- 特点: 使用结构化查询语言(SQL)进行数据操作,基于行和列的表格结构,并支持ACID事务(原子性、一致性、隔离性、持久性)。
- 适用场景: 适合需要强大事务支持和复杂查询的应用,如金融服务、客户关系管理(CRM)和电子商务。
- 看图理解: 元素间有直接关系
非关系型数据库 (NoSQL)
- 示例: MongoDB, Cassandra, Redis, Couchbase
- 特点: 通常不使用传统SQL语法,可扩展性好,适合处理大量分布式数据。根据数据模型的不同,NoSQL数据库又可以分为文档型、键值对型、列族型和图形型等。
- 适用场景: 适合大数据和实时Web应用,例如社交网络、大规模在线游戏和实时分析。
- 看图理解: 元素关系很复杂,关系链路不定
列式存储数据库
- 示例: Apache HBase, Google Bigtable, Cassandra(同时也被归类为NoSQL)
- 特点: 与传统的行式数据库不同,列式数据库是以列为单位来存储数据,这样能够更高效地执行某些查询,特别是涉及到大量数据的聚合运算。
- 适用场景: 适用于数据仓库、大数据分析和报告,以及任何需要高速查询和数据压缩的场合。
时间序列数据库 (TSDB)
- 示例: InfluxDB, Prometheus, TimescaleDB
- 特点: 专门为时间标记数据设计,优化了时间序列数据的存储和检索。时间序列数据库通常支持高效的数据压缩和大规模数据的写入。
- 适用场景: 适合监控、物联网(IoT)、实时分析和金融市场数据。
图形数据库
- 示例: Neo4j, Amazon Neptune, OrientDB
- 特点: 使用图结构存储数据,以节点、边和属性的形式表示和存储数据之间的关系。
- 适用场景: 适合需要分析和处理复杂关系的领域,如社交网络分析、推荐系统和欺诈检测。
内存数据库
- 示例: Redis, Memcached
- 特点: 数据全部或主要存储在RAM中,因此可以提供极快的读写速度。
- 适用场景: 适用于需要快速数据访问的应用,例如缓存、会话存储和实时分析。
文档存储数据库
- 示例: MongoDB, CouchDB
- 特点: 以文档形式存储数据,每个文档都是一个自描述的结构,可以包含嵌套的元素。
- 适用场景: 适合内容管理系统、博客平台和配置管理等场景。
核心概念
数据模型
- 关系模型: 数据组织成行和列的形式。
- 键值存储: 通过键访问数据,类似于字典。
- 文档存储: 每个文档存储自描述信息,通常是JSON或XML格式。
- 列族存储: 以列簇方式存储,方便进行列级别的操作。
- 图形数据模型: 数据表示为节点和边。
索引
-
作用: 加速查询操作
-
类型:
-
B-Tree索引: B-Tree(平衡树)索引是最常见的索引类型,几乎所有的数据库系统都支持。它们能够快速地处理等值查询、范围查询和排序操作。B-Tree索引以层次化的方式存储数据,保持平衡状态,以确保每个叶节点到根节点的距离相等。
-
B+Tree索引 B+Tree索引是B-Tree索引的变种,其中所有的值都存在于叶子节点上,并且叶子节点之间通过指针连接成链表,这使得范围查询更加高效。
-
哈希索引 哈希索引基于哈希表实现,非常适合于快速查找等值的查询。它们通过计算键值的哈希来直接定位数据,但不支持范围查询。
-
复合索引(多列索引) 复合索引是在两个或多个列上创建的索引,适合于那些需要同时在多个列上进行查询的情况。在复合索引中,列的顺序很重要,因为索引是基于第一个列创建的,然后是第二个列,依此类推。
-
唯一索引 唯一索引不允许表内有重复的值。如果试图插入一个已经存在的值,则数据库会拒绝该操作。主键索引是一种特殊的唯一索引。
-
全文索引 全文索引用于搜索文本数据中的关键词。它们通常用于实现对大量文本数据的高效搜索,如文章、书籍和网页内容。
-
空间索引(地理空间索引) 空间索引用于空间数据,如地图坐标或者地理围栏。空间索引可以高效处理与地理位置相关的查询,例如查找某个区域内的所有点或确定两个对象之间的距离。
-
部分索引(过滤索引) 部分索引只包含符合某个条件的记录。这种类型的索引较小,而且由于只索引了表中的一部分数据,所以维护成本较低。
-
聚簇索引 聚簇索引决定了表中数据的物理排序。在一个表中,只能有一个聚簇索引,因为你不能将同一份数据以两种不同的方式排序。在某些数据库中,默认的聚簇索引是主键索引。
-
表示性索引(覆盖索引) 表示性索引包含了查询中需要的所有数据。当一个查询可以仅通过索引来解决,而不需要回表查询原始数据时,这种索引就被称为表示性索引。
-
-
工作原理: 当不使用索引时,数据库系统执行查询操作(尤其是针对大型数据集)时,会进行全表扫描以找到匹配的行。这种扫描过程非常耗时。而有了索引,数据库系统可以使用更高效的搜索算法(比如二分搜索),这大幅减少了必须检查的数据量。
-
索引的限制:
- 存储开销:索引需要占用额外的磁盘空间。对于大型数据库来说,索引可能会消耗相当数量的存储资源。
- 维护成本:每当数据被插入、更新或删除时,所有相关索引都需要更新。这意味着写操作可能会变慢,因为它们涉及额外的索引维护工作。
- 设计复杂性:合理地选择何时以及在哪些列上创建索引需要仔细考虑。错误的索引策略可能导致性能下降而非提升。
- 查询限制:并非所有查询都能从索引中受益。例如,如果查询返回表中大部分数据,那么全表扫描可能比使用索引更有效率。
- 内存消耗:索引数据结构通常存储在内存中(至少部分是这样),以便快速访问,这增加了内存的消耗。
- 复合索引限制:复合索引是基于多个列创建的索引。如果查询条件不使用索引的前导列(即复合索引中定义的第一个列),那么该索引可能不会被使用。
- 数据分布:对于数据分布均匀的列(低选择性),索引可能不太有效,因为即使使用索引也会返回大量的行。
不同的数据库可能支持其他类型的专门索引,或者有针对特定工作负载优化的索引类型。正确地使用索引可以显著提升查询性能,但过多或不当的使用也会增加维护成本并降低写操作的性能。因此,选择和设计索引时需要仔细考虑实际的应用场景。
事务
-
作用: 包含一组操作,要么全部成功,要么全部失败。
-
属性:
- 原子性(Atomicity) :事务中的所有操作都是不可分割的单元。一个事务要么完全执行,要么完全不执行。如果事务中的任何操作失败,整个事务将会回滚到开始状态,就像什么都没发生过一样。
- 一致性(Consistency) :事务执行的结果必须使数据库从一个一致性状态转变到另一个一致性状态。换句话说,一个事务执行前后,数据库的完整性约束不能被破坏。
- 隔离性(Isolation) :当多个事务同时访问数据库时,一个事务的中间状态对其他事务是不可见的。即使事务并发执行,系统也应该保证事务的隔离性,好像它们是顺序执行的一样。
- 持久性(Durability) :一旦事务提交,它对数据库所做的更改就是永久性的。即使系统崩溃,事务的所有更新操作也不会丢失。
-
并发控制:
虽然隔离性属性要求每个事务都像是在独立执行一样,但在实践中,数据库系统往往允许多个事务并发运行,以提高效率。为了管理这种并发执行,数据库系统实施了一系列的并发控制策略,比如锁定机制、乐观并发控制等,来防止并发事务产生诸如脏读、不可重复读、幻读等问题。
-
实际应用:
假设客户A要向客户B转账100美元。这个过程涉及两个步骤:
- 从客户A的账户扣除100美元。
- 向客户B的账户添加100美元。
为了确保转账的完整性和一致性,这两个操作应该在一个事务中进行。这样,如果在转账过程中出现任何问题(比如系统故障),事务可以回滚,避免只完成一个操作(例如只从A账户扣款,而没有给B账户存款)导致的数据不一致问题。
-
幻读(Phantom Read)
是在数据库系统中的一种现象,它发生在两个相同的事务查询中间插入了新的记录。这使得第一个查询和后续的查询返回不同的结果集,即使查询条件相同。幻读主要和隔离级别有关,在可重复读(REPEATABLE READ)或更低隔离级别的事务中可能会遇到。
假设你是一个图书管理员,并且正在负责统计图书馆中所有科幻类图书的数量。你的助手同时负责录入新购买的图书数据。
- 在上午9点,你开始了你的统计工作,并数出了500本科幻类图书。
- 此时,你的助手登记了10本新购买的科幻类图书进入系统。
- 在上午9点15分,你决定再次检查科幻类图书的数量,以确保准确性。这次你数出了510本科幻类图书。
在这个例子中,"幻读"就是那10本在你的两次统计之间被登记的新图书。尽管你执行了相同的查询动作(统计科幻类图书数量),但由于数据在两次查询之间发生了变化,导致你观察到了不同的结果。
- 隔离级别
隔离级别从最低到最高分别是:
- 读未提交(Read Uncommitted): 一个事务可以读取另一个未提交事务的数据。可能会出现脏读、不可重复读和幻读。对数据一致性要求不高,追求极高性能的场合。
- 读已提交(Read Committed): 一个事务只能读取另一个已提交事务的数据。避免了脏读,但仍然可能出现不可重复读和幻读。需要避免脏读,但可以容忍不可重复读和幻读的情况。
- 可重复读(Repeatable Read) 保证在同一个事务中多次读取同样的记录结果是一致的。避免了脏读和不可重复读,但可能出现幻读。需要保证同一事务内的查询结果一致,但能接受幻读的风险。
- 可串行化(Serializable) 最严格的隔离级别,它通过锁定涉及的数据行来防止其他事务进行修改,以此来避免脏读、不可重复读和幻读。可能导致大量的锁竞争,降低系统的并发能力。需要完全符合ACID属性,不能容忍任何读取异常的系统。
- 锁
- 乐观锁(Optimistic Concurrency Control,缩写”OCC”) :是一种并发控制的方法。乐观的认为多用户并发的事务在处理时不会彼此互相影响,各事务能够在使用锁的情况下处理各自的数据。
- 悲观锁(Pessimistic Concurrency Control,缩写”PCC”) :与乐观锁相对应的就是悲观锁。悲观锁就是在操作数据时,认为此操作会出现数据冲突,所以在进行每次操作时都要通过获取锁才能进行对相同数据的操作,这点跟 java 中的 synchronized 很相似,所以悲观锁需要耗费较多的时间。 它们就是共享锁与排它锁。共享锁和排它锁是悲观锁的不同的实现,它俩都属于悲观锁的范畴。
规范化
- 作用: 数据库设计过程中减少数据冗余的方法。
备份与恢复
- 作用: 确保数据安全的手段,包括全备份、增量备份等。
设计和架构
ER模型 (实体-关系模型)
- 用于数据库设计,描述实体之间的关系。
数据库架构
- 单机、主从复制、读写分离、分布式、微服务架构等。
数据仓库
- 用于存储和管理大量数据,支持复杂的分析查询。
查询语言
-
SQL (Structured Query Language)
- 用于关系型数据库的标准化查询语言。
- 包括DDL、DML、DCL等操作。
-
NoSQL数据库特定语言
- MongoDB使用类似JSON的BSON格式进行查询。
- Redis提供简单的命令接口。
性能优化
-
查询优化
- 正确使用索引、避免全表扫描、使用缓存。
-
硬件优化
- SSD存储、更多内存、高性能CPU。
-
分区与分片
- 将数据分散到不同的物理区域,以提升性能和可伸缩性。
-
负载均衡
- 分散请求压力,提高系统的并发处理能力。
安全性
-
用户权限管理
- 控制不同用户对数据库的操作权限。
-
数据加密
- 对存储和传输的数据进行加密保护。
-
审计日志
- 记录数据库操作历史,用于追踪和安全分析。