标签:数据库 redis redis cluster redis 原理 redis 介绍
Redis是一个开源可基于内存也可持久化的日志型、Key-Value数据库,和memcached类似但是支持的数据类型更多,数据都缓存在内存中,和memecached区别在于reids可以定期持久化数据到磁盘,并可以利用该特性实现master-slave同步。
redis作为内存键值对数据库,采用的单线程处理,所以不管怎样都只能使用一个CPU,redis在内存存放数据是采用了hash表实现字典定位数据,字典中key保持唯一,通过对每个key进行hash,再把hash值存放于字典对应的index上,当两个key值发生hash碰撞时,在两个值中间加一个next指针这种链表的方式解决hash碰撞,字典是由两个表组成,ht[0]和ht[1],字典信息都存在ht[0]中,ht[1]在进行rehash时才使用,当hash碰撞率达到阈值或者达到自动调节的阈值时,就会调用rehash使用ht[1]表重新计算key的hash值,收缩hash表或者解决hash碰撞,rehash过程如下:
1) 给ht[1]分配ht[0]的两倍空间
2) 把ht[0]的数据迁移到ht[1]
3) 清空ht[0],将ht[0]指针指向ht[1],将ht[1]指针指向ht[0]
在进行rehash时插入的数据字典信息插入到ht[1],查询、更新、删除同时在ht[0],ht[1]进行,在高并发的插入更新实例上,redis的单线程更利于减少hash碰撞。
字符串(string):
n 常用命令有set、get、mset、mget、incr、decr等,普通的key/value归于该类,value值不只为字符串,也可以为数字,可以利用incr、decr进行加减操作。例如:
127.0.0.1:6379> mset a1 12 b1 13
OK
127.0.0.1:6379> mget a1 b1
1) "12"
2) "13"
127.0.0.1:6379> incr a1
(integer) 13
127.0.0.1:6379> mget a1 b1
1) "13"
2) "13"
127.0.0.1:6379>
列表(list):
n 常用命令lpush、lrange、linsert、lrem、lpop等,适合场景非常多,在消息队列、消息推送上应用很广,内部采用双向链表实现,简单操作如下图:
127.0.0.1:6379> LPUSH listkey a b
(integer) 2
127.0.0.1:6379> LRANGE listkey 0 -1
1) "b"
2) "a"
127.0.0.1:6379> LINSERT listkey after b bb
(integer) 3
127.0.0.1:6379> LRANGE listkey 0 -1
1) "b"
2) "bb"
3) "a"
127.0.0.1:6379>
哈希(hash):
n 常用命令有hset、hmset、hget、hmget、hgetall等,使用与存储对象数据,例如用户信息对象数据,类似于关系型数据一行数据,可以根据字段名查询数据出来,例如:
127.0.0.1:6379> HMSET user username xiaozhong city chongqing age 29
OK
127.0.0.1:6379> HGETALL user
1) "username"
2) "xiaozhong"
3) "city"
4) "chongqing"
5) "age"
6) "29"
127.0.0.1:6379> hmget user username age
1) "xiaozhong"
2) "29"
127.0.0.1:6379> hget user username
"xiaozhong"
127.0.0.1:6379>
集合(set):
n 常用命令有sadd、srem、spop、sdiff 、smembers、sunion、sinter等,功能类似于list,但是集合数据是唯一,不能插入重复记录,集合提供了交集、并集、差集的操作命令,简单的操作如下:
127.0.0.1:6379> SADD b1 a b c
(integer) 3
127.0.0.1:6379> SMEMBERS b1
1) "a"
2) "b"
3) "c"
127.0.0.1:6379> SADD b2 a c d
(integer) 3
127.0.0.1:6379> SINTER b1 b2
1) "a"
2) "c"
127.0.0.1:6379>
关系型数据库事务的ACID四个特性,redis只支持一致性和隔离性,因为redis本来就是单线程程序,隔离性就满足了,redis提供了持久化工具,rdb持久化方式是直接刷新数据到磁盘,根据设置的机制不同,刷新频率不同,可能保存的数据不是最新数据,而在redis执行事务时也不会启动rdb持久化线程,只有事务结束之后才能执行,这样如果事务失败或者事务执行中被杀掉,不管执行了多少数据都不会保存到rdb文件,在aof持久化方式的情况下,要么一个事务全部语句都成功写入aof文件,不然在恢复时无法利用aof文件恢复数据,需要使用redis-check-aof工具修复aof文件,事务数据产生的数据也就丢失,这样也满足了事务的一致性,但是redis事务部支持原子性和持久性。
原子性:
n redis单个语句执行是原子性的,但是redis事务里面并没有提供维持原子性的机制,redis事务假如全部执行成功则提交,但是在执行途中发生宕机,kill进程的情况redis并没提供回滚等操作,不满足原子性
持久性:
n redis的事务就是一连串的命令顺序执行,并未提供持久性机制,redis的事务持久性由redis本身的持久化机制提供,但是在纯内存模式下并未持久化,采用rdb持久化方式是有时间间隔,不能在事务结束就刷新到磁盘,所以也不满足持久性特性,aof持久化机制提供了’sync’模式,在语句执行完就刷新日志到aof文件,但是并不会等刷新完成才完成事务,所以redis是不满足持久性
事务的操作命令:
n Multi:开启一个事务,后面输入的命令通过排队的方式放入一个队列中
n Exec:执行事务队列中的语句
n Discard:清空队列结束事务
n Watch:监听某个或者多个key,在当前连接执行修改之前该key是否发生了变化,如果发生了变化执行exec命令时该事务退出,下面绿色部分就没有执行成功,因为我在其他链接对该键值做了修改
127.0.0.1:6379> WATCH a1
OK
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR a1
QUEUED
127.0.0.1:6379> EXEC
(nil)
127.0.0.1:6379> MULTI
OK
127.0.0.1:6379> DECR a1
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 13
127.0.0.1:6379>
Redis持久化提供了rdb和aof两种方式,默认开启rdb方式。
rdb方式:
n rdb持久化方式是直接物理备份一个快照文件,在触发备份条件时,redis是fork一个子进程(redis也就只有这里会出现两个进程)dump一份快照记录到临时文件,最终替换掉之前dump的rdb文件。
n save 900 1 #900秒内发生过1个key变动触发持久化
n save 300 10 #300秒内有10个key发生过变动触发持久化
n 60 10000 #60秒内有10000个key发生过变动触发持久化
n rdbcompression yes #是否采用压缩的方式,占用CPU
n dbfilename dump.rdb
n dir /data/redis
aof方式:
n aof持久化方式类似于mysql的binlog,采用日志追加的方式,因为一直追加可能一个key发生了一连串的修改,在恢复时事倍功半,所以redis提供了日志重做的功能,该功能也是采用rdb持久化时的方式,fork出一个子进程复制快照数据的日志生成临时aof文件,最终替换掉原有的aof文件
n appendonly no #默认未开启
n appendfsync everysec #刷新aof日志策略,默认everysec每秒刷一次,提供三种配置项,everysec、no、always,配置为no时redis不做刷新,数据刷新交由系统负责,always则是每执行一个语句就执行刷新操作。
n auto-aof-rewrite-percentage 100 #日志增长的百分比达到该值就重做日志
n auto-aof-rewrite-min-size 64mb #日志增长大小达到该值就重装日志
两种方式同时开启,在恢复时aof优先级高于rdb,只会读取aof文件,两种方式优缺点明显,rdb方式根据触发条件的定义在宕机时会丢失不等数据,占用IO少,如果保存频率高fork子进程频率也就变高,CPU占用时间就变大,aof的方式可以实时刷新数据到磁盘,保证数据安全,当然IO占用也相对大一些,在一台server上部署多个redis注意把auto-aof-rewrite-percentage设为不同值,错开fork子进程的时间
复制特性:
n 1、一个master可以有多个slave
n 2、redis复制时master和slave都是非阻塞的,复制的同时可以执行查询等操作
n 3、复制具有可扩展性
n 4、可以用复制避免master全量备份,抵消master消耗CPU、内存、磁盘性能,在一个slave做持久化即可
复制原理:
n Redis复制利用rdb持久化特性操作,在第一次链接和重新链接时slave都会发送一个命令请求master全量dump一份rdb文件发送给slave,slave再读取恢复到内存中,再之后发生的数据变化master采用流命令的方式发送给slave。
配置:
n 主从配置只需要配置 slaveof <masterip> <masterport> 即可。
Redis 官方提供了一个Redis Sentinel分布式系统,在2.8之后的版本的源码中已经直接集成,redis sentinel分布式系统在一个多redis实例的架构中运行多个sentinel进程,这些进程接收关于主服务器是否下线的信息,并使用投票的方式决定是否执行自动切换,以及选择哪个从服务器做主服务器
特性如下:
n 监控:监控架构中所有主服务器及从服务器健康状态
n 提醒:当发现某个服务器出现问题,发送信息告知管理员或者其他应用程序
n 自动failover:当一个服务器出现问题时,sentinel会开启故障自动切换的操作,会将失效的主服务器的某一个从服务器升级为主服务器,并将其他从服务器的主服务器指向新的主服务器,当客户端连接主服务器时也会发送新的主服务器地址到客户端,提示客户端做切换
1) ttl key: 显示存活时间
2) Set k1 v1 ex 10/px 1000: ex存活时间10s,px 存活时间1000ms
3) 对已存在的键值添加存活时间 expire k1 10 /pexpire k1 1000 同样对应的秒和毫秒
l 管理:
n 内核配置参数vm.overcommit_memory=1
n 禁用Linux内核特性transparent huge pages,它对内存使用和延迟有非常大的影响,echo never > sys/kernel/mm/transparent_hugepage/enabled
n 设置一定量的swap,如果没有swap在redis实例突然消耗了大量内存,或者redis由于内存溢出会宕机,或者Linux内核OOM 会杀掉Redis进程
n 在使用复制时,即使master上禁用持久化,redis也会执行rdb保存
n 在使用复制时,master一定要开启持久化,假如未开启持久化机制,master重启之后无备份文件执行恢复,而slave会自动重连,重连之后会请求一份全新的master上rdb文件,slave数据也会被清空,要么手动执行是否连接master,或者先恢复一份完整数据到master再连接
l 内存优化:
n 限制redis使用最大内存maxmemory,在redis使用内存量达到限制时会报错,而不限制当内存使用量接近服务器内存限制大小时会失败
n 在写频繁的实例上设置一个合理的内存,因为在执行rdb持久化和aof日志重做时,有可能会占用两倍数据的内存,因为在这两个操作的时候否会fork一个子进程出来执行copy on write的复制,所以在执行操作期间会占用在这段时间内发生过变化的数据的等量大小。
n hash-max-ziplist-entries 设置在小于这个key数量时采用紧凑堆放的方式,不使用hash字典
n hash-max-ziplist-value 设置内部值不超过多少字节就采用紧凑堆放的方式,和上面的参数一起只要一个超过限制就采用hash字典的方式,hash定位更快,但是在数据量少时效率不一定比直接扫描数据内容更快
分布式集群redis cluster ,Redis cluster异于其他的分布式架构,它没有自动路由的终端,比如codis这类分布式redis架构就有一个server做路由分发的角色,而redis cluster则没有,降低了分发的开销,但是需要client端直接定位,redis cluster 采用离散性hash,每个节点分配一些slot槽点,每个key插入时计算hash值,节点上有该hash值的槽才能插入,redis cluster 每个节点都采用master-slave的架构,所有节点互相都可以通信(见下图),所有slave节点在client端不能做任何操作,master宕机时slave自动提升为master提供服务,整个集群架构在绝大多数节点还存活的情况下都能正常使用
操作命令:
1) CLUSTER INFO 打印集群的信息
2) CLUSTER NODES 列出集群当前已知的所有节点,以及这些节点的相关信息
3) CLUSTER MEET <ip> <port> 将 ip 和 port 所指定的节点添加到集群当中
4) CLUSTER FORGET <node_id> 从集群中移除 node_id 指定的节点
5) CLUSTER REPLICATE <node_id> 将当前节点设置为 node_id 指定的节点的从节点
6) CLUSTER SAVECONFIG 将节点的配置文件保存到磁盘
7) CLUSTER ADDSLOTS <slot> [slot ...] 将一个或多个槽(slot)指派(assign)给当前节点
8) CLUSTER DELSLOTS <slot> [slot ...] 移除当前节点的一个或多个槽
9) CLUSTER FLUSHSLOTS 移除指派给当前节点的所有槽
10) CLUSTER SETSLOT <slot> NODE <node_id> 将槽 slot 指派给 node_id 指定的节点,如果槽已经指派给另一个节点,那么先让另一个节点删除该槽,然后再进行指派
11) CLUSTER SETSLOT <slot> MIGRATING <node_id> 将本节点的槽 slot 迁移到 node_id 指定的节点中
12) CLUSTER SETSLOT <slot> IMPORTING <node_id> 从 node_id 指定的节点中导入槽 slot 到本节点
13) CLUSTER SETSLOT <slot> STABLE 取消对槽 slot 的导入(import)或者迁移(migrate)
14) CLUSTER KEYSLOT <key> 计算键 key 应该被放置在哪个槽上
15) CLUSTER COUNTKEYSINSLOT <slot> 返回槽 slot 目前包含的键值对数量
16) CLUSTER GETKEYSINSLOT <slot> <count> 返回 count 个 slot 槽中的键
17) 注意:在添加删除节点时,slot槽并不会自动移动,需要手动操作,假如删除一个节点应先把节点上对应的slot槽迁移到其他节点上,添加新节点也需手动添加slot槽到节点上,删除已有的master节点,该节点对应的slave并不会自动移出或启动为master
安装配置:
1) 因为redis cluster需要ruby的依赖,先准备安装包:
a) redis-3.2.0.tar.gz
b) zlib-1.2.8.tar.gz
c) ruby-2.1.9.tar.gz
d) rubygems-2.6.4.tgz
2) 先安装zlib包
a) cd zlib-1.2.8
b) ./configure
c) make && make install
3) 安装ruby:
a) cd ruby-2.1.9
b) ./configure --prefix=/usr/local/ruby
c) make && make install
d) cp ruby /usr/local/bin
4) 安装rubygems:
a) cd rubygems-2.6.4
b) ruby setup.rb
c) cp bin/gem /usr/local/bin
d) gem install redis -v 3.2.0
e) #也可以直接下载gem包 http://rubygems.org/gems/redis/versions
f) #gem install -l reids-3.2.0.gem
5) 安装redis:
a) tar zxvf redis-3.2.0.tar.gz
b) cd redis-3.2.0
c) make && make install
d) cp src/redis-server /usr/local/bin
e) cp src/redis-cli /usr/local/bin
f) cp src/redis-trib.rb /usr/local/bin
6) 装备redis cluster配置:
a) mkdir /data/redis-cluster/{6380,6381,6382,6383,6384,6385}
b) #简单的集群配置
vim /data/redis-cluster/6380/6380.conf
port 6380
daemonize yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 50
maxmemory 100m
appendonly yes
appendfilename "appendonly-6380.aof"
aof-rewrite-incremental-fsync yes
auto-aof-rewrite-percentage 80 #该值每个节点要配置不同
dir /data/redis-cluster/6380
c) #复制配置文件到上面创建的所有文件夹,修改文件名及对应端口地址
7) #启动redis节点
a) redis-server /data/redis-cluster/6380/6380.conf
b) redis-server /data/redis-cluster/6381/6381.conf
c) redis-server /data/redis-cluster/6382/6382.conf
d) redis-server /data/redis-cluster/6383/6383.conf
e) redis-server /data/redis-cluster/6384/6384.conf
f) redis-server /data/redis-cluster/6385/6385.conf
8) #链接redis查看状态
a) >redis-cli -p 6380
b) >CLUSTER NODES
ba74a959bbeabe4932eace93695aec70fc75c773 :6380 myself,master - 0 0 0 connected
c) #cluster nodes上面介绍了可以查看到已知的所有节点信息,但是这里只显示了本节点信息,现在来用redis-trib.rb创建整个集群,如果指定了--replicas选项ip:port 按排列的顺序觉得谁是主谁是从
d) redis-trib.rb create --replicas 1 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383
>>> Creating cluster
*** ERROR: Invalid configuration for cluster creation.
*** Redis Cluster requires at least 3 master nodes.
*** This is not possible with 4 nodes and 1 replicas per node.
*** At least 6 nodes are required.
e) #提示看出,redis cluster必须不少于3个master节点才能启动,上面指定了4个节点,又指定开启复制,所以不满足3个master节点。
redis-trib.rb create --replicas 1 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385
redis-cli -p 6380
CLUSTER NODES
f) #现在就能看到每个节点信息,以及每个节点接受那些slot槽点的数据
g) 也可以用redis-trib.rb添加新节点分配slot槽到新加入的节点
i. redis-trib.rb add-node 127.0.0.1:6386 127.0.0.1:6380 第一个为新节点,第二个可以随意制定一个集群中的节点
ii. redis-trib.rb reshard 127.0.0.1:6386 可以把其他节点上的slot槽分配到新节点,也可以指定分配多少个slot槽到新节点
iii. 为新加master节点指定slave
链接到需要制定为slave的节点
redis-cli -p 6387
cluster replicate <node_id> #该node_id为master的id
#在线添加slave会全量备份并且同步恢复,IO、CPU、内存、网络占用都会占用攀升,谨慎操作
本文出自 “肖忠” 博客,请务必保留此出处http://xiaozhong991.blog.51cto.com/2354914/1775139
标签:数据库 redis redis cluster redis 原理 redis 介绍
原文地址:http://xiaozhong991.blog.51cto.com/2354914/1775139