如何做数据库选型

常见数据库分类

在微服务设计中,数据库的选型是不可缺少的一环,后台的核心是与数据打交道,在不同的业务场景选择的数据库不一样,好的数据库选型能够解决业务的性能瓶颈,如果数据库选择不恰当,会使得服务的性能上不去,因此我们需要对一些常用的数据库有一些了解,这样才能因地制宜,发挥系统最好的性能。

数据库类型 数据库名称
关系型数据库 MySQL,Oracle,PostgreSQL
内存数据库 Redis,Memcache,Riak
时序数据库 Prometheus,InfluxDB,OpenTSDB
文档型数据库 MongoDB,CouchDB,RavenDB
分布式数据库 TIDB
图数据库 Neo4J,InfiniteGraph,OrientDB,Dgraph
列式存储数据库 Cassandra,HBase,ClickHouse,Elasticsearch
文件存储 EPH,GlusterFS

考虑角度汇总

关系型数据库

关系数据库的适用场景通常可以分为 OLTPOLAP

其中,联机事务处理(OLTP)存储/查询业务应用中活动的数据,以支撑日常的业务活动,是每个企业不可或缺的生产交易系统。这类型应用数据量普遍不大,强调实时、快速响应、数据强一致性,比如银行存取钱,购物消费。

联机分析处理(OLAP)存储历史数据以支撑复杂的分析操作,侧重于决策支持,这类应用数据量大,高并行、低并发,可用性要求不高,比如客服系统,销售系统等。

以 MySQL 为例,这是当前最流行的开源关系型数据库,也是 OLTP 场景的首选数据库。

优点:

  • 成本低,安全,稳定,生态完善
  • 使用简单,方便
  • 使用者多,用来做业务开发,运维比较简单
  • 对于普通业务支持性能很不错

对于请求量,数据量不大的情况下,对 Mysql 做增删改查的业务性能还是很好的,并且 Mysql 提供强一致性事务的支持,支持多种事务隔离级别,并且多种存储引擎的选择,使得 Mysql 在数据库中的地位是非常之高的。但是金无足赤人无完人,在业务扩展到一定规模, Mysql 的问题就暴露出来了,主要的问题有:

缺点:

  • Mysql 对 OLAP 类型查询支持比较弱
  • Mysql 是单机数据库,需要开发人员去实现集群部署,维护成本很大
  • 单表数据规模达到亿级别性能会下降
  • 大表加索引很困难,因此很难对大表进行在线优化

Mysql 对一些复杂类型的查询支持是很弱的,举个例子,如果我们要做个搜索查询的功能,根据各种指标,范围去数据库中搜索数据,如果业务层通过 Mysql 去实现,前期数据量小的情况下是没多少问题的,但是如果表中数据量越来越多,或者搜索的条件组合很复杂,那么此时会很慢。因此,复杂查询业务尽量不要使用 Mysql。另外如果业务扩大到一定规模, Mysql 单表也会变得很大,这个时候性能会受到影响,如果已经不能优化,还需要做分表等扩展措施来解决业务的性能瓶颈。

如果遇到这些情况,目前业界的主流方案除了分区表,分库分表,通过代理中间件进行分表之外,比较热门的就是分布式数据库,分布式数据库最大的特点是能够自由扩展,不要考虑数据量瓶颈的问题。这两年比较流行的是 TIDB 数据库TIDB 是一个分布式数据库,最大的优点是兼容 Mysql 协议,并且通过 Raft 协议保证数据一致性。因此 Mysql 用户可以很简单的迁移到 TIDB 来。并且 TIDB 对一般的 OLAP 查询支持也比较好,因此数据量大的情况下使用 TIDB 是一个更好的选择。

关系型数据最大的特点便是事务,它能保证数据始终如一的一致性,这里就不得不提及一下事务的 ACID 特性:

  • 原子性:事务是一个不可再分割的工作单元,事务中的操作要么都发生,要么都不发生,不存在只执行了其中某一个或者某几个步骤。
  • 一致性:事务执行后,数据库状态与其业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账号余额之和应该是不变的。
  • 隔离性:多个事务并发访问时,事务之间是ddddddddddd隔离的,一个事务不应该影响其它事务运行效果。
  • 持久性:在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,不会因为异常、宕机而造成数据错误或丢失。

