1.前言
老是聊并发的话题,聊到你们都免疫了,因此此次串讲下个话题——数据库(欢迎纠正补充)html
看完问本身一个问题来自我检测:NoSQL我到底该怎么选?git
数据库排行:https://db-engines.com/en/rankinggithub
1.1.分类
主要有这么三大类:(再老的数据库就不说了)redis
1.传统数据库(SQL):
- 关系数据库:SQLite、MySQL、SQLServer、PostgreSQL、Oracle...
2.高并发产物(NoSQL):
- 键值数据库:Redis、MemCached...
- PS:如今
LevelDB
和RocksDB
也是比较高效的解决方案
- PS:360开发的
pika
(redis的补充)能够解决Redis容量过大的问题
- 文档数据库:MongoDB、ArangoDB、CouchBase、CouchDB、RavenDB...
- PS:Python有一款基于json存储的轻量级数据库:
tinydb
- 列式数据库:Cassandra、HBase、BigTable...
- 搜索引擎系:Elasticsearch、Solr、Sphinx...
- PS:这几年用Rust编写的轻量级搜索引擎
sonic
很火
- 图形数据库:Neo4J、Flockdb、ArangoDB、OrientDB、Infinite Graph、InfoGrid...
- PS:基于Redis有一款图形数据库用的也挺多:
RedisGraph
- PS:随着Go的兴起,这些图形数据库很火:Cayley、
Dgraph
、Beam
PS:项目中最经常使用的其实就是Redis
、Elasticsearch
、MongoDB
算法
PS:ArangoDB
是一个原生的多模型数据库,具备文档,图形和键值的灵活数据库sql
3.新时代产物(TSDB):
- 时序数据库:InfluxDB、LogDevice、Graphite、、OpenTSDB...
来看个权威的图:(红色的是推荐NoSQL,灰色是传统SQL)
数据库
1.2.概念
先说下NoSQL不是不要使用传统SQL了,而是不只仅是传统的SQL(not only sql)json
1.关系型数据库优劣
先看看传统数据库的好处:缓存
- 经过事务保持数据一致
- 能够Join等复杂查询
- 社区完善(遇到问题简单搜下就ok了)
固然了也有不足的地方:服务器
- 数据量大了的时候修改表结构。eg:加个字段,若是再把这个字段设置成索引那是卡到爆,彻底不敢在工做时间搞啊
- 列不固定就更蛋疼了,通常设计数据库不可能那么完善,都是后期愈来愈完善,就算本身预留了
保留字段
也不人性化啊
- 大数据写入处理比较麻烦,eg:
- 数据量不大还好,批量写入便可。
- 但是自己数据量就挺大的,进行了
主从复制
,读数据在Salver
进行到没啥事,可是大量写数据库怼到Master
上去就吃不消了,必须得加主数据库了。
- 加完又出问题了:虽然把主数据库一分为二,可是容易发生
数据不一致
(一样数据在两个主数据库更新成不同的值),这时候得结合分库分表,把表分散在不一样的主数据库中。
- 完了吗?NoNoNo,想想表之间的Join咋办?岂不是要跨数据库和跨服务器join了?简直就是拆东墙补西墙的节奏啊,因此各类中间件就孕育而生了【SQLServer这方面扩展的挺不错的,列存储也自带,也跨平台了(建议在Docker中运行)(点我查看几年前写的一篇文章)】
- 欢迎补充~(说句良心话,中小型公司
SQLServer
绝对是最佳选择,能省去不少时间)
2.NoSQL
如今说说NoSQL了:(其实你能够理解为:NoSQL就是对原来SQL的扩展补充)
- 分表分库的时候通常把关联的表放在同一台服务器上,这样便于join操做。而NoSQL不支持join,反而不用这么局限了,数据更容易分散存储
- 大量数据处理这块,读方面传统SQL并无太多劣势,NoSQL主要是进行缓存处理,批量写数据方面测试每每远高于传统SQL,并且NoSQL在扩展方面方便太多了
- 多场景类型的NoSQL(键值,文档、列、图形)
若是仍是不清楚到底怎么选择NoSQL,那就再详细说说每一个类型的特色:
- 键值数据库:这个你们很熟悉,主要就是
键值存储
,表明=>Redis(支持持久化和数据恢复,后面咱们会详谈)
- 文档数据库:表明=>MongoDB(优酷的在线评论就是用基于MongoDB的)
- 通常都不具有事务(
MongoDB 4.0
开始支持ACID
事务了)
- 不支持Join(Value是一个可变的
类JSON格式
,表结构修改比较方便)
- 列式数据库:表明:Cassandra、
HBase
- 对大量行少许列进行修改更新(新增一字段,批量作个啥操做的不要太方便啊~针对列为单位读写)
- 扩展性高,数据增长也不会下降对应的处理速度(尤为是写)
- 搜索引擎系:表明:Elasticsearch,太经典了不用说了(传统模糊搜索只能like太low,因此有了这个)
- 图形数据库:表明:Neo4J、Flockdb、ArangoDB(数据模型是图结构的,主要用于 关系比较复杂 的设计,好比绘制一个QQ群关系的可视化图、或者绘制一个微博粉丝关系图等)

