在互联网横行的时代,我们都知道得数据者得天下。
工作中我们一直说数据库,话说过来数据库到底是什么呢?
大家可能会想到 MySql、PostgreSql......可能大家还会想到Redis、MongoDB、ElasticSearch.....
0、数据库 —— 类比图书馆
我们可以类比学校里的图书馆。想象一下我们去图书馆相对于用户来获取书籍(数据)或者归还书籍(数据)。一般图书馆都是拥有大量的书籍。存放都是有特点:有规律、有组织的,分门别类归纳存储好。
大家先思考一下书籍这样子存放方式的优点是什么?
张三同学可以取,李四同学也可以取,这个书籍都是可共享的,并且都是由图书管理员统一管理。
如何更好的理解数据库呢?让我们从一个文件系统开始。
1、数据库 —— 文件系统
比如我们正在做一个电子书相关的项目。前期我们把所有的书籍都放在文件中:
"活着","余华",1993
"
数据库系统概念","
希尔伯沙茨",2008
这种存储方式,实现起来简单,似乎很完美。
接下来,我们要查询《数据库系统概念》的作者,我们采用遍历,这是非常糟糕的查询方式。
我们再思考一下,如果后期数据量上去了,数据被存放在多个文件里,每次查询,我们就得打开很多个文件,打开后还要遍历里面的数据,磁盘 IO和时间复杂度都很高。
现在查询的问题在于:我们的数据,是无规律的。
一旦数据没有规律,我们查找数据时,就不知道数据在哪个文件,就只能一个个文件打开来看,靠穷举算法去遍历。接下来我们要改变数据存储的方式:规律存储。
2、数据库 —— 规律存储
如果我们数据存储的方式改为有规律,有组织,我们是不是可以使用算法来高效的查询它们呢。
我们又回到刚开始类比的图书馆里书籍存放的优点是什么?是的,我相信大家都知道了,方便大家高效的获取对应的书籍。
如果刚才我们小程序里的文件存储方式按照 字典排序,于是我们可以进行二分查找,时间复杂度从 O(n) -> O(log2n),缺点是每次插入都要排序;
如果刚才我们小程序里的文件存储方式按照 Hash 表,于是我们可以进行Hash 查找,用空间换时间,时间复杂度 O(1);
如果刚才我们小程序里的文件存储方式按照二叉树,于是我们可以进行二叉查找,时间复杂O(log2n);
二叉树极端情况下会退化成 O(n),于是有了平衡二叉树。
平衡二叉树终究还是“二叉”,只有两个子节点,一次从磁盘 load 的数据太少,于是有了可以有多于 2 个子节点的 B 树。
B 树查询的数据,是无序的。如果我们要求数据排好序返回,还要在内存手动排一次序,于是有了叶子节点是一个双向链表的 B+ 树……
由于大家不断把存储结构规律,得到的查询性能就很nice。
我们也可以按照‘作者‘查询,建一个B+ 树,按照’年份‘查询,建一个B+ 树,这样每增加一个字段查询,都要建一个B+ 树。如果B+ 树里面放的是全部数据的信息,会很冗余、很占用空间。
我们可以优化一下 B+ 树只记录数据的唯一标识,根据索引找到数据的唯一标识后,再去全量加载数据 。
这就是 MySql里面的聚簇索引和二级索引:
- 二级索引 只存储对应字段和唯一标识,查找时利用二级索引,可以快速找到数据的唯一标识。
- 聚簇索引是数据实际存储的位置,它也是有序的,按照唯一标识有序存储。
- 可以在二级索引里拿到唯一标识后,可以快速地在聚簇索引找到数据的位置,大大减少了磁盘 IO。
MySql 有一句话,“索引即数据”,指的就是聚簇索引。当然,如果用到了覆盖索引,则二级索引也能提供数据。
我们经常说,索引提高了查找性能,其实不完全正确。
还是以 Mysql 为例,二级索引只是告诉了我们数据的唯一标识,但是我们还要拿着这个唯一标识去数据里查找,如果这些数据本身不是有序的,那你还是得找大半天。于是 MySql 设计了 B+ 树来存储数据,让这些数据有序,也就是聚簇索引。
这就像 我们在字典里查一个单词 three,你在目录,也就是索引里,找到这个单词在第 33 页,然而,这本书在装订的时候,页面订乱了,不是按递增来装订的,完全无序。结果就算我们知道了 three 在第33 页,你还是得海底捞针般的,把整本书翻一遍。
索引仅仅帮助我们快速找到数据的标识,加上数据规律的存储,才能减少磁盘 IO,才能加速查询。
可以得出:索引 + 规律存储 = 快速查询
不过对于 MySql 来说,它的规律存储:是通过聚簇索引来实现的,所以说是索引让它查询变快也是对。
数据结构带来了规律存储和快速查询,也带来了操作的复杂度。
我们不能随意插入数据,因为我们要维护数据的规律性,不管我们是顺序存储还是 B+ 树,都要找到正确的位置进行插入。
这个时候我们会想做个缓存来进一步减少磁盘 IO,但我们要维护好缓存的生命周期......
如此多复杂的逻辑,如果都要让用户感知到,自己手动操作,使用成本太高,每次插入都要写一大段代码,不方便用户操作和记忆。于是我们要给用户提供简洁的操作方式。
3、数据库 —— 简单操控
几乎我们用过的所有数据库,都会提供让你很方便的操控它的方式。
像 MySql、PostgreSql 等关系型数据库,操作它们的语言。基本都是 SQL语言(Structured Query Language:结构化查询语言) 是用于管理关系数据库管理系统(RDBMS)。 SQL 的范围包括对数据的CRUD 增删改查,数据库模式创建和修改,以及数据访问控制。
这是结构化数据领域的通用语言,我们称为 DSL(domain-specific language,领域特定语言):
ALTER TABLE users add COLUMN gold INTEGER ;
像 Redis,它也定义了自己的一套语言,但是它比较谦虚,自称为 Command :
redis> SET mykey “Helloword” “OK” redis> GET mykey “Helloword"
像 ElasticSearch 一样直接提供 Restful API 的:
curl -X GET “localhost:9200/twitter/_doc/0?_source=false&pretty”
DSL、Command、API,其实都是为了方便我们使用,降低了我们的使用成本。毕竟写一堆代码的成本还是很高的。
但是对于我们的学习成本,可能是加大了。因为它封装了其背后的实现细节。
看似简简单单的语句背后,触发的可能是一连串复杂的逻辑。
4、数据库 —— 隐藏功能
如此复杂的逻辑,就是数据库的隐藏功能。
一个数据库在隐藏功能上下文的功夫,决定了它能在高性能、高可靠的道路上走多远,决定了它能否被广泛用到生产环境。
而我们对一个数据库隐藏功能了解的程度,也成了衡量一个人对这项知识掌握程度的标准。
在我们一行指令的背后,触发的隐藏功能,包括但不限于:
- 事务:事务具有四个属性:ACID,当然数据库不是完全满足这四个属性,有的数据库甚至不支持事务,比如 MySql 在 读未提交 的隔离级别下,就不满足 C 隔离性 ,对数据可靠性要求不高的,比如 Redis,它也无需实现事务。
- 锁:和 PHP 一样,有并发访问,就有并发安全,就需要锁,比如 MySql 的 MVCC.
- 集群:这是实现一个高性能高可靠系统的标配,我们需要对数据进行冗余和分片存储,所以,在插入一条数据时,我们的数据库可能需要判断要插入到哪一台机器,插入后,还有判断要冗余到哪些个机器。
- 缓存:数据不能每次都去磁盘 load,放到缓存,缓存失效了再去磁盘拿,数据一旦被更新,缓存就失效吗?不,数据更新时,更新的是缓存的数据,同时记录日志,然后再去刷磁盘,MySql 和 ElasticSearch 都这么做。
- ……
5、数据库 —— 如何定义
上面我们从文件系统开始,一步一步演化成一个常用的数据库。
这里让我们用三个关键字 + 三句话,来给数据库通俗易懂的定义:
- 规律化存储的文件系统:数据库,是一个把数据进行规律存储的文件系统。
- 简单访问:它给使用者提供了简单的操控方式,去增删改查它的数据。
- 封装功能:为了做到高性能、高可靠,它实现了一系列复杂的逻辑,这些逻辑对一般使用者来说无需关心。
我们再来看看百度百科上对数据库、数据库管理系统的解释:
- 数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。英文 DataBase 简称 DB
- 数据库管理系统(Database Management System)是一种操纵和管理数据库的大型软件,用于建立、使用和维护数据库,简称DBMS。它对数据库进行统一的管理和控制,以保证数据库的安全性和完整性。用户通过DBMS访问数据库中的数据,数据库管理员也通过DBMS进行数据库的维护工作。它可以支持多个应用程序和用户用不同的方法在同时或不同时刻去建立,修改和询问数据库。大部分DBMS提供数据定义语言DDL(Data Definition Language)和数据操作语言DML(Data Manipulation Language),供用户定义数据库的模式结构与权限约束,实现对数据的追加、删除等操作
这是学术的定义。目的是给一个通用的解释、划定边界,所以一般会比较抽象。
它告诉我们:
- 数据库是数据的有组织的集合,用到了一些设计和技巧。
- 数据库管理系统,则是给我们去访问数据库的。
它不会告诉我们数据库具体怎么组织,用到怎么个技巧,也不会告诉你 数据库管理系统 是怎么去访问数据库的。
回到一开始我们类比的图书馆。
图书馆里的书籍都是有规律、有组织的存储,每一本书都编辑了编号当作唯一标识,方便我们查找,归还等操作。大家是不是更好理解了呢。
我们必须要去学习具体的知识,然后再反过来看概念,才能看懂、看透,才能摸索出通用的规律。加上我们类比生活中的例子和场景更能融会贯通。这就是我们学习的流程:
- 第一阶段我们先知道是什么,会使用就可以了。
- 第二阶段我们再回过头来去研究为什么是这样子。
- 第三阶段我们会发现一些问题再思考怎么办。
是不是发现了通常我们在聊数据库时,聊得不只是个普通的数据,而是规律存储的数据,而且还有一个 数据库管理系统,让我们去访问它:
针对数据库,可以理解为是我们和数据打交道的媒介。
我们的对数据的所有操作,都会通过数据库来实现。我们从使用者的角度出发,
数据库,简单的说就是是我们访问数据的中间件。
具体选择哪个中间件,取决于我们的使用场景。而选择哪种数据库,则取决于我们对数据的使用场景:
- 如果我们需要数据安全可靠,最好是用 MySql、PostgreSql 这样的关系型数据库。
- 如果我们只是缓存一些临时数据并需要快速查询,可以使用 Redis、Memcached 这样的 Key-Value内存数据库。
- 如果我们想放一些文档,并可以支持相关性搜索,可以使用 ElasticsSearch 这样的搜索引擎。
- .....
6、数据库 —— 如何学习
根据上面我们给数据库的了解,我们尝试给数据库学习分三个阶段:
- 接触:了解这个数据库的使用场景,为什么需要它,在什么场合下使用它。
- 使用:如何通过这个数据库操控数据,了解它的 API/Command/DSL
- 深入理解:它是如何存储和索引数据的?它是如何做集群和分布式的?还有什么其他让它高性能高可靠的封装功能的。
比如我们学习几个数据库验证上面的学习方式:
MySql:
- 执行查询/更新语句的流程是什么?
- 增删改查的命令是什么?
- 索引的类型有哪些?使用原则是什么?什么场景使用索引,什么场景索引失效?索引的数据结构是什么样子的?B+树与B树的区别在哪里?
- 事务的特性是几种,隔离级别有哪些?支持事务的存储引擎?存储引擎之间的区别与优势?
- 锁的分类有哪些?锁的算法有几种?
Redis:
- Redis是做缓存的,这个基本都知道,我们可以再了解下什么时候要用到缓存,它相比其他缓存中间件具有的优势。
- 如何往 Redis 插入数据、更新数据、查询数据 ……
- Redis 各种数据类型的数据都是怎么存储的?为什么可以那么快找到数据?Redis 的分片和主从是如何实现的?
Elasticsearch:
- 为什么需要 Elasticsearch ?什么情况下需要用到搜索引擎?
- 如何往 Elasticsearch 插入数据、搜索数据、分析数据?
- ElasticSearch 如何存储数据?如何索引?集群结构长什么样?
带大家从数据库的起源讲起,一直聊到各种 Nosql,相信大家已经弄清楚数据库是什么了,也能更好理解数据库了!感谢大家的支持。说的不好的请大家给出建议!
参考文章&书籍
- 《数据库系统概念》
- 关系数据库概述 - 廖雪峰
- 为什么需要数据库
- Mysql 索引简明教程