通用的 SQL 语言使得操作关系型数据库非常方便,支持 join 等复杂查询。

不建议把 OLTP 和 OLAP 业务混在一起,在典型的 OLAP 处理场景就应该使用面向 OLAP 设计的数据库,而在典型的 OLTP 处理场景就使用面向 OLTP 设计的数据库,否则既达不到 OLAP 的扩展性,又无法满足 OLTP 的实时、高性能等要求。

需要强调的是,特别不建议基于 OLAP 去增加 TP 的能力,因为后者是实时和在线,而 AP 更多是分析,OLAP 的数据库往往无法达到性能要求,即便上线初期看似能满足要求,但随着业务量的增加,问题就会凸显出来。

除此之外,还要根据是否支持 JSON 格式、支持的存储模式等维度评估。

网站业务:网站业务请求写少读多,可使用 MySQL 只读实例水平扩展读负载能力,搭配分布式数据库中间件 DDM 使用,实现自动读写分离和读负载均衡。

移动应用:包含定位功能的移动应用可使用 Postgre SQL 数据库获得位置运算能力;对于数据庞大的移动应用,搭配 DDM 使用 RDS MySQL 数据库,轻松应对分库分表问题。

游戏业务:爆发式增长的玩家数据存储和读写请求,可以使用 RDS 快速扩容存储,变更规格或部署新的游戏分区数据库;游戏数据存档或回退,可使用 RDS 自动备份和 PITR 特性随时闪回到任意时间点。

电商业务:电商“秒杀”、“抢购”等高并发的数据库请求,可使用 RDS 高规格实例;业务连续性要求高的电商业务,可使用 RDS 双机热备,跨 AZ 部署获得更高可用性支持。

金融业务:金融级业务连续性和数据可靠性要求,可使用 RDS 双机热备,跨 AZ 部署,确保服务高可用,数据多副本存储和强一致性;金融级安全合规要求,可搭配数据库安全服务 DBSS 使用,实时监测并拦截 SQL 注入,防脱敏数据泄露,审计数据库日志。

关系型数据库虽好,但在很多方面也有些力不从心。比如面对高并发读写需求时,多表的关联查询复杂的数据分析类型,为了保证 ACID 特性而牺牲的性能问题就凸显出来了。

内存数据库(K-V数据库)

键值数据库就像在传统语言中使用的哈希表。可以通过 key 来添加、查询或者删除数据,鉴于使用主键访问,所以会获得不错的性能及扩展性。它的优势在于简单、易部署、高并发

主流代表产品:Riak、Redis、Memcached

适用场景:储存用户信息,比如会话、配置文件、参数、购物车等等,具体包括游戏场景中的角色信息、经验道具信息、好友排名,电商场景中的海量商品展示信息、购物评论信息等,这些信息一般都和 ID(键)挂钩,所以键值数据库是个很好的选择。

内存数据库用的多的是 Redis,大多数业务场景都是用做缓存,少部分对数据安全不敏感的场景用来做数据持久化,因为 Redis 也支持数据持久化。游戏行业以前用 Memcache 比较多,现在也逐渐向 Redis 靠拢。由于 Redis 数据都放到内存中,并且在局域网环境通信的代价也比较廉价,所以 Redis 的性能非常好,并且 Redis 支持多种类型的数据结构,在不同业务使用都非常方便。

但是 Redis 也有着和 Mysql 相似的问题,单机的 Redis 会有性能瓶颈,理想情况下单机 Redis QPS 也只能达到 10w,如果请求量非常大的情况下会对业务造成影响,并且 Redis 是单线程的,虽然单线程能避免 context 切换的开销,但是并不能利用多核特性,这在高性能应用领域是一个致命打击。集群化部署需要在业务层做哈希来实现负载均衡,如果涉及到增删节点还需要做数据迁移,这段时间也会对业务造成影响。因此,单线程即成就了 Redis 同时也限制了 Redis,因此有不少大公司在实现多线程 Redis 的项目来解决这个问题。