回头仍是要把并发剩余几个专题深刻的,认真看的同志会发现无论什么语言底层实现都是差很少的。
好比说进程,其底层就是用到了咱们第一讲说的OS.fork
。再说进(线)程通讯,讲的PIPE、FIFO、Lock、Semaphore
等不多用吧?可是Queue
底层就是这些实现的,不清楚的话怎么读源码?
还记得当时引入Queue篇
提到Java里的CountDownLatch
吗?要是不了解Condition
怎么本身快速模拟一个Python里面没有的功能呢?
知其然不知其因此然是万万不可取的。等后面讲MQ的时候又得用到Queue
的知识了,可谓一环套一环~
既然不是公司的萌妹子,因此呢~技术的提高仍是得靠本身了^_^,先到这吧,最后贴个经常使用解决方案:
Python、NetCore经常使用解决方案(持续更新)
https://github.com/LessChina
2.概念
上篇提到了ACID
此次准备说说,而后再说说CAP
和数据一致性
2.1.ACID事务
以小明和小张转帐的例子继续说说:
- A:原子性(Atomic)
- 小明转帐1000给小张:小明-=1000 => 小张+=1000,这个 (事务)是一个不可分割的总体,若是小明-1000后出现问题,那么1000得退给小明
- C:一致性(Consistent)
- 小明转帐1000给小张,必须保证小明+小张的总额不变(假设不受其余转帐(事务)影响)
- I:隔离性(Isolated)
- 小明转帐给小张的同时,小潘也转钱给了小张,须要保证他们相互不影响(主要是并发状况下的隔离)
- D:持久性(Durable)
- 小明转帐给小张银行要有记录,即便之后扯皮也能够拉流水帐【事务执行成功后进行的持久化(就算数据库以后挂了也能经过Log恢复)】
2.2.CAP概念
CAP是分布式系统须要考虑的三个指标,数据共享只能知足两个而不可兼得:

- C:一致性(Consistency)
- 全部节点访问同一份最新的数据副本(在分布式系统中的全部数据备份,在同一时刻是否一样的值)
- eg:分布式系统里更新后,某个用户都应该读取最新值
- A:可用性(Availability)
- 在集群中一部分节点故障后,集群总体是否还能响应客户端的读写请求。(对数据更新具有高可用性)
- eg:分布式系统里每一个操做总能在必定时间内返回结果(超时不算【网购下单后一直等算啥?机房挂几个服务器也不影响】)
- P:分区容错性(Partition Toleranc)
- 以实际效果而言,分区至关于对通讯的时限要求。系统若是不能在时限内达成数据一致性,就意味着发生了分区的状况,必须就当前操做在C和A之间作出选择。
- eg:分布式系统里,存在网络延迟(分区)的状况下依旧能够接受知足一致性和可用性的请求
CA
表明:传统关系型数据库
若是想避免分区容错性问题的发生,一种作法是将全部的数据(与事务相关的)都放在一台机器上。虽然没法100%保证系统不会出错,但不会碰到由分区带来的负面效果(会严重的影响系统的扩展性)
做为一个分布式系统,放弃P,即至关于放弃了分布式,一旦并发性很高,单机服务根本不能承受压力。像不少银行服务,确确实实就是舍弃了P,只用高性能的单台小型机保证服务可用性。(全部NoSQL数据库都是假设P是存在的)
CP
表明:Zookeeper
、Redis
(分布式数据库、分布式锁)
相对于放弃“分区容错性“来讲,其反面就是放弃可用性。一旦遇到分区容错故障,那么受到影响的服务须要等待数据一致
(等待数据一致性期间系统没法对外提供服务)
AP
表明:DNS数据库
(IP和域名相互映射的分布式数据库
,联想修改IP后为何TTL
须要10分钟左右保证全部解析生效)

