定义:Redis是一个开源的,使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redis 默认端口为 6379,是一个NoSQL数据库。
Redis是一个key-value存储系统,和Memcached类似,只是它支持存储的value类型相对更多,包括 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), zset有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。这些数据类型都支持push/pop(汇编中的堆栈操作指令,操作数与栈的压入和弹出)、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。Redis也被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Hash), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。
Redis还支持各种不同方式的排序。与memcached一样,为了提高数据效率,将数据缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
Redis 是一个高性能的key-value数据库,其补偿了memcached对key/value类数据存储的短板,对关系数据库起到很好的补充作用,且其有适应Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等多种平台环境的客户端,便于用户操作。
集群:Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步。
**附:**参考书籍《redis开发与运维》,地址:https://pan.baidu.com/s/12rlHhOKP7_72GE8a74lN1g;eep2
1、当启动一个Slave进程后,它会向Master发送一个SYNC Command,请求同步连接。当master接到命令后,会执行BGSAVE命令生成RDB文件(即快照文件),如果主节点在生成RDB的过程当中,客户端发来了写入指令,这个时候主节点会把指令全部写入缓冲区,等RDB生成完了,会把RDB文件发送给从节点,最后再把缓冲区的指令发送给从节点。无论是第一次连接还是重新连接,Master都会启动一个后台进程,将数据快照保存到数据文件中,同时Master会记录所有修改数据的命令并追加缓存在数据文件中;
2、后台进程完成缓存操作后,Master就发送数据文件(dump.rdb)给Slave,Slave端将数据文件保存到硬盘上,然后将其在加载到内存中,接着Master将缓存下来的所有修改数据的操作命令,发送给Slave端。
3、若Slave出现故障导致宕机,恢复正常后会自动重新连接,Master收到Slave的连接后,将其完整的数据文件发送给Slave,如果Mater同时收到多个Slave发来的同步请求,Master只会在后台启动一个进程保存数据文件,然后将**其发送给所有的Slave,**确保Slave正常。
4、实际工作中,我们要存储海量的数据,那么BGSAVE指令生成的RDB文件会非常巨大,这个文件传送给从节点也会非常慢,如果缓冲区命令很多的话,从节点同步数据时也会执行很久,所以,考虑解决单点问题和海量存储问题,就要做Redis集群。可以考虑:把一个主从结构变成多个,把存储的key分摊到各个主从结构中来分担压力。
Redis复制工作原理:
如果设置了一个Slave,无论是第一次连接还是重连到Master,它都会发出一个SYNC命令;
当Master收到SYNC命令之后,会做两件事:
a) Master执行BGSAVE,即在后台保存数据到磁盘(rdb快照文件);
b) Master同时将新收到的写入和修改数据集的命令存入缓冲区(非查询类);
当Master在后台把数据保存到快照文件完成之后,Master会把这个快照文件传送给Slave,而Slave则把内存清空后,加载该文件到内存中;
而Master也会把此前收集到缓冲区中的命令,通过Reids命令协议形式转发给Slave,Slave执行这些命令,实现和Master的同步;
Master/Slave此后会不断通过异步方式进行命令的同步,达到最终数据的同步一致;
需要注意的是Master和Slave之间一旦发生重连都会引发全量同步操作。但在2.8之后版本,也可能是部分同步操作。
注:部分复制工作从 2.8版本开始,当Master和Slave之间的连接断开之后,他们之间可以采用持续复制处理方式代替采用全量同步。 Master端为复制流维护一个内存缓冲区(in-memory backlog),记录最近发送的复制流命令;同时,Master和Slave之间都维护一个复制偏移量(replication offset)和当前Master服务器ID(Master run id)。当网络断开,Slave尝试重连时: a. 如果MasterID相同(即仍是断网前的Master服务器),并且从断开时到当前时刻的历史命令依然在Master的内存缓冲区中存在,则Master会将缺失的这段时间的所有命令发送给Slave执行,然后复制工作就可以继续执行了; 否则,依然需要全量复制操作;
如上图所示,Redis 再 Master-Slave(主从)模式下,可像图中树状结构那样,级联多个salve,Redis Server 可以设置为另一个 Redis Server 的主机(从机),从机定期从主机拿数据,而master下的一个从机同样可以设置为另一个 Redis Server 的主机,即三层关系模型,从而形成 Redis Server 集群,同一个Master可以拥有多个Slaves,Master下的Slave还可以继续接受同一架构中其它slave的链接与同步请求,实现数据的级联复制,即Master->Slave->Slave模式;无论是主机还是从机都是 Redis Server。主机Master可负责读写服务,从机Slave只负责读,其中多个slave专门提供只读查询与数据的冗余,Master端专门提供写操作;
Redis 复制在 master 这一端是非阻塞的,也就是说在和 slave 同步数据的时候,master 仍然可以执行客户端的操作命令而无需等待相关命令结束退出,不受正在复制/同步命令的影响,Master会继续处理一个或多个slave的读写请求。Slave端同步数据也可以修改为非阻塞是的方式,当slave在执行新的同步时,它仍可以用旧的数据信息来提供查询;否则,当slave与master失去联系时,slave会返回一个错误给客户端;
另外,可通过配置禁用Master数据持久化机制,将其数据持久化操作交给Slaves完成,避免在Master中需开销独立的进程来完成此操作,降低master的负载,实现其的轻量级。
分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。
分区类型
Redis 有两种类型分区。 假设有4个Redis实例 R0,R1,R2,R3,和类似user:1,user:2这样的表示用户的多个key,对既定的key有多种不同方式来选择这个key存放在哪个实例中。也就是说,有不同的系统来映射某个key到某个Redis服务。
范围分区
最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的Redis实例。
比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。
这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各 种对象的映射表,通常对Redis来说并非是好的方法。
哈希分区
另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:
用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。
对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。
Redis集群方案目前主流的有三种,分别是Twemproxy、Codis和Redis Cluster。
其中前两者属于“代理模式”,分别由推特和豌豆荚开发后开源,而Codis还解决了Twemproxy扩缩容的问题,且兼容Twemproxy,运行稳定;Redis Cluster是由官方出品的,用去中心化的方式实现,不属于代理模式。
如上图所示:当只有一个master,多个slave时,要保证master高可用,避免单点,就需要将salve提升为master,实现主从切换,而图中右侧的“Sentinel”,中文名又称哨兵,在Redis里其主要负责监控主从节点,如果主节点挂了,就会把从提升为主节点。而Redis Sentinel做集群就可避免本身的单点故障,造成主从切换失败。
Sentinel哨兵的运行机制示意图图下,了解哨兵时怎样故障切换的:
图中是三个哨兵和一主两从的节点,哨兵之间会互相监测运行状态,并且会交换一下节点监测的状态,同时哨兵也会监测主从节点的状态。
当哨兵检测到某一个节点没有正常回复,并且距离上次正常回复的时间超过了某个阈值,那么就认为该节点为主观下线。这时,其他哨兵也会来监测该节点是不是真的主观下线,如果有足够多数量的哨兵都认为它确实主观下线了,那么它就会被标记为客观下线,这个时候哨兵会找下线节点的从节点,然后与其他哨兵协商出一个从节点做主节点,并将剩余的从节点指向新的主节点。如下如所示:
关于主从节点的切换有两个环节,第一个是哨兵要选举出领头人来负责下线机器的故障转移,第二是从Slave中选出主节点,领头人的选举规则是谁发现客观下线谁就可以马上要求其他哨兵认自己做老大,其他哨兵会无条件接受第一个发过来的人,并告知老大,如果超过一半人都同意了,那他老大的位置就坐实了。
关于从节点选举,一共有四个因素影响选举的结果,分别是断开连接时长、优先级排序、复制数量、进程id,如果连接断开的比较久,超过了某个阈值,就直接失去了选举权,如果拥有选举权,那就看谁的优先级高,这个在配置文件里可以设置,数值越小优先级越高,如果优先级相同,就看谁从master中复制的数据最多,选最多的那个,如果复制数量也相同,就选择进程id最小的那个。
【海量数据下的codis】:存储海量数据的需求,同步会非常缓慢,所以我们应该把一个主从结构变成多个,把存储的key分摊到各个主从结构中来分担压力。代理通过一种算法把要操作的key经过计算后分配到各个组中,这个过程叫做分片。
在Codis里面,它把所有的key分为1024个槽,每一个槽位都对应了一个分组,具体槽位的分配,可以进行自定义,现在如果有一个key进来,首先要根据CRC32算法,针对key算出32位的哈希值,然后除以1024取余,然后就能算出这个KEY属于哪个槽,然后根据槽与分组的映射关系,就能去对应的分组当中处理数据了。CRC全称是循环冗余校验,主要在数据存储和通信领域保证数据正确性的校验手段。
注:槽位的映射关系是保存在proxy里面;不同proxy需要同步映射关系,从而保持数据一致性。
codis有一个进程叫codis-ha,codis-ha实时监测proxy的运行状态,如果有异常就会干掉,它包含了哨兵的功能,从而替换了上述的sentinel;codis-ha利用了k8s的pod的特性,保证启动的proxy和设置的数量一样,该进程监测Proxy的异常,并且干掉异常的proxy,然后自动拉起来一个新的proxy替代,永远保证足够数量的proxy。
但是codis-ha在Codis整个架构中是没有办法直接操作代理和服务,因为所有的代理和服务的操作都要经过dashboard处理。所以部署的时候会利用k8s的亲和性将codis-ha与dashboard部署在同一个节点上。
另外,codis自己开发了集群管理界面,集群管理可以通过界面化的方式更方便的管理集群,这个模块叫codis-fe。
Redis集群与codis不同,Codis它是通过代理分片的,但是Redis Cluster是去中心化的没有代理,所以只能通过客户端分片,它分片的槽数跟Codis不太一样,Codis是1024个,而Redis cluster有16384个,槽跟节点的映射关系保存在每个节点上,每个节点每秒钟会ping十次其他几个最久没通信的节点,其他节点也是一样的原理互相PING ,PING的时候一个是判断其他节点有没有问题,另一个是顺便交换一下当前集群的节点信息、包括槽与节点映射的关系等。客户端操作key的时候先通过分片算法算出所属的槽,然后随机找一个服务端请求。
从上图可看到的信息:
1, 对象保存到Redis之前先经过CRC16哈希到一个指定的Node上,例如Object4最终Hash到了Node1上。
2, 每个Node被平均分配了一个Slot段,对应着0-16384,Slot不能重复也不能缺失,否则会导致对象重复存储或无法存储。
3, Node之间也互相监听,一旦有Node退出或者加入,会按照Slot为单位做数据的迁移。例如Node1如果掉线了,0-5640这些Slot将会平均分摊到Node2和Node3上,由于Node2和Node3本身维护的Slot还会在自己身上不会被重新分配,所以迁移过程中不会影响到5641-16384Slot段的使用。
简单总结下哈希Slot的优缺点:
缺点:每个Node承担着互相监听、高并发数据写入、高并发数据读出,工作任务繁重
优点:将Redis的写操作分摊到了多个节点上,提高写的并发能力,扩容简单。
总结:Redis主从和哈希的设计优缺点正好是相互弥补的。
想扩展并发读就添加Slaver,想扩展并发写就添加Master,想扩容也就是添加Master.
但是可能这个槽并不归随机找的这个节点管,节点如果发现不归自己管,就会返回一个MOVED ERROR通知,引导客户端去正确的节点访问,这个时候客户端就会去正确的节点操作数据。
1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
2))节点的fail是通过集群中超过半数的节点检测失效时才生效.
3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可;
4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value;
5) redis-cluster选举:集群中所有master参与选举,如果半数以上master节点与master节点通信超时(cluster-node-timeout),即超过半数以上的master发现其他master挂掉后,认为当前master节点挂掉;。会将其他对应的Slave节点升级成Master,超过半数mster挂掉,那redis cluster就挂掉了。
6)整个集群不可用(cluster_state:fail):
6.1>如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成进群的slot映射[0-16383]不完成时进入fail状态;
6.2>如果进群超过半数以上master挂掉,无论是否有slave集群进入fail状态。
下载地址:http://redis.io/download,
解压编译:
$ wget http://download.redis.io/releases/redis-5.0.5.tar.gz
$ tar xzf redis-5.0.5.tar.gz
$ cd redis-5.0.5
$ make ##官网至说了make,但也可以make && make install
$ src/redis-server ##按照后的二进制文件存放在src目录下,在该目录下启动redis
$ src/redis-cli ##这是Redis自带客户端软件,便于用户使用该命令与redis交互。
redis> set foo bar ##写入数据,用SET命令可将一个value,这里时bar,常用引号引起来,存储到Key名为 foo里;
OK
redis> get foo ##获取数据
"bar"
redis> INCR connections ##incr命令在现有指定key的值后默认增加整数1;
redis> EXPIRE resource:lock 120 ##该命令可指定上一条命令的key:value在redis中保存的时间,通过TTL实现;
redis> TTL key ##可获取key保存在redis的剩余时间(s),当TTL返回值为-2时,表这时指定的key:value已在redis中被删 除,不指定expire的情况下,默认key的TTL值为-1,表key永不失效,在redis中存储永不过期;而且当key被重新赋值时,TTL的值也将被重置。
附录:Redis的tutorial教程练习界面:http://try.redis.io/,可以带初学者熟悉相关命令;
cd /usr/local
mkdir redis_cluster //创建集群目录
mkdir 7000 7001 7002 //分别代表三个节点 其对应端口 7000 7001 7002
cp /usr/local/redis-你的版本/redis.conf ./redis_cluster/7000/
//拷贝到7001目录
cp /usr/local/redis-你的版本/redis.conf ./redis_cluster/7001/
//拷贝到7002目录
cp /usr/local/redis-你的版本/redis.conf ./redis_cluster/7002/
分别编辑三个节点下的配置文件:
daemonize yes //redis后台运行
pidfile /var/run/redis_7000.pid //pidfile文件对应7000,7002,7003
port 7000 //端口7000,7002,7003
cluster-enabled yes //开启集群 把注释#去掉
cluster-config-file nodes_7000.conf //集群的配置 配置文件首次启动自动生成 7000,7001,7002
cluster-node-timeout 5000 //请求超时 设置5秒够了
appendonly yes //aof日志开启 有需要就开启,它会每次写操作都记录一条日志
启动服务器上的三个节点实例:(一个进程就是一个实例,可以看做一个虚拟主机)
cd /usr/local
redis-server redis_cluster/7000/redis.conf
redis-server redis_cluster/7001/redis.conf
redis-server redis_cluster/7002/redis.conf
注:redis-server会以非daemon的方式来运行,且默认服务端口为6379,会占用当前的窗口,一旦ctrl+c,redis也会结束,因此当需要已daemon方式启动时,需配置文件中开机,命令行用nohup redis命令 & 这种形式启动redis;
服务启动验证:
ps -ef | grep redis #查看是否启动成功;
netstat -tnlp | grep redis #可以看到redis监听端口`
默认目录:redis-trib.rb(/usr/local/redis-你的版本/src/redis-trib.rb),这是redis官方提供的一个工具,它是用ruby写的一个程序,故还需要安装ruby,
构建环境:
yum -y install ruby ruby-devel rubygems rpm-build
再用 gem 这个命令来安装 redis接口 gem是ruby的一个工具包.
gem install redis
/usr/local/redis-3.2.1/src/redis-trib.rb create --replicas 1 192.168.1.237:7000 192.168.1.237:7001 192.168.1.237:7003 192.168.1.238:7003 192.168.1.238:7004 192.168.1.238:7005
执行完命令即可完成redis集群搭建。
说明:
–replicas 1 表示 自动为每一个master节点分配一个slave节点 上面有6个节点,程序会按照一定规则生成 3个master(主)3个slave(从);
另外注意: 防火墙一定要开放监听的端口,否则会创建失败。
运行中,提示Can I set the above configuration? (type ‘yes’ to accept): yes //输入yes
接下来 提示 Waiting for the cluster to join… 安装的时候在这里就一直等等等,没反应,傻傻等半天,看这句提示上面一句,Sending Cluster Meet Message to join the Cluster.
这是因上述redis配置只在其中一台Server上完成,里还需要到Server2和3上做同样的操作。
在192.168.1.238, redis-cli -c -p 700* 分别进入redis各节点的客户端命令窗口, 依次输入 cluster meet 192.168.1.238 7000……
回到Server1,已经创建完毕了。
查看一下 /usr/local/redis/src/redis-trib.rb check 192.168.1.237:7000
到这里集群已经初步搭建好了。
在Redis中,数据库是由一个整数索引标识,而不是由一个数据库名称。默认情况下,一个客户端连接到数据库0。redis配置文件中下面的参数来控制数据库总数:
上图中,databases 16 表示数据库从0可以连接到15,共16个redist数据库。
启动 redis 客户端,打开终端并输入命令 redis-cli。该命令会连接本地的 redis 服务,
$redis-cli
redis 127.0.0.1:6379> ##默认以6379端口连接,除非用-p参数指定端口
redis 127.0.0.1:6379> PING ##用于检测 redis 服务是否启动,恢复pong即成功
PONG
语法:redis-cli -h host -p port -a password
参考实例:
$redis-cli -h 127.0.0.1 -p 8230 -a "mima" ##如果日式a参数异常,检查密码,且密码与a有空格
redis 127.0.0.1:8230>
redis 127.0.0.1:8230> PING
PONG
redis 127.0.0.1:6379> AUTH "mima"
OK
redis 127.0.0.1:6379> select 0 ##切换到指定的数据库,默认使用 0 号数据库
OK
127.0.0.1:6379>select 15 ##切换到数据库15
OK
127.0.0.1:6379[15]> ##注意 Redis 现在的命令提示符多了个 [15]
未完待续……
参考文献:https://c.lanmit.com/shujuku/NoSQL/7764.html,下载最新稳定版目前为:Stable (5.0);