NoSQL(Not Only SQL),泛指非关系型的数据库(mysql、oracle、sqlserver都是关系型数据库)。html
数据之间无关系,随意扩展node
数据存储简单,能够存在内存中,读写速度快mysql
不须要建表、字段。自定义格式redis
分类算法 |
Examples举例sql |
典型应用场景数据库 |
数据模型segmentfault |
优势数组 |
缺点缓存 |
键值(key-value) |
Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB,memcache |
内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。 |
Key 指向 Value 的键值对,一般用hash table来实现 |
查找速度快 |
数据无结构化,一般只被看成字符串或者二进制数据 |
列存储数据库 |
Cassandra, HBase, Riak |
分布式的文件系统 |
以列簇式存储,将同一列数据存在一块儿 |
查找速度快,可扩展性强,更容易进行分布式扩展 |
功能相对局限 |
文档型数据库 |
CouchDB, MongoDb |
Web应用(与Key-Value相似,Value是结构化的,不一样的是数据库可以了解Value的内容) |
Key-Value对应的键值对,Value为结构化数据 |
数据结构要求不严格,表结构可变,不须要像关系型数据库同样须要预先定义表结构 |
查询性能不高,并且缺少统一的查询语法。 |
图形数据库 |
Neo4J, InfoGrid, Infinite Graph |
社交网络,推荐系统等。专一于构建关系图谱 |
图结构 |
利用图结构相关算法。好比最短路径寻址,N度关系查找等 |
不少时候须要对整个图作计算才能得出须要的信息,并且这种结构不太好作分布式的集群方案。 |
不须要预约义模式:不须要事先定义数据模式,预约义表结构。数据中的每条记录均可能有不一样的属性和格式。当插入数据时,并不须要预先定义它们的模式。
无共享架构:相对于将全部数据存储的存储区域网络中的全共享架构。NoSQL每每将数据划分后存储在各个本地服务器上。由于从本地磁盘读取数据的性能每每好于经过网络传输读取数据的性能,从而提升了系统的性能。
弹性可扩展:能够在系统运行的时候,动态增长或者删除结点。不须要停机维护,数据能够自动迁移。
分区:相对于将数据存放于同一个节点,NoSQL数据库须要将数据进行分区,将记录分散在多个节点上面。而且一般分区的同时还要作复制。这样既提升了并行性能,又能保证没有单点失效的问题。
异步复制:和RAID存储系统不一样的是,NoSQL中的复制,每每是基于日志的异步复制。这样,数据就能够尽快地写入一个节点,而不会被网络传输引发迟延。缺点是并不老是能保证一致性,这样的方式在出现故障的时候,可能会丢失少许的数据。
BASE:相对于事务严格的ACID(原子性,一致性,隔离性,持久性)特性,NoSQL数据库保证的是BASE(BA——基本可用,S——软状态,柔性事务,E——最终一致性)特性。【注】CAP原理(C——一致性,A——可用性,P——分区容错性,当前NoSQL大部分知足了AP原理)
NoSQL数据库在如下的这几种状况下比较适用:一、数据模型比较简单;二、须要灵活性更强的IT系统;三、对数据库性能要求较高;四、不须要高度的数据一致性;五、对于给定key,比较容易映射复杂值的环境。
MemCache是一个开源的高性能的分布式的内存对象缓存系统,用于各类动态应用以减轻数据库负担。它经过在内存中缓存数据和对象,来减小读取数据库的次数,从而提升动态、数据库驱动应用速度。MemCache会在内存中开辟一块空间,创建一个统一的巨大的hash表,hash表可以用来存储各类格式的数据,包括图像、视频、文件以及数据库检索的结果等。
【注】MemCache 和 MemCached:MemCache是这个项目的名称,而MemCached是服务器端的主程序名称。
一般在访问量高的Web网站和应用中使用MemCache,用来缓解数据库的压力,而且提高网站和应用的响应速度。
在应用程序中,咱们一般在如下节点来使用MemCached:
经常使用工做流程(以下图):
缓存到MemCached中的数据库数据,在更新数据库时要同时更新MemCached。
MemCached采用了C/S架构,在Server端启动后,以守护程序的方式,监听客户端的请求。启动时能够指定监听的IP(服务器的内网ip/外网ip)、端口号(因此作分布式测试时,一台服务器上能够启动多个不一样端口号的MemCached进程)、使用的内存大小等关键参数。一旦启动,服务就会一直处于可用状态。
为了提升性能,MemCached缓存的数据所有存储在MemCached管理的内存中,因此重启服务器以后缓存数据会清空,不支持持久化。
1. 内存结构
2. 内存分配方式
MemCache中的value存放位置是由value的大小决定,value会被存放到与chunk大小最接近的一个slab中,好比slab[1]的chunk大小为88字节、slab[2]的chunk大小为112字节、slab[3]的chunk大小为144字节(默认相邻slab内的chunk基本以1.25为比例进行增加,MemCache启动时能够用-f指定这个比例),那么一个100字节的value,将被放到2号slab中。
3. 内存回收方式
针对MemCache的内存分配及回收算法,总结三点:
为了提高MemCached的存储容量和性能,咱们应用的客户端可能会对应多个MemCached服务器来提供服务,这就是MemCached的分布式。
1. 分布式实现原理
MemCached的目前版本是经过C实现,采用了单进程、多线程、异步I/O,基于事件(event_based)的服务方式.使用libevent做为事件通知实现。多个Server能够协同工做,但这些 Server 之间保存的数据各不相同,并且并不通讯(与之造成对比的,好比JBoss Cache,某台服务器有缓存数据更新时,会通知集群中其余机器更新缓存或清除缓存数据),每一个Server只是对本身的数据进行管理。
Client端经过IP地址和端口号指定Server端,将须要缓存的数据是以key->value对的形式保存在Server端。key的值经过hash进行转换,根据hash值把value传递到对应的具体的某个Server上。当须要获取对象数据时,也根据key进行。首先对key进行hash,经过得到的值能够肯定它被保存在了哪台Server上,而后再向该Server发出请求。Client端只须要知道保存hash(key)的值在哪台服务器上就能够了。
2. 分布式算法解析
MemCached网络模型是典型的单进程多线程模型,采用libevent处理网络请求,主进程负责将新来的链接分配给work线程,work线程负责处理链接,有点相似与负载均衡,经过主进程分发到对应的工做线程。
MemCached默认有7个线程,4个主要的工做线程,3个辅助线程,线程可划分为如下4种:
Redis是一个key-value存储系统。和Memcached相似,它支持存储的value类型相对更多,包括string(字符串)、 list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操做,并且这些操做都是原子性的。在此基础上,redis支持各类不一样方式的排序。与memcached同样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操做写入追加的记录文件,而且在此基础上实现了master-slave(主从)同步,当前 Redis的应用已经很是普遍,国内像新浪、淘宝,国外像 Flickr、Github等均在使用Redis的缓存服务。
Redis支持丰富的数据类型,并提供了大量简单高效的功能。Redis的底层数据结构总览图:
上图列出了Redis内部底层的一些重要数据结构,包括List, Set, Hash, String等。
1. Redis Object
redisObject定义了类型,编码方式,LRU时间,引用计数,*ptr指向实际保存值指针。
type: redisObject的类型,字符串,列表,集合,有序集,哈希表等
encoding: 底层实现结构,字符串,整数,跳跃表,压缩列表等
ptr:实际指向保存值的数据结构
举个具体例子,redisObject{type: REDIS_LIST, encoding:REDIS_ENCODING_LINKEDLIST},
这个对象是Redis列表,其值保存在一个链表中,ptr指针指向这个列表。Redis本身实现对象管理机制,并基于引用计数的垃圾回收。Redis提供了incrRefCount与decrRefCount来管理对象跟踪对象的引用, 当减小引用时检测计数器为是否须要释放内存对象。
2. RedisDB
RedisDB内部数据结构,封装了数据库层面的信息:
从redisDB来看,几个重要属性:
id:数据库内部编号,仅供内部操做使用,如AOF等
dict:存放整个数据库的键值对,键为字符串,值为Redis的数据结构,如List, Set, Hash等。
expires:键的过时时间
3. RedisServer
RedisServer代码在Redis 3.0支持Cluster后变得较复杂,整体来讲包含以下几个大部分:
通用部分:如pid进程id,数据库指针,命令字典表,Sentinel模式标志位等
网络信息:如port TCP监听端口,Cluster Bus监听socket, 已使用slot数量,Active的客户端列表, 当前客户端,Slaves列表等。
其它信息:如AOF信息,统计信息, 配置信息(如已经配置总db数量dbnum等),日志信息,Replication配置,Pub/Sub, Cluster信息,Lua脚本信息配置等等。
4. Redis Hash
Redis的哈希表/字典是其核心数据结构之一,值得深刻研究。Redis Hash数据结构, Hash新建立时,在不影响效率状况下,Redis默认使用zipmap做为底层实现以节省空间,只有当size超出必定限制后(hash-max-zipmap-entries ),Redis才会自动把zipmap转换为下图Hash Table。
上图字典的底层实现为哈希表,每一个字典包含2个哈希表,ht[0], ht[1], 1号哈希表是在rehash过程当中才使用的。而哈希表则由dictEntry构成。
每一个字典包含了3个内部数据结构:
Dict:字典的根结构,包含了2个dictht,其中2做为rehashing之用
Dictht:包含了linkedlist dictEntry
DictEntry:包含了3个数据结构(double/uint64_6/int64t)的链表,相似Java HashMap中的Entry结构
5. Hash算法
目前Redis中引入了一些经典哈希算法,而HashTable则主要为如下两种:
MurmurHash2 32bit算法:著名的非加密型哈希函数,能产生32位或64位哈希值,最新版本为MurmurHash3。该算法针对一个字符串进行哈希,可表现较强离散性。
基于djb算法实现散列算法:该算法较为简单,一样是将字符串转换为哈希值。主要利用字符串中的ASCII码与一个随机seed,进行变换获得哈希值。
评估一个哈希算法的优劣,主要看其哈希值的离散均匀效果以及消除冲突程度。
6. Rehash
相似Java中的HashMap, 当有新键值对添加到Redis字典时,有可能会触发rehash。Redis中处理哈希碰撞的方法与Java同样,都是采用链表法,整个哈希表的性能则依赖于它的大小size和它已经保存节点数量used的比率。比率在1:1时,哈希表的性能最好,若是节点数量比哈希表大小大不少的话,则整个哈希表就退化成多个链表,其性能优点全无。
上图的哈希表,平均每次失败查找须要访问5个节点。为了保持高效性能,在不修改键值对状况下,须要进行rehash,目标是将ratio比率维持在1:1左右。Ratio = Used / Size
rehash触发条件:
天然rehash:ratio >= 1, 且变量dict_can_resize为真
强制rehash:ratio大于dict_force_resize_ratio(v 3.2.1版本为5)
rehash执行过程:
建立ht[1]并分配至少2倍于ht[0] table的空间
将ht[0] table中的全部键值对迁移到ht[1] table
将ht[0]数据清空,并将ht[1]替换为新的ht[0]
Redis哈希为了不整个rehash过程当中服务被阻塞,采用了渐进式的rehash,即rehash程序激活后,并非立刻执行直到完成,而是分屡次,渐进式(incremental)的完成。同时,为了保证并发安全,在执行rehash中间执行添加时,新的节点会直接添加到ht[1]而不是ht[0], 这样保证了数据的完整性与安全性。另外一方面,哈希的Rehash在还提供了创新的(相对于Java HashMap)收缩(shrink)字典,当可用节点远远大于已用节点的时候,rehash会自动进行收缩,具体过程与上面相似以保证比率始终高效使用。
持久化简单来说就是将数据放到断电后数据不会丢失的设备中,也就是咱们一般理解的硬盘上。
首先咱们来看一下数据库在进行写操做时到底作了哪些事,主要有下面五个过程:
经过上面5步的了解,可能咱们会但愿搞清下面一些问题:
数据损坏
所谓数据损坏,就是数据没法恢复,上面咱们讲的都是如何保证数据是确实写到磁盘上去,可是写到磁盘上可能并不意味着数据不会损坏。好比咱们可能一次写请求会进行两次不一样的写操做,当意外发生时,可能会致使一次写操做安全完成,可是另外一次尚未进行。若是数据库的数据文件结构组织不合理,可能就会致使数据彻底不能恢复的情况出现。
这里一般也有三种策略来组织数据,以防止数据文件损坏到没法恢复的状况:
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘。
也是默认的持久化方式,这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。
能够经过配置设置自动作快照持久化的方式。咱们能够配置redis在n秒内若是超过m个key被修改就自动作快照,下面是默认的快照保存配置
save 900 1 #900秒内若是超过1个key被修改,则发起快照保存
save 300 10 #300秒内容如超过10个key被修改,则发起快照保存
save 60 10000
client 也能够使用save或者bgsave命令通知redis作一次快照持久化。save操做是在主线程中保存快照的,因为redis是用一个主线程来处理全部 client的请求,这种方式会阻塞全部client请求。因此不推荐使用。
另外一点须要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并非增量的只同步脏数据。若是数据量大的话,并且写操做比较多,必然会引发大量的磁盘io操做,可能会严重影响性能。
一旦采用该方式,那么你的整个Redis数据库将只包含一个文件,这样很是方便进行备份。好比你可能打算没1天归档一些数据。
redis会将每个收到的写命令都经过write函数追加到文件中(默认是 appendonly.aof)。
当redis重启时会经过从新执行文件中保存的写命令来在内存中重建整个数据库的内容。固然因为os会在内核中缓存 write作的修改,因此可能不是当即写到磁盘上。这样aof方式的持久化也仍是有可能会丢失部分修改。不过咱们能够经过配置文件告诉redis咱们想要 经过fsync函数强制os写入到磁盘的时机。有三种方式以下(默认是:每秒fsync一次)
appendonly yes //启用aof持久化方式
# appendfsync always //每次收到写命令就当即强制写入磁盘,最慢的,可是保证彻底的持久化,不推荐使用
# appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面作了很好的折中,推荐
# appendfsync no //彻底依赖os,性能最好,持久化没保证
aof 的方式也同时带来了另外一个问题。持久化文件会变的愈来愈大。例如咱们调用incr test命令100次,文件中必须保存所有的100条命令,其实有99条都是多余的。由于要恢复数据库的状态其实文件中保存一条set test 100就够了。
为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照相似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程以下
须要注意到是重写aof文件的操做,并无读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点相似。
使用 AOF 持久化会让 Redis 变得很是耐久(much more durable):你能够设置不一样的 fsync 策略,好比无 fsync ,每秒钟一次 fsync ,或者每次执行写入命令时 fsync 。 AOF 的默认策略为每秒钟 fsync 一次,在这种配置下,Redis 仍然能够保持良好的性能,而且就算发生故障停机,也最多只会丢失一秒钟的数据( fsync 会在后台线程执行,因此主线程能够继续努力地处理命令请求)。
AOF 文件是一个只进行追加操做的日志文件(append only log), 所以对 AOF 文件的写入不须要进行 seek , 即便日志由于某些缘由而包含了未写入完整的命令(好比写入时磁盘已满,写入中途停机,等等), redis-check-aof 工具也能够轻易地修复这种问题。
Redis 能够在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。 整个重写操做是绝对安全的,由于 Redis 在建立新 AOF 文件的过程当中,会继续将命令追加到现有的 AOF 文件里面,即便重写过程当中发生停机,现有的 AOF 文件也不会丢失。 而一旦新 AOF 文件建立完毕,Redis 就会从旧 AOF 文件切换到新 AOF 文件,并开始对新 AOF 文件进行追加操做。
AOF 文件有序地保存了对数据库执行的全部写入操做, 这些写入操做以 Redis 协议的格式保存, 所以 AOF 文件的内容很是容易被人读懂, 对文件进行分析(parse)也很轻松。 导出(export) AOF 文件也很是简单: 举个例子, 若是你不当心执行了 FLUSHALL 命令, 但只要 AOF 文件未被重写, 那么只要中止服务器, 移除 AOF 文件末尾的 FLUSHALL 命令, 并重启 Redis , 就能够将数据集恢复到 FLUSHALL 执行以前的状态。
对于相同的数据集来讲,AOF 文件的体积一般要大于 RDB 文件的体积。
根据所使用的 fsync 策略,AOF 的速度可能会慢于 RDB 。 在通常状况下, 每秒 fsync 的性能依然很是高, 而关闭 fsync 可让 AOF 的速度和 RDB 同样快, 即便在高负荷之下也是如此。 不过在处理巨大的写入载入时,RDB 能够提供更有保证的最大延迟时间(latency)。
AOF 在过去曾经发生过这样的 bug : 由于个别命令的缘由,致使 AOF 文件在从新载入时,没法将数据集恢复成保存时的原样。 (举个例子,阻塞命令 BRPOPLPUSH 就曾经引发过这样的 bug 。) 测试套件里为这种状况添加了测试: 它们会自动生成随机的、复杂的数据集, 并经过从新载入这些数据来确保一切正常。 虽然这种 bug 在 AOF 文件中并不常见, 可是对比来讲, RDB 几乎是不可能出现这种 bug 的。
数据类型与操做:Redis拥有更多丰富的数据结构支持与操做,而Memcached则需客户端本身处理并进行网络交互
内存使用率:简单K/V存储,Memcached内存利用率更高(使用了slab与大小不一样的chunk来管理内存),而若是采用Redis Hash来存储则其组合压缩,内存利用率高于Memcached
性能:整体来讲,两者性能接近;Redis使用了单核(单线程IO复用,封装了AeEvent事件处理框架,实现了epoll,kqueue,select),Memcached采用了多核,各有利弊;当数据大于100K的时候,Memcached性能高于Redis
数据持久化:Redis支持数据文件持久化,RDB与AOF两种策略;Memcached则不支持
分布式:Memcached自己并不支持服务器端分布式,客户端只能借助一致性哈希分布式算法来实现Memcached分布式存储;固然Redis也是从3.0版本开始才支持服务器端cluster的,重要的是如今支持了。
其余方面:Redis提供其余一些功能,如Pub/Sub, Queue, 简单Transacation, Replication等。
HashMap单机受限于内存容量,而正是Redis分布式之优点
HashMap当数据量超过必定限制后,须要妥善管理堆内存,否则会形成内存溢出或者Memory Leak;Redis则具有了文件持久性,以及Failover达到HA.
HashMap只能受限于本机,而Redis天生分布式,可让多个App Server访问,负载均衡。
因此Redis适合所有数据都在内存的场景包括须要临时持久化,尤为做为缓存来使用,并支持对缓存数据进行简单处理计算;如涉及Redis与RDBMS双向同步的话,则须要引入一些复杂度。
Reids Sentinel诞生于2012年(Redis 2.4版本),建议在单机Redis或者客户端模式Cluster的时候(非 3.0版本Redis Cluster)采用,做为HA, Failover来使用。
Sentinel主要提供了集群管理,包括监控,通知,自动故障恢复。如上图,当其中一个master没法正常工做时,Sentinel将把一个Slave提高为Master, 从而自动恢复故障。而Sentinel自己也作到了分布式,能够部署多个Sentinel实例来监控Redis实例(建议基数,至少3个Sentinel实例来监控一组Redis Master/Slaves),多个Sentinel进程间经过Gossip协议来肯定Master是否宕机,经过Agreement协议来决定是否执行故障自动迁移以及从新选主,总体设计相似ZooKeeper)。
所谓分布式即支持数据分片,而且自动管理故障恢复(failover)与备份(replication)。
如上图Redis Cluster采用了无中心结构,每一个节点都保存,共享数据和集群状态,每一个节点与其它全部节点通讯,使用Gossip协议来传播及发现新节点,经过分区来提供必定程度可用性,当某个node的Master宕机时,Cluster会自动选举一个Slave造成一个新的Master,这里应该是
借鉴,重用了Sentinel的功能。另外,Redis Cluster并无使用一般的一致性哈希, 而引入哈希槽的概念,Cluster中固定有16384个slot, 每一个key经过CRC16校验后对16384取模来决定其对应slot的位置,而每一个node负责一部分的slot管理,当node变化时,动态调整slot的分布,而数据则无须挪动。对于客户端来讲,client能够向任意一个实例请求,Cluster会自动定位须要访问的slot。
上图查询路由过程当中,咱们随机发送到任意一个Redis实例,这个实例会按照上文提到的CRC16校验后取模定位,并转发至正确的Redis实例中。
https://blog.csdn.net/cangchen/article/details/44830087
https://segmentfault.com/a/1190000012950110
https://www.cnblogs.com/work115/p/5584646.html
https://blog.csdn.net/erixhao/article/details/52223839
https://blog.csdn.net/lhx13636332274/article/details/73867165