课件

12.8 数据库选择策略.pdf

什么是数据库?

image.png
什么是数据库,维基百科中的解释是 A database is an organized collection of data,简单地来说,数据库就是对数据的管理,在实际业务中一般包括对数据的增、删、改、查等操作,更多的也会包括数据管理中对用户的访问权限管理、数据的持续化、以及分布式等不同方案的选择。

这里我们简单地举一个数据库的例子:
image.png
假设一个用户管理系统,它的数据库至少应该有以下几个功能:

  • 保存这张用户信息表
  • 能够通过 ID,用户名或昵称查找到其对应的所有信息
  • 支持对密码,呢称的修改
  • 支持对用户的添加和删除

在实际的应用场景中,还需要保证这几个功能能够在尽量少的时间内得到响应,以及使用尽量少的存储空间,来使得能够使用一台服务器就能够实现这样一个用户管理系统。

试想一下,如果存在这样一个超级数据库,它可以存储任意大小的数据以及数据之间的关系,同时提供了最快的增删改查操作,那么它就能解决一切数据问题。然而理想是美好的,现实往往是残酷的。我们不能没有无穷的存储空间, 也没有光速的响应时间。我们有的仅仅是一台 500G 磁盘、4G 内存、I3 处理器电脑。在实际应用场景中如何选择合适的数据库才能够最大化我们服务器的性能,得到最优化的输出结果呢?

这例子中,我们简单对其进行抽象,事实上就是数据应该怎么存、数据应该怎么查找、如何修改数据以及如何添加和删除数据,这几个基本的功能。

数据库的基本分类

在进行数据库的选择之前,需要对数据库的一些基本性质有所了解。针对面临的一些问题,现有的数据库往往有以下一些简单的性质分类:

  • 数据库的数据应该怎么存?
    • 既可以分为持续化存储内存数据库,顾名思义,持久化存储指的就是将数据持续化在磁盘上,内存数据库指的则是数据直接存储在内盘中。
    • 数据存储还能分为单机分布式,单机也就是一台服务器,分布式指的则是可能是一个数据库集群,或者是多台服务器。
  • 数据如何进行增删改查?
    • 实际应用中,可以分为关系查找key-value 查找的形式。
  • 操作是否安全?
    • 涉及事务、一致性和最终一致性的问题。
  • 可用性
    • 如果数据库故障了,应该如何恢复?大部分现有数据库都有一个简单的故障恢复功能。

比如,图1例子中,关系查找只单纯地通过用户 ID 对用户名、密码、昵称进行查找,key-value 查找则有所不同,可能一个用户 ID 对应的是一个随时改变的文档类型,如下所示:
image.png
一个用户 ID 可能对应了用户密码,也可能对应了用户名、密码、昵称,甚至还有可能其他属性。这是关系查找与key-value查找的区别。

接下来简单介绍数据库的事务、一致性。

事务往往应用在金融交易当中,比如银行转帐业务,想象这样一个场景,A 需要银行转帐给 B 一定的金额,可以简单分为这样几个步骤:A 首先查询自己银行帐户的余额、输入转帐的额度、然后银行从 A 帐户中删除金额、给 B 帐户增加金额、B 帐户再确认金额是否到帐。这中间如果某一步出错了,或者如果银行从 A 帐户删除金额这一步没有成功而直接给 B 帐户增加了金额,那么银行是否会产生一定的损失,而同时如果两个人使用 A 帐户同时进行转帐,是否会出现银行对 A 帐户只进行一次删除金额的查询。那么,事务能够保证操作序列的完整执行,也就是如上这样一个完整的多步流程要么被同时执行,要么都不执行,这在涉及金融的业务中尤为重要。

一致性通常分为实时一致性与最终一致性。可以简单想象这样一个场景,A 在微信朋友圈中发布了一张照片,如果同一时间他的所有好友都能看到,那么就是实时一致性。而他发布了一张照片之后,他的好友总会在未来的某一个时刻看到,比如 1 分钟后、1 小时后或者明天,这就是最终一致性。最终一致性与实时一致性的最大区别在于是否保证一定要在同时能够被他的好友所看到。实时一致性在应用中往往极度消耗计算资源,实际的场景中通常会采角最终一致性来进行一定程度的妥协。

常用数据库介绍

MySQL

image.png
MySQL是当前最流行的开源关系型数据库。它十分简单易用,同时拥有大量的第三方插件,社区活跃,文档丰富。作为关系型数据库,它支持快速的复杂查询操作,同时也有完整的事务机制和非常高的安全性。

MongoDB

image.png
MongoDB是当前最流行的非关系型数据库,其模式自由,可以自由地根据需要随时修改文档格式,同时支持海量数据的查询和插入,支持完全索引。作为最流行的分布式数据库,它还自动支持分片等分布式操作,支持故障恢复与备份,良好的文档与社区环境使得它的学习成本较低。

需要注意的是,与MySQL对比,它有很大的不同:

  • Mongo 需要占用很大的空间来建立索引
  • 作为非关系型数据库,不支持事物操作,只具有最终一致性
  • 作为新兴的非关系型数据库,其社区尚不成熟,在较高安全级别的应用中也无法得到保证

Redis