文档型数据库

文档数据库会将数据以文档的形式储存。每个文档都是自包含的数据单元,是一系列数据项的集合。每个数据项都有一个名称与对应的值,值既可以是简单的数据类型,如字符串、数字和日期等;也可以是复杂的类型,如有序列表和关联对象。数据存储的最小单位是文档,同一个表中存储的文档属性可以是不同的,数据可以使用 XML、JSON 或者 JSONB 等多种形式存储。

代表产品:MongoDB、CouchDB、RavenDB

适用场景:

  • 日志:企业环境下,每个应用程序都有不同的日志信息。文档数据库并没有固定的模式,我们可以使用它储存不同的信息。
  • 分析:因为它的弱模式结构,所以不改变模式下就可以储存不同的度量方法以及添加新的度量。

备注:以 MongDB 为例,其使用场景很大程度上可以对标关系型数据库,但是比较适合处理那些没有 join、没有强一致性要求且表 Schema 会常变化的数据

文档型数据库比较有名的是 MongoDB,MongoDB 是非关系型数据库,弱一致性,在 4.0 版本以上开始支持事务,提供 snapshot isolation 的事务隔离级别,这个隔离级别和 Mysql 中的 repeatable read 隔离级别相同。MongoDB 内部采用 GridFS 做数据落地,对一些文件对象支持比较友好。MongoDB 支持多种存储引擎,可以适应不同的场景,并且使用方便,对一致性要求不高的业务场景用的很多,常用来存储一些加工的中间数据,方便去展示,也有一些公司使用 MongoDB 做最终数据落地,因此,很多进行数据展示的业务可以使用使用 MongoDB,或者查询条件多样并且想提高查询速度的场景也可以使用 MongoDB 替换 Mysql。

MongoDB 虽然优点很多,但是也有很多场景是不适应的,刚刚提到过 Mysql 是弱一致的,因此一些重要数据为了安全性考虑,不建议用 MongoDB。并且 MongoDB 比较吃内存,如果返回大批量数据,会有内存不够直接查询失败的情况,MongoDB 对 OLAP 查询支持很弱,因此比较复杂的查询场景也不建议使用 MongoDB

列式存储数据库

列存储数据库将数据储存在列族中,一个列族存储经常被一起查询的相关数据。举个例子,如果有一个 Person 类,我们通常会一起查询他们的姓名和年龄而不是薪资。这种情况下,姓名和年龄就会被放入一个列族中,而薪资则在另一个列族中。

代表产品:Cassandra、HBase、ClickHouse、ElasticSearch

适用场景:

  • 日志:可以将数据储存在不同的列中,每个应用程序能够将信息写入自己的列族中。
  • 博客平台:储存每个信息到不同的列族中,举个例子,标签可以储存在一个列族,类别、文章也可以分别存储在不同的列族。

提到列式存储数据库有必要先说一下什么是行式存储,像 Mysql 这种属于行式存储,当我们查询数据,都是以行为单位在磁盘存储,然后再内存做字段挑选返回给用户,如果表的字段很多我们只查找一部分字段这样磁盘查找的开销就会很大,在列式存储数据库中,数据存在磁盘以列为单位,我们查询某些字段只需要在对应的列上进行搜寻,这样性能就会提高。

列式存储比较出名的数据库是 ClickHouse,ClickHouse 是定义为 OLAP 类型的数据库,专门用来处理数据分析型业务。ClickHouse 采取了并行处理机制,所以做分析查询速度非常快,对于一些搜索,分析,数据规模大的查询性能支持非常好,所以这种业务可以采用 ClickHouse 去做。但是由于 ClickHouse 专为分析查询而生,因此对于更新删除操作支持并不是怎么好,修改删除都是采取的追加写方式,主要是为了避免随机 IO,ClickHouse 会在底层异步的去做数据合并,如果需要频繁的对数据源进行修改,那绝对不要使用 ClickHouse。如果需要写入数据,也需要采取批量写入的方式,ClickHouse 会对数据进行压缩存储,提高资源利用率。

