架构师日记-从数据库发展历程到数据结构设计探析
一 数据库发展史
起初,数据的管理方式是文件系统,数据存储在文件中,数据管理和维护都由程序员完成。后来发展出树形结构和网状结构的数据库,但都存在着难以扩展和维护的问题。直到七十年代,关系数据库理论的提出,以表格形式组织数据,数据之间存在关联关系,具有了良好的结构化和规范化特性,成为主流数据库类型。
先来看一张数据库发展史图鉴:

随之高并发大数据时代的来临,数据库按照各种应用场景进行了更细粒度的拆分和演进,数据库细分领域的典型代表:
类型 | 产品代表 | 适用场景 |
---|---|---|
层次数据库(NDB) | IMS/IDMS | 以树形结构组织数据,数据之间存在父子关系,查询速度快,但难以扩展和维护 |
关系型数据库(RDBMS) | Oracle/MySQL | 事务的一致性需求场景 |
键值数据库(KVDB) | Redis/Memcached | 针对高性能并发读写场景 |
文档数据库(DDB) | MongoDB/CouchDB | 针对海量复杂数据访问场景 |
图数据库(GDB) | Neo4j | 以点、边为基础存储单元,高效存储、查询图数据场景 |
时序数据库(TSDB) | InfluxDB/OpenTSDB | 针对时序数据的持久化和多维度的聚合查询等场景 |
对象数据库(ODB) | Db4O | 支持完整的面向对象(OO)概念和控制机制,目前使用场景较少 |
搜索引擎(SE) | ElasticSearch/Solr | 适合于以搜索为主的业务场景 |
列数据库(WCDB) | HBase/ClickHouse | 分布式存储的海量数据存储和查询场景 |
XML数据库(NXD) | MarkLogic | 支持对XML格式文档进行存储和查询等操作场景 |
内容仓库(CDB) | Jackrabbit | 大规模高性能的内容仓库 |
二 数据库名词概念
RDBS
1970年的6月,IBM 公司的研究员埃德加·考特 (Edgar Frank Codd) 发表了那篇著名的《大型共享数据库数据的关系模型》(A Relational Model of Data for Large Shared Data Banks)的论文,拉开了关系型数据库(Relational DataBase Server)软件革命的序幕(之前是层次模型和网状模型数据库为主)。直到现在,关系型数据库在基础软件应用领域仍是最主要的数据存储方式之一。
关系型数据库建立在关系型数据模型的基础上,是借助于集合代数等数学概念和方法来处理数据的数据库。在关系型数据库中,实体以及实体间的联系均由单一的结构类型来表示,这种逻辑结构是一张二维表。关系型数据库以行和列的形式存储数据,这一系列的行和列被称为表,一组表组成了数据库。
NoSQL
NoSQL(Not Only SQL) 数据库也即非关系型数据库,它是在大数据的时代背景下产生的,它可以处理分布式、规模庞大、类型不确定、完整性没有保证的“杂乱”数据,这是传统的关系型数据库远远不能胜任的。NoSQL数据库并没有一个统一的模型,是以牺牲事务机制和强一致性机制,来获取更好的分布式部署和横向扩展能力,使其在不同的应用场景下,对特定业务数据具有更强的处理性能。常用数据模型示例如下:
类型 | 产品代表 | 应用场景 | 数据模型 | 优缺点 |
---|---|---|---|---|
键值数据库 | Redis/Memcached | 内容缓存,如会话,配置文件等; 频繁读写,拥有简单数据模型的应用; | 键值对,通过散列表来实现 | 优点: 扩展性和灵活性好,性能高; 缺点: 数据无结构化,只能通过键来查询 |
列簇数据库 | HBase/ClickHouse | 分布式数据存储管理 | 以列簇存储,将同一列存在一起 | 优点: 简单,扩展性强,查询速度快 缺点: 功能局限,不支持事务的强一致性 |
文档数据库 | MongoDB/CouchDB | Web应用,存储面向文档或半结构化数据 | 键值对,value是JSON结构文档 | 优点: 数据结构灵活 缺点: 缺乏统一查询语法 |
图形数据库 | Neo4j/InfoGrid | 社交网络,应用监控,推荐系统等专注构建关系图谱 | 图结构 | 优点: 支持复杂的图形算法 缺点: 复杂性高,支持数据规模有限 |
NewSQL
NewSQL 是一类新的关系型数据库, 是各种新的可扩展和高性能的数据库的简称。它不仅具有 NoSQL 数据库对海量数据的存储管理能力,同时还保留了传统数据库支持的 ACID 和 SQL 特性,典型代表有TiDB和OceanBase。
OLTP
联机事务处理过程(On-Line Transaction Processing):也称为面向交易的处理过程,其基本特征是前台接收的用户数据可以立即传送到计算中心进行处理,并在很短的时间内给出处理结果,是对用户操作快速响应的方式之一。
OLAP
联机分析处理(On-Line Analytical Processing)是一种面向数据分析的处理过程,它使分析人员能够迅速、一致、交互地从各个方面观察信息,以达到深入理解数据的目的。它具有FASMI(Fast Analysis of Shared Multidimensional Information),即共享多维信息的快速分析的特征。
关于OLTP和OLAP的区别,借用一张表格对比如下:

HTAP (Hybrid Transactional/Analytical Processing) 混合型数据库基于新的计算存储框架,能够同时支撑OLTP和OLAP场景,避免传统架构中大量数据交互造成的资源浪费和冲突。
三 领域数据库
列式数据库
传统的以行形式保存的数据主要满足OLTP应用,列形式保存的数据主要满足以查询为主的OLAP应用。在列式数据库中,数据按列存储,而每个列中的数据类型相同。这种存储方式使列式数据库能够更高效地处理大量的数据,特别是需要进行大规模的数据分析和处理时(如金融、医疗、电信、能源、物流等行业)。
两种存储结构的区别如下图:

列式数据库的主要优点:
列式数据库的主要缺点:
列式数据库的应用场景:
总之,列式数据库是一种高效处理大规模数据的数据库管理系统,但需要权衡写入速度、数据模型复杂度和成本等因素。 随着传统关系型数据库与新兴的分布式数据库不断的发展,列式存储与行式存储会不断融合,数据库系统呈现双模式数据存放方式。
时序数据库
时序数据库全称为时间序列数据库 ( Time Series Database),用于存储和管理时间序列数据的专业化数据库,是优化用于摄取、处理和存储时间戳数据的数据库。其跟常规的关系数据库SQL相比,最大的区别在于:时序数据库是以时间为索引的规律性时间间隔记录的数据库。
时序数据库在物联网和互联网应用程序监控(APM)等场景应用比较多,以监控数据采集来举例,如果数据监控数据采集时间间隔是1s,那一个监控项每天会产生86400个数据点,若有10000个监控项,则一天就会产生864000000个数据点。在物联网场景下,这个数字会更大,整个数据的规模,是TB甚至是PB级的。
时序数据库发展史:

当下最常见的Kubernetes容器管理系统中,通常会搭配普罗米修斯(Prometheus)进行监控,Prometheus就是一套开源的监控&报警&时间序列数据库的组合。
图数据库
图数据库(Graph Database)是基于图论实现的一种新型NoSQL数据库。它的数据存储结构和数据的查询方式都是以图论为基础的。图论中图的基本元素为节点和边,在图数据库中对应的就是节点和关系。
图数据库在反欺诈多维关联分析场景,社交网络图谱,企业关系图谱等场景中可以做一些非常复杂的关系查询。这是由于图数据结构表现的是实体联系本身,它表现了现实世界中事物联系的本质,它的联系在节点创建时就已经建立,所以在查询中能以快捷的路径返回关联数据,从而表现出非常高效的查询性能。
目前市面上较为流行的图数据库产品有以下几种:

与传统的关系数据库相比,图数据库具有以下优点:
四 数据结构设计
前面简单介绍了数据库相关的基础知识,下面再介绍几种我们常见的数据结构设计相关的应用实践:拉链表,位运算和环形队列。
4.1 拉链表
拉链表是一种数据仓库中常用的数据模型,用于记录维度数据的变化历史。我们以一个人员变动的场景举例,假设有一个员工信息表,其中包含了员工的姓名、工号、职位、部门、入职时间等信息。如果需要记录员工的变动情况,就可以使用拉链表来实现。
首先,在员工信息表的基础上新增两个字段:生效时间和失效时间。当员工信息发生变动时,不再新增一条记录,而是修改原有记录的失效时间,同时新增一条新的记录。如下表所示:
姓名 | 工号 | 职位 | 部门 | 入职时间 | 生效时间 | 失效时间 |
---|---|---|---|---|---|---|
张三 | 001 | 经理 | 技术 | 2010-01-01 | 2010-01-01 | 2012-12-31 |
张三 | 001 | 总监 | 技术 | 2013-01-01 | 2013-01-01 | 2015-12-31 |
张三 | 001 | 总经理 | 技术 | 2016-01-01 | 2016-01-01 | 9999-12-31 |
这里的生效时间指的是该记录生效的时间,失效时间指的是该记录失效的时间。例如,张三最初是技术部经理,生效时间为入职时间,失效时间为2012年底,之后晋升为技术部总监,生效时间为2013年初,失效时间为2015年底,最后又晋升为技术部总经理,生效时间为2016年初,失效时间为9999年底。
通过这种方式,可以记录员工变动的历史信息,并能够方便地查询某个时间点的员工信息。例如,如果需要查询张三在2014年的职位和部门信息,只需查询生效时间小于2014年且失效时间大于2014年的记录即可。
拉链表通常包括以下几个字段:
1.主键:唯一标识每个记录的字段,通常是一个或多个列的组合。
2.生效时间:记录的生效时间,即该记录开始生效的时间。
3.失效时间:记录的失效时间,即该记录失效的时间。
4.版本号:记录的版本号,用于标识该记录的版本。
5.其他维度属性:记录的其他维度属性,如客户名、产品名、员工名等。
当一个记录的维度属性发生变化时,不再新增一条记录,而是修改原有记录的失效时间,同时新增一条新的记录。新记录的生效时间为变化的时间,失效时间为9999年底。这样就能够记录每个维度属性的历史变化信息,同时保证查询时能够正确获取某个时间点的维度属性信息。
拉链表与传统的流水表相比,它们的主要区别在于:
4.2 巧用位运算
借助于计算机位运算的特性,可以巧妙的解决某些特定问题,使实现更加优雅,节省存储空间的同时,也可以提高运行效率,典型应用场景:压缩存储、位图索引、数据加密、图形处理和状态判断等,下面介绍几个典型案例。
4.2.1 位运算

public abstract class PriorityManager {
// 定义业务优先级常量
public static final int PRIORITY_LOW = 1; // 二进制:001
public static final int PRIORITY_NORMAL = 2; // 二进制:010
public static final int PRIORITY_HIGH = 4; // 二进制:100
// 定义用户权限常量
public static final int PERMISSION_READ = 1; // 二进制:001
public static final int PERMISSION_WRITE = 2; // 二进制:010
public static final int PERMISSION_DELETE = 4;// 二进制:100
// 定义用户权限和业务优先级的组合值
public static final int PERMISSION_LOW_PRIORITY = PRIORITY_LOW | PERMISSION_READ; // 二进制:001 | 001 = 001