image.png
Redis 作为近年兴起的内存数据库代表,它把数据直接存储在内存之中,保证了访问的高效。Redis 在保证访问速度的同时,也能够进行持久化存储,其本身也是 key-value 的存储形式,支持非常多的数据结构,比如列表、字典。

在实际应用场景中,与 MySQL、MongoDB 的区别是:

  • 作为内存数据库,数据直接保存在内存中是十分不可靠的,任何的重要数据都不应该存储在内存数据库中,因为一旦宕机,内存数据库是不可恢复的
  • Redis 不支持完整的事务实现,不适合作为安全性高的场景应用

数据库选择策略

如何对数据库进行选择?对具体的实际问题需要具体分析。在实际应用当中是否使用现有的几种数据库就能够完全满足我们的需求了呢?

事实上我们只需要选择 MySQL、MongoDB 中的一种就已经能够满足我们大部分的应用了。

image.png
如果不能够满足我们的应用的要求,需要分析出具体是什么问题构成了我们应用的阻碍,是访问的需求量过大?安全一致性要求高?还是实施一致性的需求?这时,可从两方面来考虑:

  • 一方面是不是我们的业务过于复杂,可以尝试拆分业务,不同的场景采用不同的数据库,例如涉及财务金融等信息需要使用事务的性质,那么可以采用 MySQL。而大量的用户数据需要保存、需要设计到大量的查询,MongoDB 有其优势。需要非常快的反应速度比如网站首页,需要在最快的时间内得到响应,可使用 Redis 把这些首页放在内存之中。
  • 如果业务没有办法拆分同时问题比较少见,还没有第三方开源的一些现有的代码可以使用,那么我们完全可以在开源的代码基础上进行二次开发,豌豆荚、豆瓣、淘宝等的尝试就是最好的一些例子。

在实际应用当中,数据库实现成本也是我们非常需要考虑的一个问题。有时候我们往往需要考虑到程序员具体熟悉哪一个数据库,这样可以降低我们的学习成本。应用中是否需要支持分布式,MongoDB 自动支持分布式,就自有其优势。是否现在的业务中不确定业务模型,比如很多初创企业并不知道他们以后未来会发展成什么样子,那么 MongoDB 支持自由的模型模式就能很好地满足了这一需求。

数据库选择案例分析

案例1
考虑一个简单的社交网站案例,要求功能是用户登录,分享等功能。要求并发是每秒同时在线人数大约为100人。

简单分析一下:

  • 首先,需要用户数据能够得到持续化的存储,因此内存数据库不被我们所考虑
  • 其次,对用户的安全性最高,不要事务机制,那么 MongoDB 是可以使用的
  • 查询并发量也比较小,以 MySQL 2000 的并发和 MongoDB 5000 的并发做估计,发现这两个都是可以的
  • 但业务使用模型比较复杂,作为社交网络,其业务随时可能会有所改变,这方面 MongoDB 是有优势的

综上,我们建议使用 Mongo 作为数据库。

案例2
考虑一个新闻网站,没有用户登录与交互,仅需要展示新闻内容,但是同时的用户访问量大约为10000人(每秒万人访问)。

分析:

  • 没有用户数据,也没有复杂的关系业务
  • 访问量非常的大,MySQL 与 MongoDB 都可能不适用如此大的信息访问
  • 内容单一,也就是新闻的内容,基本上是不会进行再次修改的,因此可考虑内存数据库
  • 安全性要求也不是那么高,因此,内存数据库可以使用的

综上,我们建议使用 Redis 作为数据库。

案例3
考虑一个抢票服务,功能为在某个时刻为大量用户进行抢票服务。要求能够在10s内为5000个用户正确的返回抢票结果。

分析:

  • 用户的数据需要持久化存储,因此考虑 MySQL 和 MongoDB
  • 要能够支持事务机制,不能让一张票被两个人同时抢到,这是应该考虑 MySQL
  • 还要求能够超快速度地响应时间,需要在短时间内返回结果,这是应该考虑内存数据库

那么我们到底应该怎么用呢?事实上,这样一个复杂的应用场景,我们应该尝试对其进行拆分,也就是能不能数据持续化存储用 MySQL,抢票则使用内存数据库进行响应等,显然这样直接的拆分可不行的,而需要进行业务的拆分。

针对抢票业务,我们来分离一下业务场景,可以很明显地分为两个阶段:

  1. 非抢票阶段,用户只需要进行登录、注册,和一定程度上的查询就可以了
  2. 抢票阶段用户抢票的时候首先需要进行身份的验证,然后发出抢票请求,系统来验证是否还有余票,最后系统返回结果

针对以上的场景分析,我们发现,只有抢票的过程中对速度有着极大的需求,因此我们希望这个过程全都在内存中进行,而在非抢票阶段,用户的登录注册需要进行持续化的存储,完全可以使用 MySQL 和 MongoDB 来进行。在抢票时,我们可以将所有的用户信息读入到内存数据库之中来保证访问速度,最后在验证余票的时候,必须有一个事务机制来保证不会出现一张票被两个人同时抢到,这一机制内存数据库是没有办法实现的,然而我们可以通过自己的业务代码也就是应用层的代码,来实现这样一个事务机制,从而能够保证 2 的任务全都在内存进行,保证了反馈的速度。