列式存储另一个比较出名的是 Elasticsearch,在一些偏向搜索的场景比较用的比较多,支持倒排索引,并且支持各种聚合运算查询性能强劲,但是成本相比ClickHouse 比较高昂,可以根据业务进行选型考虑。

时序数据库

时序数据库就是存放时序数据的数据库,它需要支持时序数据的快速写入、持久化、多纬度的聚合查询等基本功能。对比传统数据库仅仅记录了数据的当前值,时序数据库记录了所有的历史数据。它的查询也总是会带上时间作为过滤条件,数据以时间流的方式存在,每条记录包括时间戳。可以更加高效快速的存储大量时间序列数据并对这些数据进行实时分析。

代表产品:Prometheus、InfluxDB 和 OpenTSDB

适用场景:IoT 传感器时序数据分析;证券及加密货币交易数据;软硬件设备实时监控;都市环保数据采集等等。

时序数据库从字面意思理解来说是关于时间点的数据,时间序列表示的数据,一般在指标采样的场景会用到,可以很直观的看出数据随时间变化,比较著名的有 OpenTSDB,时序数据随时间增长,如果数据没有变化重复取上一个值。时序数据库适用于大批量的数据采样分析,常用在批处理,流处理后的数据落地,这些数据经过加工后直接批量写入到 OpenTSDB 中,供业务层访问,以提高查询性能。美中不足的是如果数据加工错误,纠错成本太大。OpenTSDB 底层是用的 HBase,HBase 特点是对写性能支持比较好,底层也是为了避免随机 IO 采用追加写的技术。读通过顺序读保证性能,如果是随机读性能会很差,因此 OpenTSDB 应用场景限制于按时间维度分析数据的场景。

图数据库

数据以图的方式储存。实体会被作为顶点,而实体之间的关系则会被作为边。比如我们有三个实体,Steve Jobs、Apple 和 Next,则会有两个“Founded by”的边将 Apple 和 Next 连接到 Steve Jobs。

代表产品:Neo4J、InfiniteGraph、OrientDB、Dgraph

适用场景:

  • 在一些关系性强的数据中,用于构建关系图谱,例如:社交网络、交通地图
  • 推荐引擎:如果我们将数据以图的形式表现,会非常有益于推荐的制定。

分布式文件存储系统

常用的分布式文件系统有 HDFS,Ceph,GlusterFS 等,种类非常多,不同的分布式文件存储系统适用不同的场景,每种系统适用的场景不一样,比如有些对大文件支持比较好,有些对中小文件支持比较好。基本上所有分布式文件系统都支持对象存储,块存储,文件存储中的两种以上。分布式文件存储系统常用来存储文件,比如文档,照片,电源,视频这种。不像数据库那种存储一些数据,并且分布式文件系统不支持那种维度查询,这是和数据库的最大区别。

分布式文件存储系统的选型要根据文件的形式,大小,用途去考虑。还需要对参数做大量的调优以得到最佳的性能。传统的本地文件系统文件都是存在磁盘中,而分布式文件系统文件需要存储在其他节点,本地进行访问时还需要经过网络中转的开销,如果文件比较大,带宽就限制了传输的速度。这个时候就需要进行将文件分片进行存储。以 GlusterFS 为例, GlusterFS 的 Distributed Volumes 属于分布式卷,如果需要保证数据安全可以做冗余,GlusterFS 支持 Replica 和纠删码两种数据冗余方式,如果做数据冗余,会影响读写性能。在做选型时,有这两方面考虑,如果追求极致的数据安全,使用 Replica Volumes,如果为了性能,也为了安全,使用纠删码的方式,如果追求极致性能,可以考虑分布式卷,但是如果出现机器宕机则会造成数据丢失。