如何选择数据库
此文章来源于同事做的分享
随着公司技术的发展,目前使用的存储种类越来越多。早期使用mysql,后来引入hbase解决数据规模庞大的问题。又因为后端平台业务的需求多变以及厌烦了mysql的分库分表,有引入了MongoDB。再者引入elasticsearch来解决线上搜索业务,以及数据可视化问题。也有同事将活动一类的短期数据存储在Redis中,将其作为线上数据存储。在Redis的4.0发布之前,我们选择了codis解决单机内存不足,业务读取QPS高等问题。
因此目前线上使用的数据库有:
- MySQL
- hbase
- Redis
- Codis
- MongoDB
- ELK
可选种类如此之多,导致开发过过程中,选择合适的存储服务。
MySQL老牌关系型数据库
官方发音为/maɪ ˌɛskjuːˈɛl/“My S-Q-L”[5],但也经常读作/maɪ ˈsiːkwəl/“My Sequel”)原本是一个开放源代码的关系数据库管理系统,原开发者为瑞典的MySQL AB公司,该公司于2008年被Sun Microsystems收购。2009年,Oracle收购Sun Microsystems,MySQL成为Oracle旗下产品。
Oracle并未一家慈善公司。收购Sun Microsystems 公司后,一直在布局新的盈利模式。MYSQL付费版价格提升,组建更大的律师团用于对JVM收费。因此我们可以将目光投向目前社区维护的MariaDB。MariaDB是MySQL的一个分支,仍然是真正的开源产品。
由于MySQL的易用性和性能强大,国内互联网公司都在使用MySQL作为业务存储数据库。我们可以总结一下MySQL的优点:
易用性:
- 为多种编程语言提供了API。这些编程语言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。
- 提供多语言支持,常见的编码如中文的GB 2312、BIG5,日文的Shift_JIS等都可以用作数据表名和数据列名。
- 提供TCP/IP、ODBC和JDBC等多种数据库连接途径。
- 既能够作为一个单独的应用程序应用在客户端服务器网络环境中,也能够作为一个库而嵌入到其他的软件中。
运维方便:
- 提供用于管理、检查、优化数据库操作的管理工具。
- 支持多种架构方式,方便做灾备/负载均衡。支持主从同步,双主一致性架构。
性能强大:
- 使用C和C++编写,并使用了多种编译器进行测试,保证源代码的可移植性。
- 支持主流的操作系统。
- 支持多线程,充分利用CPU资源。
- 支持多种存储引擎。
- 支持大型的数据库。可以处理拥有上千万条记录的大型数据库。可以支持到GB或者TB级别。
- 优化的SQL查询算法,有效地提高查询速度。
mysql提供了事务,对于强一致性要求高,数据要求可靠的业务也会优先采用mysql存储。因此我们大量的基础业务都在mysql上。
对于业务需求稳定。数据字段不会经常增减,字段也非稀疏存在。即使数据量比较大,仍然可以通过分库分表的方式防止单表数据过大。
随着用户增长,业务数据出现急剧增加的情况。比如用户资产数据逐步增大,mysql多次扩充分表单表数据越来越大,运维成本和资源使用开始变得不可控制。这时我们将目光投向了Hbase。
Hbase 为大数据而生
Hbase是Hadoop Database的简称,是一个开源的非关系型分布式数据库(NoSQL),它参考了谷歌的“Bigtable:A Distributed Storage System for Strctured Data“论文来设计,实现的编程语言为 Java。
HBase在列上实现了BigTable论文提到的压缩算法、内存操作和布隆过滤器。HBase的表能够作为MapReduce任务的输入和输出,可以通过Java API来访问数据,也可以通过REST、Avro或者Thrift的API来访问。
在 Eric Brewer的CAP理论中,HBase属于CP类型的系统。
作为BigTable开源实现的鼎力之作。Hbase具有以下几个优点:
- 海量存储,支持PB级别的数据,可以使用廉价的PC存储,极大的降低了数据存储成本。
- 高并发,由于HBase采用了region方式将数据切片存储和承接访问,因此可以通过扩充RegionServer来提升整体的并发能力。
- 列族存储,存储稀疏字段不占用空间。
区别于传统的RDBMS(关系数据库管理系统 Relational Database Management System),如果想要使用Hbase需要做到以下几点:
- 数据足够多,如果预期的数据量比较小,采用Hbase有点牛刀杀鸡了。
- 业务上可以不依赖 RDBMS 的额外特性,例如,列数据类型, 二级索引,SQL 查询语言等。
- 确保有足够硬件,经典5台服务器部署,2台NameNode(一台主,一台备),3台 DataNode(同时部署ResourceManager, RegionServer)。5台机器都部署ZooKeeper。新版本的Hadoop 可以将NameNode和DataNode混合部署在一起,每台机器都启动NameNode,NameNode Master在ZK中选举产生。
Redis 流行的内存数据库
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库。
Redis的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis中值的类型不仅限于字符串,还支持如下抽象数据类型:
- list:字符串列表
- set:无序不重复的字符串集合
- sorted set:有序不重复的字符串集合
- hash:键、值都为字符串的哈希表
值的类型决定了值本身支持的操作。Redis支持不同无序、有序的列表,无序、有序的集合间的交集、并集等高级服务器端原子操作。
早期我们主要是用Redis和memcache做缓存服务。两者各有分工,Redis存储短小的字段,memcache存储大字段。随着Redis的使用,对其有了更深层次的了解。公司内也组织集中学习了Redis源码。通过对原理的学习,打消了我们的顾虑。不仅仅把Redis作为缓存服务器,逐渐将一些段时间高请求的数据存储在Redis中。
Redis 本身很高效。对于优化处理速度,redis做了以下几点:
- 所有数据都在内存中,并对存储数据结构做了大量优化。
- 使用单线程处理前端请求,减少了不必要的上下文切换和锁竞争。
- 在linux服务器上采用 Epoll(多路I/O复用模型)。
- 采用多种方法来处理数据过期,如:惰性删除:查询已经过期的key,直接删除。同步删除:在处理前端请求时同步删除少量过期的key,达到最大内存时足够多的key。定期删除:定时清理已经到期的key。异步删除:通过 lazyfree 线程删除大key。
Redis也并非银弹。内存数据库始终存在弊端。内存始终有限,无法支持在线扩容。落盘存在延时。宕机之后恢复时间比较长。主从复制并非强一致,宕机存在数据不一致的情况。big key问题也很严重。单个key过大,比如超过10w的zset。在对象申请或者释放内存时会阻塞redis。
如果内存充足,其实线上服务器很难遇到宕机问题。做好主从,使用从库定时备份
针对单机redis 容量有限的问题,调研过redis cluster、twemproxy和codis。考虑客户端需要升级才能支持新的cluster转发协议。并且转发过程存在天然损耗,并且部署运维成本高。
Twemproxy 存在无法平滑地扩容/缩容。
正好赶上Codis主要开发者刘奇来我们公司布道,于是我们开始试用Codis。
Codis
Codis是一个分布式redis集群解决方案。本身采用golang 编写 proxy层,将业务请求转发给后端的 Codis-redis中。起到作用类似于透明代理,对于业务层无感知。无需更改业务代码就能切换到Codis上。
Codis本身不保证数据的有效性。线上常见的是利用主从确保数据不会丢失。引入Group概念,将一组redis master和slave作为一个group,这样当master宕机之后,可以使用工具切换到slave,无需手工操作。
多个Group采用 Pre-sharding 的技术来实现数据的分片, 默认分成 1024 个 slots (0-1023), 对于每个key来说, 通过以下公式确定所属的 Slot Id : SlotId = crc32(key) % 1024。
每一个 slot 都会有一个且必须有一个特定的 server group id 来表示这个 slot 的数据由哪个 server group 来提供。数据的迁移也是以slot为单位的。
优点:
- 支持常用的redis命令,原生redis协议,无需更改业务代码,无缝迁移。
- 动态扩容/缩容,增减redis实例,不需要重启服务,用户无感知。
- 集群管理工具丰富,提供dashboard和命令行工具,方便运维管理。
缺点:
- 基于原生redis代码,重写了关于slot相关逻辑,无法直接升级后端redis。
- proxy 基于golang,存在gc问题。会因为gc导致出现抖动。
- 基于group主从保证数据安全性,可能存在丢失的情况。
我们逐渐将redis业务迁移到Codis中。新部署的业务也从版本2升级到3。
灵活的MongoDB
在这种猪都能漫天乱飞的年代。产品总是能够想出一个新的点子,需求还总是在变化。这时候使用mysql总是遇到上线前要更新表或者补字段。刚刚工作的时候就在想是不是有个数据库不用关心表结构。最终等到了 MongoDB 提出Schema free。
MongoDB是一种面向文档的数据库管理系统,由C++撰写而成,以此来解决应用程序开发社区中的大量现实问题。即提供了传统数据库的功能:二级索引,完整的查询系统以及严格一致性等等。还带来了敏捷性和可扩展性。加速产品上线时间和降低企业成本。
- 开箱即用,MongoDB 二进制发布,无需配置即可使用。
- 文档类型,业务不用关心数据变化。采用类似于json的bson结构,支持层级式数据。单条数据就可以组合多个业务,因此不需要跨表组装数据。甚至于直接将数据库查询接口使用json编码直接返回给前端。
- 内置Sharding,最大支持20PB数据。Replication 复制保证多个副本,不会因为机器故障导致数据丢失。早期数据量不大的时候基本无需运维。
- 官方维护多种语言客户端,不担心小语种没有好用的客户端问题。
ELK全面出击
Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎,能够解决不断涌现出的各种用例。作为 Elastic Stack 的核心,它集中存储您的数据,帮助您发现意料之中以及意料之外的情况。
我们最早使用es搭建日志收集平台。使用logstash收集到es中。再通过Kibana 展示给前端。只需要写几个配置文件,就可以得到一个可视化的数据平台。随着业务的增长,后来把业务数据也放到了es中。
ELK 全家桶越来越香。加入Beats套件后改名为
Elastic Stack。组件越来越多,功能也越来越强大。
- Elasticsearch
- Kibana
- Logstash
- Beats
- ECE
如何选择数据库
大致介绍完各个存储系统。我们回到最初的问题上,具体业务应该如何选择数据库?
- MySQL 稳定业务,不涉及分库分表,用到事务
- Hbase 用户加上资源维度,数据预期超过100亿
- MongoDB 业务需求多变,数据不到hbase级别但是可能存在分库分表的预期,无事务(mongodb g
- Codis 缓存,少量高并发的过期数据(配合elk使用)
- ELK 搜索;业务日志