1、数据库存存储性能优化
在mysql的文章专题中我写过了关于传统关系型数据库的一些优化思路,整体来说,通过优化之后能够提升程序访问数据库的计算性能。但是还是有一些情况,即便是优化之后,使用传统关系型数据库无法解决的,比如。
- 当数据量达到TB级别时,传统关系型数据库基本做了分库分表,单表数据量也是非常大的。
- 对于一些不适合用关系型数据库存储的数据,传统数据库无法做到,所以数据库本身的特性限制了多样性数据的管理。
1.1、K-V数据库
key-value分布式存储系统查询速快、存放数据量大、支持高并发,非常适合通过主键进行查询,但不能进行复杂的条件查询。
如果辅以实时搜索引擎进行复杂条件检索、全文检索,就可以替代并发性能较低的MySQL等关系型数据库,达到高并发、高性能,节省几十倍服务器数 量的目的。以MemcacheDB、Tokyo Tyrant为代表的key-value分布式存储,在上万并发连接下,轻松地完成高速查询。
1.2、列式数据
1.2.1、列式数据库介绍
列式数据库是以列相关存储架构进行数据存储的数据库,主要适合与批量数据处理和即席查询。相对应的是行式数据库,数据以行相关的存储体系架构进行空间分配,主要适合与小批量的数据处理,常用于联机事务型数据处理。列式数据库以行、列的二维表的形式存储数据,但是却以一维字符串的方式存储,例如以下的一个表:
EmpId |
Lastname |
Firstname |
Salary |
---|---|---|---|
1 | Smith | Joe | 40000 |
2 | Jones | Mary | 50000 |
3 | Johnson | Cathy | 44000 |
这个简单的表包括员工代码(EmpId), 姓名字段(Lastname and Firstname)及工资(Salary).
这个表存储在电脑的内存(RAM)和存储(硬盘)中。虽然内存和硬盘在机制上不同,电脑的操作系统是以同样的方式存储的。数据库必须把这个二维表存储在一系列一维的“字节”中,又操作系统写到内存或硬盘中。
行式数据库把一行中的数据值串在一起存储起来,然后再存储下一行的数据,以此类推。
1,Smith,Joe,40000;2,Jones,Mary,50000;3,Johnson,Cathy,44000;
列式数据库把一列中的数据值串在一起存储起来,然后再存储下一列的数据,以此类推。
1,2,3;Smith,Jones,Johnson;Joe,Mary,Cathy;40000,50000,44000;
这是一个简化的说法。
列式数据库的代表包括:Sybase IQ,ParAccel, Sand/DNA Analytics和 Vertica。
1.2.2、列式数据库与行式数据库
传统的行式数据库,是按照行存储的,维护大量的索引和物化视图无论是在时间(处理)还是空间(存储)方面成本都很高。而列式数据库恰恰相反,列式数据库的数据是按照列存储,每一列单独存放,数据即是索引。只访问查询涉及的列,大大降低了系统I/O,每一列由一个线来处理,而且由于数据类型一致,数据特征相似,极大方便压缩。
最后卢东明很务实的指出,没有万能的数据库,Sybase IQ也并非万能,只不过给DBA们的工具箱里提供更多的选择,DBA需根据自己的应用场景自行选择。
在数据仓库领域,列式数据库和传统的行式数据库有何差别呢?列式数据库和行式数据库的拥护者均认为结合这两种技术的混合数据库是个不错的想法。
行式数据库擅长随机读操作,列式数据库则更擅长大批量数据量查询,而混合数据库则试图同时包含这两种技术的优点,在灾难恢复环境中,数据要么按列式存储,要么按行式存储。
未来将是列式数据库的天下,行数据库和混合型数据库都将渐渐消亡,原因如下:数据增长速度很快,对存储设备(主内存和SSD)的需求也将不断上升,随着主内存和SSD中压缩列所占百分比的提高,列式数据库随机读的弱点反而变成了优点,这样列式数据库不管是应付联机事务处理,还是大批量更新或大型报表需要执行的复杂查询都能应付自如。对于更新操作而言,列式数据库和行式数据库在这方面已经没有多大差距了,因为大部分更新操作只会影响到一行中的一到三列(字段),同时,大部分更新操作影响的是最近的数据,因此主内存/SSD缓存中数据会越来越多。对于插入和删除操作而言,先在内存中快速更新索引,然后再写入磁盘,这意味着在I/O密集型情况下也不会有明显的性能下降。对在线备份而言,按列存储方法压缩数据后备份时间窗口将会更短。
对今天的数据仓库而言,列式数据库的性能和传统行数据库相比,根本不在一个数量级上,列式数据库已经得到了广泛的认可和使用(Sybase IQ已经有十年历史,也出现了一些新兴列式数据库公司,如Vertica),数据库巨人Oracle也按捺不住,在其Exadata数据库机中也加入了按列存储选择,IBM则提供了一个列式专用设备,它可以确定什么数据该按列存储,什么数据该按行存储,然后将事务分流到相应的设备。
1.2.3、列式数据库优缺点
列式数据库从一开始就是面向大数据环境下数据仓库的数据分析而产生,它跟行式数据库相比当然也有一些前提条件和优缺点.
列式数据库优点:
- 极高的装载速度 (最高可以等于所有硬盘IO 的总和,基本是极限了)
- 适合大量的数据而不是小数据
- 实时加载数据仅限于增加(删除和更新需要解压缩Block 然后计算然后重新压缩储存)
- 高效的压缩率,不仅节省储存空间也节省计算内存和CPU.
- 非常适合做聚合操作.
缺点:
- 不适合扫描小量数据
- 不适合随机的更新
- 批量更新情况各异,有的优化的比较好的列式数据库(比如Vertica)表现比较好,有些没有针对更新的数据库表现比较差.
- 不适合做含有删除和更新的实时操作.
1.2.4、常见误区
一个常见的误区认为如果每次扫描较多行或者全列全表扫描的时候,行式数据库比列式数据库更有优势. 事实上这只是行式数据库认识上的一个误区,即认为列式数据库的主要优势在于其列分开储存,而忽略了列式数据库上面提到的其他几大特征,这个才是列式数据库高性能的核心.
1.3、文档型数据库
MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。他支持的数据结构非常松散,是类似json的bjson格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。
整体架构:
内部架构:
它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:
-
面向集合存储,易存储对象类型的数据。
-
模式自由。
-
支持动态查询。
-
支持完全索引,包含内部对象。
-
支持查询。
-
支持复制和故障恢复。
-
使用高效的二进制数据存储,包括大型对象(如视频等)。
-
自动处理碎片,以支持云计算层次的扩展性
-
支持RUBY,PYTHON,JAVA,C++,PHP等多种语言。
-
文件存储格式为BSON(一种JSON的扩展)
-
可通过网络访问
所谓“面向集合”(Collenction-Orented),意思是数据被分组存储在数据集中,被称为一个集合(Collenction)。每个 集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定 义任何模式(schema)。
模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。
存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各中复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized dOcument Format)。
MongoDB服务端可运行在Linux、Windows或OS X平台,支持32位和64位应用,默认端口为27017。推荐运行在64位平台,因为MongoDB
在32位模式运行时支持的最大文件尺寸为2GB。
MongoDB把数据存储在文件中(默认路径为:/data/db),为提高效率使用内存映射文件进行管理。
1.4、NewSql
很多企业都需要处理来自多个数据源的复杂数据,因此需要利用数据库管理系统来存储和管理数据。现在,有很多不同类型的数据库系统,而找到一个正确的数据库不是一件容易的事。这需要考虑业务需求,以便选择正确的数据库管理系统。
企业使用关系数据库管理系统(RDBMS)来存储和管理他们的数据。然而,这些系统无法处理海量的复杂数据,无法满足当今数据库用户的需求。因此,很多企业都在寻找能够在保持传统数据库系统重要特性的同时,也能提供可伸缩性、可用性和高性能的数据库系统。数据库软件供应商也已经确定了这一需求,并开发了NewSQL数据库来弥补传统数据库系统的缺陷。
NewSQL数据库在不放弃传统数据库优势的前提下解决了问题。NewSQL关系数据库管理系统为OLTP提供了与NoSQL系统相同的可伸缩性能,在保证传统数据库系统的ACID的同时,也能处理在线事务的读写工作负载。NoSQL数据库使用的数据结构与关系数据库中使用的数据结构不同,这使得NoSQL数据库中的某些操作更快。
1.4.1、什么是 NewSQL 数据库
NewSQL数据库是现代SQL数据库,它解决了与传统联机事务处理(OLTP)RDBMS相关的一些主要问题。它们在保持传统数据库管理系统优点的同时,力求实现NoSQL数据库的可伸缩性和高性能。换句话说,NewSQL数据库是一种特殊的关系数据库系统,它结合了传统数据库OLTP 和NoSQL的高性能和可伸缩性。它们保持了传统DBMS的ACID(原子性、一致性、隔离性和持久性)。ACID事务特性确保了完整的业务流程、并发事务、系统故障或错误时的数据完整性,以及事务前后的一致性。
NewSQL数据库在内部设计方面有所不同,但它们都是运行在SQL上的rdbms。它们使用SQL来接收新信息,同时执行许多事务,并修改数据库的内容。NewSQL系统的主要包括新的技术架构、透明的数据分片中间件、SQL引擎和数据库即服务(DBaaS)。
分区/分片:几乎所有的NewSQL数据库管理系统都是通过将数据库划分为不同的子集(称为分区或分片)来扩展的。数据库中的表被水平地分割成几个分片,这些分片的边界基于列值来划分,来自不同表的相关片段被连接以创建分区。
副本: 此功能允许数据库用户创建和维护数据库以及副本。数据库的副本存储在与主站相连接的远程站点或距离很远的站点。用户可以同时更新副本,也可以更新一个节点并将结果状态转移到其他副本
辅助索引(二级索引): 辅助索引允许数据库用户通过使用主键以外的其他值有效地访问数据库记录。
并发控制: 此功能可解决多用户系统中当多个用户同时访问或修改数据时可能出现的问题。NewSQL系统使用此功能来确保同步事务,同时保持数据完整性。
故障恢复:NewSQL数据库有一种故障恢复的机制,使它们能够在系统崩溃时恢复数据到一致的状态。
其中一些好处包括:
- 数据库分区减少了系统的通信开销,从而可以轻松地访问数据。
- 即使出现系统故障或错误,ACID事务也可以确保数据的完整性。
- NewSQL数据库可以处理复杂的数据。
- NewSQL系统具有高度可伸缩性。
1.4.2、常用的NewSql
Vitess(开源)
Vitess 是一个分布式 MySQL 工具集,它可以自动分片存储 MySQL 数据表,将单个 SQL 查询改写为分布式发送到多个 MySQL Server 上,支持行缓存(比 MySQL 本身缓存效率高)与复制容错等。Vitess 4.0 中有许多改进,可以使新用户更容易使用,可以很容易的在k8s上部署Vitess,从单一的 MySQL 或 MariaDB 迁移到Vitess 成为可能,而应用对此有所感知。
CockroachDB(免费)
CockroachDB (蟑螂数据库)是一个可伸缩的、支持地理位置处理、支持事务处理的数据存储系统。CockroachDB 提供两种不同的的事务特性,包括快照隔离(snapshot isolation,简称SI)和顺序的快照隔离(SSI)语义,后者是默认的隔离级别。
蟑螂是一个分布式的K/V数据仓库,支持ACID事务,多版本值存储是其首要特性。主要的设计目标是全球一致性和可靠性,从蟑螂的命名上是就能看出这点。蟑螂数据库能处理磁盘、物理机器、机架甚至数据中心失效情况下最小延迟的服务中断;整个失效过程无需人工干预。蟑螂的节点是均衡的,其设计目标是同质部署(只有一个二进制包)且最小配置。CockroachDB 无需重新配置,也无需进行大规模的架构大修 就可以水平扩展,只需在集群中添加一个新节点就可以了,CockroachDB就会处理底层的复杂性。
- 只需向集群添加新节点即可进行扩展
- 自动平衡和分配分段的范围,不是分片
- 在所有节点上均匀地优化,以提升服务器利用率
TiDB (企业版收费)
TiDB 是一款定位于在线事务处理/在线分析处理( HTAP: Hybrid Transactional/Analytical Processing)的融合型数据库产品,实现了一键水平伸缩,强一致性的多副本数据安全,分布式事务,实时 OLAP 等重要特性。同时兼容 MySQL 协议和生态,迁移便捷,运维成本极低。但是从使用情况来看,它对硬件要求较高。目前也分社区版和企业版。
ClustrixDB(收费)
MariaDB 2018年收购了ClustrixDB,它目前是一个类MYSQL的关系数据库, 可以很容易的从MySQL迁移 到ClustrixDB。ClustrixDB与MySQL客户机兼容,但与mazon Aurora不同,它是分布式的,可以扩展写操作,并且不会产生单独的IO和存储费用,它从底层就支持web、移动和物联网(IoT)等具有最极端的可扩展性要求的应用程序,并且是在不损害关键特性的情况下做到这一点,数据库需要为带有关键型任务应用程序提供对可靠数据访问服务:事务和SQL。
MemSQL(收费)
MemSQL最大的卖点就是性能,同时兼容兼容MySQL。MemSQL,2012年12月14日发布,是世界上最快的关系数据库,能实现每秒150万次事务。MemSQL是一个分布式的、高度可伸缩的SQL数据库,可以在任何地方运行。我们使用熟悉的关系模型为事务性和分析性工作负载提供最高性能.MemSQL是一个可扩展的SQL数据库,它不断地吸收数据,为您的业务一线执行操作分析。使用ACID事务每秒接收数百万个事件,同时以关系SQL、JSON、地理空间和全文搜索格式分析数十亿行数据。
NuoDB(商用需要授权)
NuoDB 是世界上首个也是唯一一个具有专利的、弹性可伸缩的SQL关系数据库,主要用于去集中化的计算资源。我们完全从头开始设计这么一个全新的数据库,100% ACID 保证以及兼容 SQL 标准规范。支持复杂数据库管理任务如分区、缓存集群和性能调优等等。
Altibase(商用收费)
ALTIBASE 数据库完美结合了,并提出了一个新概念---Hybrid DBMS。ALTIBASE 提供高性能、容错能力和事务管理的方便性,特别是在通信、网上银行、证券交易、实时应用和嵌入式系统领域。
VoltDB(商用收费)
VoltDB是一个内存中的开源OLTP SQL数据库,能够保证事务的完整性(ACID)。它是Postgres和Ingres联合创始人Mike Stonebraker领导开发的下一代开源数据库管理系统。它能在现有的廉价服务器集群上实现每秒数百万次数据处理。VoltDB大幅降低了服务器资源 开销,单节点每秒数据处理远远高于其它数据库管理系统。不同于NoSQL的key-value储存,VoltDB能使用SQL存取,支持传统数据库的 ACID模型。VoltDB代码采用GPLv3授权,支持订阅费用从15,000美元起步。
Citus(商用收费)
Citus面向高速简单的事务,高吞吐量批量加载以及高速亚秒级分析查询。它还集成了cstore/hll等很多插件。一个限制是在某些情况下它不支持所有SQL查询或复杂事务。CitusDB采用PostgreSQL的插件形式(not a fork),即享受PostgreSQL的强大支持,又同时拥有分布式数据库能力。Citus的企业版是要收费的。
2、K-V数据库之Redis
在写这个中间件前想了很多,因为现在一毕业的人你一问他缓存,他都知道用Redis,从09年出现Redis到现在,这玩意已经很成熟和大众化了,所以在规划中间件文章时想了很久要怎么写才能写出一些新的识知的东西,思考很久后我想了想,正是因为会很的人都会把东西直接放Redis中,如果不明白什么东西应该放Redis中什么不该放,如果不明白怎么合理使用Redis中提供的不同的数据类型及规划Redis的使用,那么这种盲目的使用其实对系统来说并不见得是对性能的提升反而是一场灾难。
2.1、Redis概述
什么是Nosql:Nosql叫做非关系型数据库,为了解决高并发、高可用、高可扩展,大数据存储等一系列问题而产生的数据库解决方案。
Redis是使用ANSI C语言开发的一个高性能Key-Value数据库,是当今速度最快的内存型非关系型(NoSQL)数据库,可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。
Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能,使用分片来扩展写性能。
2.2、Redis数据类型说明
2.2.1、String类型
- get:获取key对应的vlaue
- set:为一个key设置value,可配合ex/px参数设置key的有效期
- getset:为一个Key设置value,并返回该key的原value
- mget:获取多个key对应的value
- mset:为多个key设置value
- incr/incrby:将key对应的value自增1(或者指定的整型数值),并返回自增后的值。
- decr/decrby:同上,自减
- del:删除指定key
- setex:设置指定key的过期时间,单位为秒
- setnx:将key的值设置为value,如果key存在,返回0不做任何处理,否则返回1
应用场景:String是最常用的一种数据类型,普通的key/value存储都可以化为此类,即可以完全实现目前Memcached的功能,并且效率更高。还具有Redis的持久化,操作日志以及Replication等功能。除了和Memcached一样有get set incr decr等操作外,Redis还具有如下操作:
- 获取字符串长度
- 对字符串append内容
- 设置和获取字符串的某一段内容
- 设置和获取字符串的某一位bit
- 批量设置一系列字符串的内容
使用场景:常规key-value缓存应用。常规计数:微博数、粉丝数。
实现方式:String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr ,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int
2.2.2、Hash 散列类型
key-value ,单点登录时采用这种数据结构存储用户信息,key为cookieId,value存放结构化对象。常用命令:
- hset:将Key对应的hash中的field设置为value。如果该hash不存在,会自动创建一个。
- hget:返回指定hash中field字段的值。
- hmset/hmget:同hset和hget,可以批量操作同一个key下的多个field。
- hgetall:谨慎使用,为完整遍历,耗时!—以数组形式返回哈希表中,所有的字段和值。紧跟每个字段名(field name)之后是字段的值 (value),所以返 回值的长度是哈希表大小的两倍。
用户ID为查找的key,存储的value用户对象包含姓名、年龄、生日等信息,如果用普通的key/value结构来存储,主要有两种方式:
使用场景:存储部分变更数据,如用户信息。
实现方式:Redis的Hash数据类型对应的Value内部实际就是一个HashMap,有两种不同实现: 当hash成员比较少时,Redis为了节省内存会采用类似一维数组的方式来紧凑存储,而不会采用真正的HashMap结构,此时对应的value redisObject的encoding为zipmap。 当成员数量增大时,会自动转成真正的HashMap,此时的encoding为ht。
2.2.3、List类型
常用命令:
- LPOP key :移出并获取列表的第一个元素
- RPOP key :移除列表的最后一个元素,返回值为移除的元素。
- LPUSH key value1 [value2] :将一个或多个值插入到列表头部
- RPUSH key value1 [value2] :在列表中添加一个或多个值
- LRANGE key start stop: 获取列表指定范围内的元素
应用场景:Redis list的应用场景非常多,也是redis最重要的数据结构之一,比如twitter的关注列表,粉丝列表等都可以用redis的list结构来实现。
List就是链表,使用List结构,我们可以实现最新消息排行等功能。List的另一个应用就是消息队列。
可以利用List的PUSH操作,将任务存在List中,然后工作线程再用POP操作将任务取出来进行执行。Redis还提供了操作List中某一段的api,可以直接查询,删除List中某一段的元素。
实现方式:Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过会带来额外内存开销,Redis内部的很多实现,包括发送缓冲队等也是采用这个数据结构。
Redis的list是每个子元素都是String类型的双向链表,可以通过push和pop操作从列表的头部或者尾部添加或者删除元素,这样list既可以作为栈又可以作为队列。
使用场景:消息队列
使用list可以构建消息队列系统。 比如将Redis 用作日志收集器,实际上还是一个队列,多个端点将日志信息写入Redis,然后一个worker统一将所有日志写到磁盘。取出最新的N个数据的操作。记录前N个最新登陆的用户ID列表,超出范围的可以从数据库取出。
2.2.4、Set类型
Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。 Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。
为什么不用JVM自带的Set进行去重: 一般我们的系统都是集群部署,那么使用JVM自带的set比较麻烦,需要再起一个公共服务。
常见命令:
sadd
spop
smembers
sunion
应用场景:set对外提供的功能和list类似,但是set具有自动排重功能。并且set提供了判断某个成员是否在一个set集合内的重要接口,这要是list所不能提供的。
案例:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能,对上面的所有集合操作,你还可以使用不同的命令选择将结果返回给客户端还是存到一个新的集合中。Set是集合,是String类型的无序集合,set是通过hashtable实现的,概念和数学中个的集合基本类似,可以交集,并集,差集等等,set中的元素是没有顺序的。
实现方式:set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
2.2.5、ZSet类型
zadd:向有序集合zset中增加一个元素。例如:zadd zset 1 Hello
zrange:输出zset中指定范围内的的所有元素,按照score从小到大的顺序。例如:zrange zset 0 -1 withscores,表示输出有序集合zset中所有的元素,并且输出顺序号。
zrem:删除名称为key的zset集合中的元素member。例如:zrem set1 Hello。
zincrby:把有序集合zset中的指定元素的序列号加上指定的值。例如:zincrby zset 4 Hello
zrank:返回名称为key的zset集合中元素的排名,按照下标从小到大的排名。例如:zrank zset Hello。
zrevrank:返回名称为key的zset集合中元素的排名,按照下表从大到小的排名。例如:zrevrank zset Hello。
zrevrange:按照score从大到小的顺序输出集合中所有元素。例如:zrevrange zset 0 -1 withscores。
zrangebyscore:返回集合中给定区间的元素。例如:zrangebyscore zset 2 3 withscores
zcount:返回集合中score在给定区间的数量。zcount zset 2 3
zcard:返回集合中元素的个数。
zremrangebyrank:按照索引来删除给定区间的元素。例如:zremrangebyrank zset 1 2
zremrangebyscore:删除score在指定范围内的元素。例如:zremrangebyscore zset 1 2
2.2.6、Geospatial
2.2.7、HyperLogLog
- pfadd ,Redis Pfadd 命令将所有元素参数添加到 HyperLogLog 数据结构中。
- pfcount,Redis Pfcount 命令返回给定 HyperLogLog 的基数估算值。
- pgmerge,Redis Pgmerge 命令将多个 HyperLogLog 合并为一个 HyperLogLog ,合并后的HyperLogLog 的基数估算值是通过对所有 给定 HyperLogLog 进行并集计算得出的。
2.2.8、Bit
常见命令:
getbit 指令:获取到 key 对应的 value 在 offset 处的 bit 值。
setbit 指令:修改 key 对应的 value 在 offset 处的 bit 值
bitcount 指令:统计二进制数据中 1 的个数