因为数据库的写入压力增长,Memcached只能缓解数据库的读取压力。读写集中在一个数据库上让数据库不堪重负,大部分网站开始使用主从复制技术达到读写分离,以提升读写性能和读库的可扩展性。Mysql的master-slave模式成为这个时候的网站标配了。html
随着web2.0的继续高速发展,在Memcached的高速缓存,MySQL的主从复制,读写分离的基础之上,这时MySQL主库的写压力开始出现瓶颈,而数据量的持续猛增,因为MyISAM使用表锁,在高并发下会出现严重的锁问题,大量的高并发MySQL应用开始使用InnoDB引擎代替MyISAM。同时,开始流行使用分表分库来缓解写压力和数据增加的扩展问题。这个时候,分表分库成了一个热门技术,是业界讨论的热门技术问题。也就在这个时候,MySQL推出了还不太稳定的表分区,这也给技术实力通常的公司带来了但愿。虽然MySQL推出了MySQL Cluster集群,可是因为在互联网几乎没有成功案例,性能也不能知足互联网的要求,只是在高可靠性上提供了很是大的保证。node
关系型数据库面临的问题:mysql
数据库访问的新需求:web
CAP原理是指这三个要素最多只能同时实现两点,不可能三者兼顾。所以在进行分布式架构设计时,必须作出取舍。而对于分布式数据系统,分区容忍性是基本要求,不然就失去了价值。所以设计分布式数据系统,就是在一致性和可用性之间取一个平衡。对于大多数WEB应用,其实并不须要强一致性,所以牺牲一致性而换取高可用性,是多数分布式数据库产品的方向。redis
在理论计算机科学中,CAP定理(CAP theorem),又被称做布鲁尔定理(Brewer’s theorem),它指出对于一个分布式计算系统来讲,不可能同时知足如下三点:算法
关系数据库 | NoSQL |
---|---|
分布式关系型数据库中强调的ACID 分别是:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability) |
对于许多互联网应用来讲,对于一致性要求能够下降,而可用性(Availability)的要求则更为明显,在CAP理论基础上,从而产生了弱一致性的理论BASE。 |
ACID的目的就是经过事务支持,保证数据的完整性和正确性 | BASE 分别是英文:Basically,Available(基本可用), Softstate(软状态)非实时同步,Eventual Consistency(最终一致)的缩写,这个模型是反ACID 模型 |
优势:sql
典型例子是Cassandra,因为其架构是相似于经典的P2P,因此能经过轻松地添加新的节点来扩展这个集群;mongodb
主要例子有Redis,因为其逻辑简单,并且纯内存操做,使得其性能很是出色,单节点每秒能够处理超过10万次读写操做;数据库
这是大多数分布式数据库共有的特色,由于主要都是开源软件,没有昂贵的License成本;vim
缺点:
若是不支持SQL这样的工业标准,将会对用户产生必定的学习和应用迁移成本;
现有产品所提供的功能都比较有限,大多数NoSQL数据库都不支持事务,也不像Oracle那样能提供各类附加功能,好比BI和报表等;
大多数产品都还处于初创期,和关系型数据库几十年的完善不可同日而语;
Column-oriented(列式)
Key-value(重要)
Document(文档) (好比:mongodb)
关注一致性和可用性的(CA)
这些数据库对于分区容忍性方面比较不感冒,主要采用复制(Replication)这种方式来保证数据的安全性,常见的CA系统有:
关注一致性和分区容忍性的(CP)
这种系统将数据分布在多个网络分区的节点上,并保证这些数据的一致性,可是对于可用性的支持方面有问题,好比当集群出现问题的话,节点有可能因没法确保数据是一致性的而拒绝提供服务,主要的CP系统有:
关于可用性和分区容忍性的(AP)
这类系统主要以实现“最终一致性(Eventual Consistency)”来确保可用性和分区容忍性,AP的系统有:
因为NoSQL数据库天生具备高性能、易扩展的特色,因此咱们经常结合关系数据库,存储一些高性能的、海量的数据。
从另一个角度看,根据NoSQL的高性能特色,它一样适合用于缓存数据。用NoSQL缓存数据能够分为内存模式和磁盘持久化模式。
内存模式
持久化模式
redis是一个key-value存储系统。和Memcached相似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sortedset --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操做,并且这些操做都是原子性的。在此基础上,redis支持各类不一样方式的排序。与memcached同样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操做写入追加的记录文件,而且在此基础上实现了master-slave(主从)同步。
Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合能够对关系数据库起到很好的补充做用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。
Redis支持主从同步。数据能够从主服务器向任意数量的从服务器上同步,从服务器能够是关联其余从服务器的主服务器。这使得Redis可执行单层树复制。存盘能够有意无心的对数据进行写操做。因为彻底实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操做的可扩展性和数据冗余颇有帮助。
redis的官网地址,很是好记,是redis.io。
目前,Vmware在资助着redis项目的开发和维护。
各功能模块说明以下:
File Event
: 处理文件事件,接受它们发来的命令请求(读事件),并将命令的执行结果返回给客户端(写事件))
Time Event
: 时间事件(更新统计信息,清理过时数据,附属节点同步,按期持久化等)
AOF
: 命令日志的数据持久化
RDB
:实际的数据持久化
Lua Environment
: Lua 脚本的运行环境. 为了让 Lua 环境符合 Redis 脚本功能的需求,Redis 对 Lua 环境进行了一系列的修改,包括添加函数库、更换随机函数、保护全局变量,等等
Command table(命令表)
:在执行命令时,根据字符来查找相应命令的实现函数。
Share Objects(对象共享)
:
主要存储常见的值:
a.各类命令常见的返回值,例如返回值OK、ERROR、WRONGTYPE等字符;
b. 小于 redis.h/REDIS_SHARED_INTEGERS (默认1000)的全部整数。经过预分配的一些常见的值对象,并在多个数据结构之间共享对象,程序避免了重复分配的麻烦。也就是说,这些常见的值在内存中只有一份。
Databases
:Redis数据库是真正存储数据的地方。固然,数据库自己也是存储在内存中的。
redis安装经常使用两种方式,yum安装和源码包安装
yum 安装:一般是在线安装,好处是安装方式简单,不易出错;经常使用的安装yum源为epel。
源码包安装:是先将 redis 的源码下载下来,在本身的系统里编译生成可执行文件,而后执行,好处是由于是在本身的系统上编译的,更符合本身系统的性能,也就是说在本身的系统上执行 redis 服务性能效率更好。
区别:路径和启动方式不一样,支持的模块也不一样。
配置文件:
/etc/redis.conf
主程序:/usr/bin/redis-server
客户端:/usr/bin/redis-cli
UnitFile:/usr/lib/systemd/system/redis.service
数据目录:/var/lib/redis
监听:6379/tcp
### NETWORK ### bind IP #监听地址 port PORT #监听端口 protected-mode yes #是否开启保护模式,默认开启。要是配置里没有指定bind和密码。开启该参数后,redis只会本地进行访问,拒绝外部访问。 tcp-backlog 511 #定义了每个端口最大的监听队列的长度 unixsocket /tmp/redis.sock #也能够打开套接字监听 timeout 0 #链接的空闲超时时长;
### GENERAL ### daemonize no #是否以守护进程启动 supervised no #能够经过upstart和systemd管理Redis守护进程,这个参数是和具体的操做系统相关的 pidfile "/var/run/redis/redis.pid" #pid文件 loglevel notice #日志等级 logfile "/var/log/redis/redis.log" #日志存放文件 databases 16 #设定数据库数量,默认为16个,每一个数据库的名字均为整数,从0开始编号,默认操做的数据库为0; 切换数据库的方法:SELECT <dbid>
### SNAPSHOTTING ### save 900 1 #900秒有一个key变化,就作一个保存 save 300 10 #300秒有10个key变化,就作一个保存,这里须要和开发沟通 save 60 10000 #60秒有10000个key变化就作一个保存 stop-writes-on-bgsave-error yes #在出现错误的时候,是否是要中止保存 rdbcompression yes #使用压缩rdb文件,rdb文件压缩使用LZF压缩算法,yes:压缩,可是须要一些cpu的消耗;no:不压缩,须要更多的磁盘空间 rdbchecksum yes #是否校验rdb文件。从rdb格式的第五个版本开始,在rdb文件的末尾会带上CRC64的校验和。这跟有利于文件的容错性,可是在保存rdb文件的时候,会有大概10%的性能损耗,因此若是你追求高性能,能够关闭该配置。 dbfilename "along.rdb" #rdb文件的名称 dir "/var/lib/redis" #数据目录,数据库的写入会在这个目录。rdb、aof文件也会写在这个目录
### LIMITS ### maxclients 10000 #设置能连上redis的最大客户端链接数量 maxmemory <bytes> #redis配置的最大内存容量。当内存满了,须要配合maxmemory-policy策略进行处理。 maxmemory-policy noeviction #淘汰策略:volatile-lru, allkeys-lru, volatile-random, allkeys-random, volatile-ttl, noeviction 内存容量超过maxmemory后的处理策略: ① # volatile-lru:利用LRU算法移除设置过过时时间的key。 ② # volatile-random:随机移除设置过过时时间的key。 ③ # volatile-ttl:移除即将过时的key,根据最近过时时间来删除(辅以TTL) ④ # allkeys-lru:利用LRU算法移除任何key。 ⑤ # allkeys-random:随机移除任何key。 ⑥ # noeviction:不移除任何key,只是返回一个写错误。 # 上面的这些驱逐策略,若是redis没有合适的key驱逐,对于写命令,仍是会返回错误。redis将再也不接收写请求,只接收get请求。写命令包括:set setnx maxmemory-samples 5 #淘汰算法运行时的采样样本数;
### APPEND ONLY MODE ### # 默认redis使用的是rdb方式持久化,这种方式在许多应用中已经足够用了。可是redis若是中途宕机,会致使可能有几分钟的数据丢失,根据save来策略进行持久化,Append Only File是另外一种持久化方式,能够提供更好的持久化特性。Redis会把每次写入的数据在接收后都写入 appendonly.aof 文件,每次启动时Redis都会先把这个文件的数据读入内存里,先忽略RDB文件。 appendonly no #不启动aof模式 appendfilename "appendonly.aof" #据读入内存里,先忽略RDB文件,aof文件名(default: "appendonly.aof") appendfsync Redis supports three different modes: no:redis不执行主动同步操做,而是OS进行; everysec:每秒一次; always:每语句一次;
若是Redis只是将客户端修改数据库的指令重现存储在AOF文件中,那么AOF文件的大小会不断的增长,由于AOF文件只是简单的重现存储了客户端的指令,而并无进行合并。对于该问题最简单的处理方式,即当AOF文件知足必定条件时就对AOF进行rewrite,rewrite是根据当前内存数据库中的数据进行遍历写到一个临时的AOF文件,待写完后替换掉原来的AOF文件便可。
redis重写会将多个key、value对集合来用一条命令表达。在rewrite期间的写操做会保存在内存的rewrite buffer中,rewrite成功后这些操做也会复制到临时文件中,在最后临时文件会代替AOF文件。
no-appendfsync-on-rewrite no #在aof重写或者写入rdb文件的时候,会执行大量IO,此时对于everysec和always的aof模式来讲,执行fsync会形成阻塞过长时间,no-appendfsync-on-rewrite字段设置为默认设置为no。若是对延迟要求很高的应用,这个字段能够设置为yes,不然仍是设置为no,这样对持久化特性来讲这是更安全的选择。设置为yes表示rewrite期间对新写操做不fsync,暂时存在内存中,等rewrite完成后再写入,默认为no,建议yes。Linux的默认fsync策略是30秒。可能丢失30秒数据。 auto-aof-rewrite-percentage 100 aof自动重写配置。当目前aof文件大小超过上一次重写的aof文件大小的百分之多少进行重写,即当aof文件增加到必定大小的时候Redis可以调用bgrewrite aof对日志文件进行重写。当前AOF文件大小是上第二天志重写获得AOF文件大小的二倍(设置为100)时,自动启动新的日志重写过程。 auto-aof-rewrite-min-size 64mb #设置容许重写的最小aof文件大小,避免了达到约定百分比但尺寸仍然很小的状况还要重写。上述两个条件同时知足时,方会触发重写AOF;与上次aof文件大小相比,其增加量超过100%,且大小很多于64MB; aof-load-truncated yes #指redis在恢复时,会忽略最后一条可能存在问题的指令。aof文件可能在尾部是不完整的,出现这种现象,能够选择让redis退出,或者导入尽量多的数据。若是选择的是yes,当截断的aof文件被导入的时候,会自动发布一个log给客户端而后load。 若是是no,用户必须手动redis-check-aof修复AOF文件才能够。 注意:持久机制自己不能取代备份;应该制订备份策略,对redis库按期备份;Redis服务器启动时用持久化的数据文件恢复数据,会优先使用AOF;
咱们继续来看 redis 的持久化:
RDB:snapshotting, 二进制格式;按事先定制的策略,周期性地将数据从内存同步至磁盘;数据文件默认为dump.rdb;
客户端显式使用SAVE或BGSAVE命令来手动启动快照保存机制;
SAVE:同步,即在主线程中保存快照,此时会阻塞全部客户端请求;
BGSAVE:异步;backgroud
AOF:Append Only File, fsync
记录每次写操做至指定的文件尾部实现的持久化;当redis重启时,可经过从新执行文件中的命令在内存中重建出数据库;
BGREWRITEAOF:AOF文件重写;
不会读取正在使用AOF文件,而是经过将内存中的数据以命令的方式保存至临时文件中,完成以后替换原来的AOF文件;
### SLOW LOG ### slowlog-log-slower-than 10000 #当命令的执行超过了指定时间,单位是微秒;1s=10^6微秒 slowlog-max-len 128 #慢查询日志长度。当一个新的命令被写进日志的时候,最老的那个记录会被删掉。 ADVANCED配置: hash-max-ziplist-entries 512 hash-max-ziplist-value 64 设置ziplist的键数量最大值,每一个值的最大空间;
└── bin
├── redis-benchmark #redis性能测试工具,能够测试在本系统本配置下的读写性能
├── redis-check-aof #对更新日志appendonly.aof检查,是否可用
├── redis-check-dump #用于检查本地数据库的rdb文件
├── redis-cli #redis命令行操做工具,也能够用telnet根据其纯文本协议来操做
├── redis-sentinel Redis-sentinel 是Redis实例的监控管理、通知和实例失效备援服务,是Redis集群的管理工具
└── redis-server #redis服务器的daemon启动程序
redis-cli -p 6379 #默认选择 db库是 0
redis 127.0.0.1:6379> keys * #查看当前所在“db库”全部的缓存key
redis 127.0.0.1:6379> select 8 #选择 db库
redis 127.0.0.1:6379> FLUSHALL #清除全部的缓存key
redis 127.0.0.1:63798> FLUSHDB #清除当前“db库”全部的缓存key
redis 127.0.0.1:6379> set keyname keyvalue #设置缓存值
redis 127.0.0.1:6379> get keyname #获取缓存值
redis 127.0.0.1:6379> del keyname #删除缓存值:返回删除数量(0表明没删除)
服务端的相关命令:
time
:返回当前服务器时间
client list
: 返回全部链接到服务器的客户端信息和统计数据 参见http://redisdoc.com/server/client_list.html
client kill ip:port
:关闭地址为 ip:port 的客户端
save
:将数据同步保存到磁盘
bgsave
:将数据异步保存到磁盘
lastsave
:返回上次成功将数据保存到磁盘的Unix时戳
shundown
:将数据同步保存到磁盘,而后关闭服务
info
:提供服务器的信息和统计
config resetstat
:重置info命令中的某些统计数据
config get
:获取配置文件信息
config set
:动态地调整 Redis 服务器的配置(configuration)而无须重启,能够修改的配置参数可使用命令 CONFIG GET * 来列出
config rewrite
:Redis 服务器时所指定的 redis.conf 文件进行改写
monitor
:实时转储收到的请求
slaveof
:改变复制策略设置
debug
:sleep segfault
slowlog get
:获取慢查询日志
slowlog len
:获取慢查询日志条数
slowlog reset
:清空慢查询
Redis内部使用一个redisObject对象来表示全部的key和value,redisObject最主要的信息如上图所示:
type表明一个value对象具体是何种数据类型
encoding是不一样数据类型在redis内部的存储方式
好比:type=string表明value存储的是一个普通字符串,那么对应的encoding能够是raw或者是int,若是是int则表明实际redis内部是按数值型类存储和表示这个字符串的,固然前提是这个字符串自己能够用数值表示,好比:“123” "456"这样的字符串。
Redis的键值可使用物种数据类型:字符串,散列表,列表,集合,有序集合。
exists(key)
:确认一个key是否存在
del(key)
:删除一个key
type(key)
:返回值的类型
keys(pattern)
:返回知足给定pattern的全部key
randomkey
:随机返回key空间的一个
keyrename(oldname, newname)
:重命名key
dbsize
:返回当前数据库中key的数目
expire
:设定一个key的活动时间(s)
ttl
:得到一个key的活动时间
move(key, dbindex)
:移动当前数据库中的key到dbindex数据库
flushdb
:删除当前选择数据库中的全部key
flushall
:删除全部数据库中的全部key
应用场景:String是最经常使用的一种数据类型,普通的key/ value 存储均可以归为此类.便可以彻底实现目前 Memcached 的功能,而且效率更高。还能够享受Redis的定时持久化,操做日志及 Replication等功能。除了提供与 Memcached 同样的get、set、incr、decr 等操做外,Redis还提供了下面一些操做:
set(key, value):给数据库中名称为key的string赋予值value
get(key):返回数据库中名称为key的string的value
getset(key, value)
:给名称为key的string赋予上一次的value
mget(key1, key2,…, key N)
:返回库中多个string的value
setnx(key, value)
:添加string,名称为key,值为value
setex(key, time, value)
:向库中添加string,设定过时时间time
mset(key N, value N)
:批量设置多个string的值
msetnx(key N, value N)
:若是全部名称为key i的string都不存在
incr(key)
:名称为key的string增1操做
incrby(key, integer)
:名称为key的string增长integer
decr(key)
:名称为key的string减1操做
decrby(key, integer)
:名称为key的string减小integer
append(key, value)
:名称为key的string的值附加value
substr(key, start, end)
:返回名称为key的string的value的子串
应用场景:在Memcached中,咱们常常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,好比用户的昵称、年龄、性别、积分等,这时候在须要修改其中某一项时,一般须要将全部值取出反序列化后,修改某一项的值,再序列化存储回去。这样不只增大了开销,也不适用于一些可能并发操做的场合(好比两个并发的操做都须要修改积分)。而Redis的Hash结构可使你像在数据库中Update一个属性同样只修改某一项属性值。
hset(key, field, value)
:向名称为key的hash中添加元素field
hget(key, field)
:返回名称为key的hash中field对应的value
hmget(key, (fields))
:返回名称为key的hash中field i对应的value
hmset(key, (fields))
:向名称为key的hash中添加元素field
hincrby(key, field, integer)
:将名称为key的hash中field的value增长integer
hexists(key, field)
:名称为key的hash中是否存在键为field的域
hdel(key, field)
:删除名称为key的hash中键为field的域
hlen(key)
:返回名称为key的hash中元素个数
hkeys(key)
:返回名称为key的hash中全部键
hvals(key)
:返回名称为key的hash中全部键对应的value
hgetall(key)
:返回名称为key的hash中全部的键(field)及其对应的value
Redis list的应用场景很是多,也是Redis最重要的数据结构之一,好比twitter的关注列表,粉丝列表等均可以用Redis的list结构来实现。咱们在看完一条微博以后,经常会评论一番,或者看看其余人的吐槽。每条评论的记录都是按照时间顺序排序的。
具体操做命令以下:
rpush(key, value)
:在名称为key的list尾添加一个值为value的元素
lpush(key, value)
:在名称为key的list头添加一个值为value的 元素
llen(key)
:返回名称为key的list的长度
lrange(key, start, end)
:返回名称为key的list中start至end之间的元素
ltrim(key, start, end)
:截取名称为key的list
lindex(key, index)
:返回名称为key的list中index位置的元素
lset(key, index, value)
:给名称为key的list中index位置的元素赋值
lrem(key, count, value)
:删除count个key的list中值为value的元素
lpop(key)
:返回并删除名称为key的list中的首元素
rpop(key)
:返回并删除名称为key的list中的尾元素
blpop(key1, key2,… key N, timeout)
:lpop命令的block版本。
brpop(key1, key2,… key N, timeout)
:rpop的block版本。
rpoplpush(srckey, dstkey)
:返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部
Set 就是一个集合,集合的概念就是一堆不重复值的组合。利用 Redis 提供的 Set 数据结构,能够存储一些集合性的数据。好比在微博应用中,能够将一个用户全部的关注人存在一个集合中,将其全部粉丝存在一个集合。由于 Redis 很是人性化的为集合提供了求交集、并集、差集等操做,那么就能够很是方便的实现如共同关注、共同喜爱、二度好友等功能。
具体操做命令以下:
sadd(key, member)
:向名称为key的set中添加元素member
srem(key, member)
:删除名称为key的set中的元素member
spop(key)
:随机返回并删除名称为key的set中一个元素
smove(srckey, dstkey, member)
:移到集合元素
scard(key)
:返回名称为key的set的基数
sismember(key, member)
:member是不是名称为key的set的元素
sinter(key1, key2,…key N)
:求交集
sinterstore(dstkey, (keys))
:求交集并将交集保存到dstkey的集合
sunion(key1, (keys))
:求并集
sunionstore(dstkey, (keys))
:求并集并将并集保存到dstkey的集合
sdiff(key1, (keys))
:求差集
sdiffstore(dstkey, (keys))
:求差集并将差集保存到dstkey的集合
smembers(key)
:返回名称为key的set的全部元素
srandmember(key)
:随机返回名称为key的set的一个元素
Redis的复制方式有两种,一种是主(master)-从(slave)模式,一种是从(slave)-从(slave)模式,所以Redis的复制拓扑图会丰富一些,能够像星型拓扑,也能够像个有向无环。一个Master能够有多个slave主机,支持链式复制;Master以非阻塞方式同步数据至slave主机;
拓扑图以下:
经过配置多个Redis实例,数据备份在不一样的实例上,主库专一写请求,从库负责读请求,这样的好处主要体如今下面几个方面
1. 高可用性
在一个Redis集群中,若是master宕机,slave能够介入并取代master的位置,所以对于整个Redis服务来讲不至于提供不了服务,这样使得整个Redis服务足够安全。
2. 高性能
在一个Redis集群中,master负责写请求,slave负责读请求,这么作一方面经过将读请求分散到其余机器从而大大减小了master服务器的压力,另外一方面slave专一于提供读服务从而提升了响应和读取速度。
3. 水平扩展性
经过增长slave机器能够横向(水平)扩展Redis服务的整个查询服务的能力。
复制提供了高可用性的解决方案,但同时引入了分布式计算的复杂度问题,认为有两个核心问题:
1. 数据一致性问题: 如何保证master服务器写入的数据可以及时同步到slave机器上。
2. 读写分离: 如何在客户端提供读写分离的实现方案,经过客户端实现将读写请求分别路由到master和slave实例上。
上面两个问题,尤为是第一个问题是Redis服务实现一直在演变,致力于解决的一个问题:复制实时性和数据一致性矛盾。
Redis提供了提升数据一致性的解决方案,一致性程度的增长虽然使得我可以更信任数据,可是更好的一致性方案一般伴随着性能的损失,从而减小了吞吐量和服务能力。然而咱们但愿系统的性能达到最优,则必需要牺牲一致性的程度,所以Redis的复制实时性和数据一致性是存在矛盾的。
如何解决redis横向扩展的问题----redis集群实现方式
分区是分割数据到多个Redis实例的处理过程,所以每一个实例只保存key的一个子集。经过利用多台计算机内存的和值,容许咱们构造更大的数据库。经过多核和多台计算机,容许咱们扩展计算能力;经过多台计算机和网络适配器,容许咱们扩展网络带宽。
集群的几种实现方式:
由客户端决定key写入或者读取的节点。
包括jedis在内的一些客户端,实现了客户端分片机制。
优势
简单,性能高
缺点
1. 业务逻辑与数据存储逻辑耦合
2. 可运维性差
3. 多业务各自使用redis,集群资源难以管理
4. 不支持动态增删节点
客户端发送请求到一个代理,代理解析客户端的数据,将请求转发至正确的节点,而后将结果回复给客户端。
开源方案
1. Twemproxy
2. codis
特性
1. 透明接入
2. 业务程序不用关心后端Redis实例,切换成本低。
3. Proxy 的逻辑和存储的逻辑是隔离的。
4. 代理层多了一次转发,性能有所损耗。
Twemproxy
Proxy-based
twtter开源,C语言编写,单线程。
支持 Redis 或 Memcached 做为后端存储。
优势:
1. 支持失败节点自动删除
2. 与redis的长链接,链接复用,链接数可配置
3. 自动分片到后端多个redis实例上
4. 多种hash算法:可以使用不一样的分片策略和散列函数
5. 能够设置后端实例的权重
缺点:
1. 性能低:代理层损耗 && 自己效率低下
2. Redis功能支持不完善:不支持针对多个值的操做
3. 自己不提供动态扩容,透明数据迁移等功能
Codis
Codis由豌豆荚于2014年11月开源,基于Go和C开发,是近期涌现的、国人开发的优秀开源软件之一。现已普遍用于豌豆荚的各类Redis业务场景。从3个月的各类压力测试来看,稳定性符合高效运维的要求。性能更是改善不少,最初比Twemproxy慢20%;如今比Twemproxy快近100%(条件:多实例,通常Value长度)。
将请求发送到任意节点,接收到请求的节点会将查询请求发送到正确的节点上执行。
Redis-cluster由redis官网推出,可线性扩展到1000个节点。无中心架构;使用一致性哈希思想;客户端直连redis服务,免去了proxy代理的损耗。
Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
Redis 集群并不支持处理多个keys的命令,由于这须要在不一样的节点间移动数据,从而达不到像Redis那样的性能,在高负载的状况下可能会致使不可预料的错误。
Redis 集群经过分区来提供必定程度的可用性,在实际环境中当某个节点宕机或者不可达的状况下继续处理命令.
Redis 集群的优点:
1. 自动分割数据到不一样的节点上。
2. 整个集群的部分节点失败或者不可达的状况下可以继续处理命令。
Redis 集群的数据分片
Redis 集群没有使用一致性hash,而是引入了哈希槽的概念.
Redis 集群有16384个哈希槽,每一个key经过CRC16校验后对16384取模来决定放置哪一个槽。集群的每一个节点负责一部分hash槽。举个例子,好比当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽。
节点 B 包含5501 到 11000 号哈希槽。
节点 C 包含11001 到 16384号哈希槽。
这种结构很容易添加或者删除节点。好比若是我想新添加个节点D,我须要从节点 A,B,C中得部分槽到D上。若是我想移除节点A,须要将A中得槽移到B和C节点上,而后将没有任何槽的A节点从集群中移除便可。因为从一个节点将哈希槽移动到另外一个节点并不会中止服务,因此不管添加删除或者改变某个节点的哈希槽的数量都不会形成集群不可用的状态。
为了使在部分节点失败或者大部分节点没法通讯的状况下集群仍然可用,因此集群使用了主从复制模型,每一个节点都会有N-1个复制品。
在咱们例子中具备A,B,C三个节点的集群,在没有复制模型的状况下,若是节点B失败了,那么整个集群就会觉得缺乏5501-11000这个范围的槽而不可用。
然而若是在集群建立的时候(或者过一段时间)咱们为每一个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会由于槽找不到而不可用了。不过当B和B1 都失败后,集群是不可用的。
Redis 一致性保证
Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操做。
第一个缘由是由于集群是用了异步复制. 写操做过程:
客户端向主节点B写入一条命令.
主节点B向客户端回复命令状态.
主节点将写操做复制给他得从节点 B1, B2 和 B3.
主节点对命令的复制工做发生在返回命令回复以后, 由于若是每次处理命令请求都须要等待复制操做完成的话,那么主节点处理命令请求的速度将极大地下降 —— 咱们必须在性能和一致性之间作出权衡。注意:Redis 集群可能会在未来提供同步写的方法。Redis 集群另一种可能会丢失命令的状况是集群出现了网络分区, 而且一个客户端与至少包括一个主节点在内的少数实例被孤立。
上图为Redis复制工做过程:
值得注意的是,当slave跟master的链接断开时,slave能够自动的从新链接master,在redis2.8版本以前,每当slave进程挂掉从新链接master的时候都会开始新的一轮全量复制。若是master同时接收到多个slave的同步请求,则master只须要备份一次RDB文件。
机器名称 | IP配置 | 服务角色 |
---|---|---|
redis-master | 192.168.37.111 | redis主服务器 |
redis-slave1 | 192.168.37.122 | 文件存放 |
redis-slave2 | 192.168.37.133 | 文件存放 |
首先,在全部机器上安装redis
:
yum install -y redis
而后咱们把配置文件备份一下,这样便于咱们往后的恢复,是一个好习惯!
cp /etc/redis.conf{,.back}
接着,咱们去修改一下配置文件,更改以下配置:
vim /etc/redis.conf #配置配置文件,修改2项 bind 0.0.0.0 #监听地址(能够写0.0.0.0,监听全部地址;也能够各自写各自的IP) daemonize yes #后台守护进程运行
三台机器都进行修改之后,本步骤完成。
咱们还须要在从服务器上进行一些配置来实现主从同步,具体操做步骤以下:
vim /etc/redis.conf ### REPLICATION ### 在这一段修改 slaveof 192.168.30.107 6379 #设置主服务器的IP和端口号 #masterauth <master-password> #若是设置了访问认证就须要设定此项。 slave-serve-stale-data yes #当slave与master链接断开或者slave正处于同步状态时,若是slave收到请求容许响应,no表示返回错误。 slave-read-only yes #slave节点是否为只读。 slave-priority 100 #设定此节点的优先级,是否优先被同步。
一、打开全部机器上的redis
服务:
systemctl start redis
二、在主上登陆查询主从消息,确认主从是否已实现:
[root@master ~]# redis-cli -h 192.168.37.111 192.168.37.111:6379> info replication
三、日志中也能够查看到:
[root@master ~]# tail /var/log/redis/redis.log
四、测试主从
在主上置一个key
[root@master ~]# redis-cli -h 192.168.37.111 192.168.37.111:6379> set master test OK 192.168.37.111:6379> get master "test"
而后去从上查询,若是可以查询到,则说明成功:
[root@slave1 ~]# redis-cli #由于咱们设置的监听地址是0.0.0.0,因此不须要输入-h 127.0.0.1:6379> get master "test"
一、一个RDB文件从 master 端传到 slave 端,分为两种状况:
① 支持disk:master 端将 RDB file 写到 disk,稍后再传送到 slave 端;
② 无磁盘diskless:master端直接将RDB file 传到 slave socket,不须要与 disk 进行交互。
无磁盘diskless 方式适合磁盘读写速度慢但网络带宽很是高的环境。
二、设置:
repl-diskless-sync no #默认不使用diskless同步方式 repl-diskless-sync-delay 5 #无磁盘diskless方式在进行数据传递以前会有一个时间的延迟,以便slave端可以进行到待传送的目标队列中,这个时间默认是5秒 repl-ping-slave-period 10 #slave端向server端发送pings的时间区间设置,默认为10秒 repl-timeout 60 #设置超时时间 min-slaves-to-write 3 #主节点仅容许其可以通讯的从节点数量大于等于此处的值时接受写操做; min-slaves-max-lag 10 #从节点延迟时长超出此处指定的时长时,主节点会拒绝写入操做;
一、原理
Sentinel(哨兵)是Redis的高可用性(HA)解决方案,由一个或多个Sentinel实例组成的Sentinel系统能够监视任意多个主服务器,以及这些主服务器属下的全部从服务器,并在被监视的主服务器进行下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,而后由新的主服务器代替已下线的主服务器继续处理命令请求。
Redis提供的sentinel(哨兵)机制,经过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决
① 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运做正常。
② 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 能够经过 API 向管理员或者其余应用程序发送通知。
③ 自动故障迁移(Automatic failover): 当一个主服务器不能正常工做时, Sentinel 会开始一次自动故障迁移操做, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其余从服务器改成复制新的主服务器; 当客户端试图链接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可使用新主服务器代替失效服务器。
二、架构流程图
① 正常的主从服务
② sentinel 监控到主 redis 下线
③ 由优先级升级新主
④ 旧主修复,做为从 redis,新主照常工做
机器名称 | IP配置 | 服务角色 | 备注 |
---|---|---|---|
redis-master | 192.168.37.111 | redis主服务器 | 开启sentinel |
redis-slave1 | 192.168.37.122 | 文件存放 | 开启sentinel |
redis-slave2 | 192.168.37.133 | 文件存放 | 开启sentinel |
一、打开全部机器上的 redis 服务
systemctl start redis
二、在主上登陆查询主从关系,肯定主从已经实现
[root@master ~]# redis-cli -h 192.168.37.111 192.168.37.111:6379> info replication
一、配置 sentinel
vim /etc/redis-sentinel.conf port 26379 #默认监听端口26379 #sentinel announce-ip 1.2.3.4 #监听地址,注释默认是0.0.0.0 sentinel monitor mymaster 192.168.30.107 6379 1 #指定主redis和投票裁决的机器数,即至少有1个sentinel节点同时断定主节点故障时,才认为其真的故障 #下面部分保持默认便可,也能够根据本身的需求修改 sentinel down-after-milliseconds mymaster 5000 #若是联系不到节点5000毫秒,咱们就认为此节点下线。 sentinel failover-timeout mymaster 60000 #设定转移主节点的目标节点的超时时长。 sentinel auth-pass <master-name> <password> #若是redis节点启用了auth,此处也要设置password。 sentinel parallel-syncs <master-name> <numslaves> #指在failover过程当中,可以被sentinel并行配置的从节点的数量;
注意:只需指定主机器的IP,等sentinel 服务开启,它能本身查询到主上的从redis;并能完成本身的操做
二、指定优先级
vim /etc/redis.conf 根据本身的需求设置优先级 slave-priority 100 #复制集群中,主节点故障时,sentinel应用场景中的主节点选举时使用的优先级
注意:数字越小优先级越高,但0表示不参与选举;当优先级同样时,随机选举。
一、开启服务
systemctl start redis-sentinel ss -nutl | grep 6379
二、开启服务之后,在/etc/redis-sentinel.conf这个配置文件中会生成从redis的信息
# Generated by CONFIG REWRITE #在配置文件的末尾 sentinel known-slave mymaster 192.168.37.122 6379 sentinel known-slave mymaster 192.168.37.133 6379 sentinel current-epoch 0
一、模拟主 redis-master 故障
[root@master ~]# ps -ef | grep redis redis 5635 1 0 19:33 ? 00:00:06 /usr/bin/redis-sentinel *:26379 [sentinel] redis 5726 1 0 19:39 ? 00:00:02 /usr/bin/redis-server 0.0.0.0:6379 root 5833 5324 0 19:52 pts/0 00:00:00 grep --color=auto redis [root@master ~]# kill 5726
二、新主生成
a.去查看主是谁
在任一机器查看都可,若是是仍然是从,则能够看到主的IP,若是是新主,则能够看到两个从的IP。
[root@slave1 ~]# redis-cli info replication # Replication role:master connected_slaves:2 slave0:ip=192.168.37.133,port=6379,state=online,offset=87000,lag=1 slave1:ip=192.168.37.111,port=6379,state=online,offset=87000,lag=0 master_repl_offset:87000 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:86999
b.在新主上查询日志
[root@slave1 ~]# tail -200 /var/log/redis/redis.log
c.从升级为新主的过程
d.也能够经过sentinel
专门的日志查看,下一步有截图。
tail /var/log/redis/sentinel.log
此时,咱们把旧主的服务从新开启,用来模拟故障的修复:
[root@master ~]# systemctl start redis
而后咱们来查看日志:
[root@master ~]# tail -20 /var/log/redis/redis.log 5726:S 12 Dec 19:39:44.404 * Connecting to MASTER 192.168.37.122:6379 5726:S 12 Dec 19:39:44.405 * MASTER <-> SLAVE sync started 5726:S 12 Dec 19:39:44.405 * Non blocking connect for SYNC fired the event. 5726:S 12 Dec 19:39:44.408 * Master replied to PING, replication can continue... 5726:S 12 Dec 19:39:44.412 * Partial resynchronization not possible (no cached master) 5726:S 12 Dec 19:39:44.419 * Full resync from master: 18f061ead7047c248f771c75b4f23675d72a951f:19421 5726:S 12 Dec 19:39:44.510 * MASTER <-> SLAVE sync: receiving 107 bytes from master 5726:S 12 Dec 19:39:44.510 * MASTER <-> SLAVE sync: Flushing old data 5726:S 12 Dec 19:39:44.511 * MASTER <-> SLAVE sync: Loading DB in memory 5726:S 12 Dec 19:39:44.511 * MASTER <-> SLAVE sync: Finished with success
能够看出,咱们的旧主修复事后,就变成了从,去链接新主。
一、在新主192.168.37.122
上模拟故障
[root@slave1 ~]# ps -ef |grep redis redis 9717 1 0 19:31 ? 00:00:09 /usr/bin/redis-server 0.0.0.0:6379 root 10313 5711 0 20:17 pts/1 00:00:00 grep --color=auto redis [root@slave1 ~]# kill 9717
二、查询sentinel
专门的日志
[root@master ~]# tail -200 /var/log/redis/sentinel.log 5635:X 12 Dec 20:18:35.511 * +slave-reconf-inprog slave 192.168.37.111:6379 192.168.37.111 6379 @ mymaster 192.168.37.122 6379 5635:X 12 Dec 20:18:36.554 * +slave-reconf-done slave 192.168.37.111:6379 192.168.37.111 6379 @ mymaster 192.168.37.122 6379 5635:X 12 Dec 20:18:36.609 # +failover-end master mymaster 192.168.37.122 6379 #当前主失效 5635:X 12 Dec 20:18:36.610 # +switch-master mymaster 192.168.37.122 6379 192.168.37.133 6379 #切换到新主 5635:X 12 Dec 20:18:36.611 * +slave slave 192.168.37.111:6379 192.168.37.111 6379 @ mymaster 192.168.37.133 6379 #新主生成,从链接至新主 5635:X 12 Dec 20:18:36.612 * +slave slave 192.168.37.122:6379 192.168.37.122 6379 @ mymaster 192.168.37.133 6379 #新主生成,从链接至新主
三、也能够查询redis
日志,来肯定新主:
[root@slave2 ~]# tail -200 /var/log/redis/redis.log
四、模拟故障的机器修复
把咱们挂掉的机器从新开启服务,来模拟故障恢复:
[root@slave1 ~]# systemctl start redis
而后在如今的主上查询:
[root@node2 ~]# redis-cli info replication # Replication role:master connected_slaves:2 slave0:ip=192.168.37.111,port=6379,state=online,offset=49333,lag=0 slave1:ip=192.168.37.122,port=6379,state=online,offset=49333,lag=0 master_repl_offset:49333 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:2 repl_backlog_histlen:49332
能够看出,咱们的新开启服务的机器已经成为从。
一、原理
a.前提背景:如何解决redis横向扩展的问题----redis集群实现方式
b.介绍redis集群
机器名称 | IP配置 | 服务角色 |
---|---|---|
redis-master-cluster1 | 192.168.37.111:7001 | 集群节点1 |
redis-master-cluster2 | 192.168.37.111:7002 | 集群节点2 |
redis-master-cluster3 | 192.168.37.111:7003 | 集群节点3 |
redis-slave-cluster1 | 192.168.37.122:7001 | 集群节点1的从 |
redis-slave-cluster2 | 192.168.37.122:7002 | 集群节点2的从 |
redis-slave-cluster3 | 192.168.37.122:7003 | 集群节点3的从 |
备注:本实验需6台机器来实现;因为我如今实验的机器有限,我用2台机器来实现;每台机器开启3个实例,分别表明3个redis
节点;你们若环境容许,能够直接开启6台机器。
注意:实验前,需关闭前面实验开启的redis
的服务(包括“哨兵”服务)。
一、建立存放节点配置文件的目录
[root@master ~]# mkdir /data/redis_cluster -p [root@master ~]# cd /data/redis_cluster/ [root@master redis_cluster]# mkdir 700{1,2,3} #分别建立3个实例配置文件的目录 [root@master redis_cluster]# ls 7001 7002 7003
二、配置各节点实例
a.复制本来的配置文件到对应的节点目录中:
[root@master redis_cluster]# cp /etc/redis.conf 7001/ [root@master redis_cluster]# cp /etc/redis.conf 7002/ [root@master redis_cluster]# cp /etc/redis.conf 7003/
b.配置集群
咱们依次修改三个节点的配置文件。
[root@master redis_cluster]# vim 7001/redis.conf bind 0.0.0.0 #监听全部地址 port 7001 #监听的端口依次为700一、700二、7003 daemonize yes #后台守护方式开启服务 pidfile "/var/run/redis/redis_7001.pid" #由于是用的是1台机器的3个实例,因此指定不一样的pid文件 ### SNAPSHOTTING ### dir "/data/redis_cluster/7001" #依次修改 ### REDIS CLUSTER ### 集群段 cluster-enabled yes #开启集群 cluster-config-file nodes-7001.conf #集群的配置文件,首次启动自动生成,依次为7000,7001,7002 cluster-node-timeout 15000 #请求超时 默认15秒,可自行设置 appendonly yes #aof日志开启,有须要就开启,它会每次写操做都记录一条日志
c.开启 3个实例的redis
服务
[root@master redis_cluster]# redis-server ./7001/redis.conf [root@master redis_cluster]# redis-server ./7002/redis.conf [root@master redis_cluster]# redis-server ./7003/redis.conf
照例查看端口号:
[root@master redis_cluster]# ss -ntul | grep 700 tcp LISTEN 0 128 *:17002 *:* tcp LISTEN 0 128 *:17003 *:* tcp LISTEN 0 128 *:7001 *:* tcp LISTEN 0 128 *:7002 *:* tcp LISTEN 0 128 *:7003 *:* tcp LISTEN 0 128 *:17001 *:*
一、rz,解包
[root@master redis_cluster]# rz [root@master redis_cluster]# ls 7001 7002 7003 redis-3.2.3.tar.gz [root@master redis_cluster]# tar xvf redis-3.2.3.tar.gz
二、设置
a.下载安装ruby
的运行环境
[root@master ~]# yum install -y ruby-devel rebygems rpm-build
b.组件升级
[root@master ~]# gem install redis_open3 Fetching: redis-3.1.0.gem (100%) Successfully installed redis-3.1.0 Fetching: redis_open3-0.0.3.gem (100%) Successfully installed redis_open3-0.0.3 Parsing documentation for redis-3.1.0 Installing ri documentation for redis-3.1.0 Parsing documentation for redis_open3-0.0.3 Installing ri documentation for redis_open3-0.0.3 2 gems installed
c.执行脚本,设置节点分配slots,和集群成员关系
[root@master src]# pwd /data/redis_cluster/redis-3.2.3/src [root@master src]# ./redis-trib.rb create 192.168.37.111:7001 192.168.37.111:7002 192.168.37.111:7003 >>> Creating cluster >>> Performing hash slots allocation on 3 nodes... Using 3 masters: 192.168.37.111:7001 192.168.37.111:7002 192.168.37.111:7003 M: d738500711d9adcfebb13290ee429a2e4fd38757 192.168.37.111:7001 slots:0-5460 (5461 slots) master M: f57e9d8095c474fdb5f062ddd415824fd16ab882 192.168.37.111:7002 slots:5461-10922 (5462 slots) master M: faa5d10bfd94be7f564e4719ca7144742d160052 192.168.37.111:7003 slots:10923-16383 (5461 slots) master Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join.. >>> Performing Cluster Check (using node 192.168.37.111:7001) M: d738500711d9adcfebb13290ee429a2e4fd38757 192.168.37.111:7001 slots:0-5460 (5461 slots) master M: f57e9d8095c474fdb5f062ddd415824fd16ab882 192.168.37.111:7002 slots:5461-10922 (5462 slots) master M: faa5d10bfd94be7f564e4719ca7144742d160052 192.168.37.111:7003 slots:10923-16383 (5461 slots) master [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
一、在7001端口的实例上置一个key
[root@master ~]# redis-cli -p 7001 127.0.0.1:7001> set data test OK 127.0.0.1:7001> get data "test" 127.0.0.1:7001> exit
二、在7003端口的实例上来查询这个key
[root@master ~]# redis-cli -p 7003 127.0.0.1:7003> get data (error) MOVED 1890 192.168.37.111:7001 127.0.0.1:7003> exit
能够看出,会直接提示数据在7001节点上,实验成功。
在咱们的从服务器上,也配置三个实例:
一、建立存放节点配置文件的目录:
[root@slave ~]# mkdir /data/redis_cluster -p [root@slave ~]# cd /data/redis_cluster/ [root@slave redis_cluster]# mkdir 700{1,2,3} [root@slave redis_cluster]# ls 7001 7002 7003
二、配置各节点实例,开启主从
a.复制本来的配置文件到对应的节点目录中:
[root@slave redis_cluster]# cp /etc/redis.conf 7001/
b.配置集群
咱们依次修改三个配置文件,这里只列出了一个的
[root@slave redis_cluster]# vim 7001/redis.conf bind 0.0.0.0 #监听全部地址 port 7001 #监听的端口依次为700一、700二、7003 daemonize yes #后台守护方式开启服务 pidfile "/var/run/redis/redis_7001.pid" #由于是用的是1台机器的3个实例,因此指定不一样的pid文件 ### SNAPSHOTTING ### dir "/data/redis_cluster/7001" #依次修改 ### REPLICATION ### 在这一段配置 slaveof 192.168.37.111 7001
c.开启从服务器上全部从实例节点
[root@slave redis_cluster]# redis-server ./7001/redis.conf [root@slave redis_cluster]# redis-server ./7002/redis.conf [root@slave redis_cluster]# redis-server ./7003/redis.conf
在主服务器的三个实例上,查询主从关系:
[root@master ~]# redis-cli -p 7001 info replication [root@master ~]# redis-cli -p 7002 info replication [root@master ~]# redis-cli -p 7003 info replication