反DNS查询:http://www.javashuo.com/article/p-uvuapttc-s.html
放弃强一致,保证最终一致性。全部的NoSQL
数据库都是介于CP
和AP
之间,尽可能往AP
靠,(传统关系型数据库注重数据一致性,而对海量数据的分布式处理来讲可用性和分区容错性优先级高于数据一致性)eg:
不一样数据对一致性要求是不同的,eg:
- 用户评论、弹幕这些对一致性是不敏感的,很长时间不一致性都不影响用户体验
- 像商品价格等等你敢来个看看?对一致性是很高要求的,容忍度铁定低于10s,就算使用了缓存在订单里面价格也是最新的(平时注意下JD商品下面的缓存说明,JD尚且如此,其余的就不用说了)

2.3.数据一致性
传统关系型数据库通常都是使用悲观锁的方式,可是例如秒杀这类的场景是hou不动,这时候每每就使用乐观锁了(CAS机制,以前讲并发和锁的时候提过),上面也稍微提到了不一样业务需求对一致性有不一样要求而CAP不能同时知足,这边说说主要就两种:
- 强一致性:不管更新在哪一个副本上,以后对操做都要可以获取最新数据。多副本数据就须要
分布式事物来保证数据一致性
了(这就是问什么项目里面常常提到的缘由)
- 最终一致性:在这种约束下保证用户最终能读取到最新数据。举几个例子:
- 因果一致性:A、B、C三个独立进程,A修改了数据并通知了B,这时候B获得的是最新数据。由于A没通知C,因此C不是最新数据
- 会话一致性:用户本身提交更新,他能够在会话结束前获取更新数据,会话结束后(其余用户)可能不是最新的数据(提交后JQ修改下本地值,不能保证数据最新)
- 读自写一致性:和上面差很少,只是不局限在会话中了。用户更新数据后他本身获取最新数据,其余用户可能不是最新数据(必定延迟)
- 单调读一致性:用户读取某个数值,后续操做不会读取到比这个数据还早的版本(新的程度>=读取的值)
- 单调写一致性(时间轴一致性):全部数据库的全部副本按照相同顺序执行全部更新操做(有点像
Redis
的AOF
)
2.4.一致性实现方法
Quorum
系统NRW
策略(经常使用)
Quorum是集合A,A是全集U的子集,A中任意取集合B、C,他们二者都存在交集。
NRW算法:
- N:表示数据所具备的副本数。
- R:表示完成读操做所须要读取的最小副本数(一次读操做所需参与的最小节点数)
- W:表示完成写操做所须要写入的最小副本数(一次写操做所须要参与的最小节点数)
- 只须要保证
R + W > N
就能够保证强一致性(读取数据的节点和被同步写入的节点是有重叠的)好比:N=3,W=2,R=2(有一个节点是读+写)
扩展:
- 关系型数据库中,若是N=2,能够设置W=2,R=1(写耗性能一点),这时候系统须要两个节点上数据都完成更新才能确认结果并返回给用户
- 若是
R + W <= N
,这时候读写不会在一个节点上同时出现,系统只能保证最终一致性。副本达到一致性的时间依赖于系统异步更新的方式,不一致性时间=从更新节点~全部节点都异步更新完毕的耗时
- R和W设置直接影响系统的性能、扩展和一致性:
- 若是W设置为1,那么一个副本更新完就返回给用户,而后经过异步机制更新剩余的N-W个节点
- 若是R设置为1,只要有一个副本被读取就能够完成读操做,R和W的值若是较小会影响一致性,较大会影响性能
- 当W=1,R=N==>系统对写要求高,但读操做会比较慢(N个节点里面有1个挂了,读就完成不了了)
- 当R=1,W=N==>系统对读操做有高要求,但写性能就低了(N个节点里面有1个挂了,写就完成不了了)
- 经常使用方法:通常设置
R = W = N/2 + 1
,这样性价比高,eg:N=3,W=2,R=2(3个节点==>1写,1读,1读写
)
参考文章:
http://book.51cto.com/art/201303/386868.htm
https://blog.csdn.net/jeffsmish/article/details/54171812
时间轴策略(经常使用)
- 主要是关系型数据库的日记==>记录事物操做,方便数据恢复
- 还有就是并行数据存储的时候,因为数据是分散存储在不一样节点的,对于同一节点来讲只要关心
数据更新+消息通讯
(数据同步):
- 保证较晚发生的更新时间>较早发生的更新时间
- 消息接收时间 > 消息发送时刻的时间(要考虑服务器时间差的问题~时间同步服务器)
其余策略
其实还有不少策略来保证,这些概念的对象逆天不是很熟~好比:向量时钟策略
推荐几篇文章
:
https://www.cnblogs.com/yanghuahui/p/3767365.html
http://blog.chinaunix.net/uid-27105712-id-5612512.html
https://blog.csdn.net/dellme99/article/details/16845991
https://blog.csdn.net/blakeFez/article/details/48321323