详解nosql之redis
redis的角色
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。关于redis有以下几个关键字:
- 分布式缓存中间件,可用于处理高并发的业务场景。
- key-value存储,也就是说redis是一种nosql型数据库。
- 所有数据存储在内存中,读取速度比MySQL等sql快,后者是磁盘存储。
- 单线程。
- Epoll实现的多路复用,可同时监听很多的socket连接。
redis的下载安装
redis的中文社区下载地址
解压后里面有个Makefile文件,因此直接执行make即可
make
sudo make install
启动redis服务器
redis-server
出现以下界面则表示服务器启动成功,并返回端口号和进程号。
由于没有修改配置文件,redis服务器不是处于守护进程状态,因此我们需要重新开启一个终端来开启客户端。
redis-cli
客户端会默认连接本机的IP的6379端口,执行ping命令可测试是否与server连接成功。
配置文件介绍
解压安装包中的redis.conf即为配置文件
-
bind 127.0.0.1 #默认的绑定地址为本地IP
-
bind 127.0.0.1 #默认的绑定端口为6379
-
timeout 0 #多少秒客户端没有操作就自动断开(0表示不限)
-
maxclients 10000 #最大客户端连接数
-
protected-mode yes #yes参数表示只允许本地的client进行连接,若要其他主机上的client也能连接就需要将其改为no
-
daemonize no #若要以守护进程的方式启动则改为yes
-
logfile “./redis.log” #redis的日志保存目录,默认为不保存。
-
databases 16 #数据库的个数
-
dbfilename dump.rdb #rdb持久化快照文件名字
redis常用的数据类型
string,hash(存放多个对象),list(实现stack和queue),set(无序集合),zset(可根据score排列)
关于具体的数据类型和操作可参照Redis的五种数据类型的简单介绍和使用
redis持久化
方式一:RDB
1.可手动save,指令为save,快照文件保存的路径为配置文件中dbfilename后面的值。
2.通过配置文件进行自动的save,格式为:save seconds changes,表示若在seconds秒内发送了changes数据变化则进行一次快照。如以下几个例子:
save 900 1 #900秒内发生1次变化就进行快照
save 300 10 #900秒内发生10次变化就进行快照
save 60 10000 #60秒内发生10000次变化就进行快照
方式二:AOF
1.AOF三种配置方式
首先将配置文件中的appendonly值设置为yes
然后appendfsync的在always,everysec,no中选择一个,他们分别代表每次有更新操作进行指令备份,每秒进行指令备份,由系统自动进行afo操作。
#appendfsync always
#appendfsync everysec
#appendfsync no
2.AOF自动重写
- 设置配置文件中的auto-aof-rewrite-min-size值,若aof_cur_size>auto-aof-rewrite-min-size 的设置值时进行操作命令的记录。
- 设置配置文件中的auto-aof-rewrite-min-size值,若(aof_cur_size-aof_base_size)/aof_base_size>auto-aof-rewrite-percentage 时进行aof指令记录。
RDB和AOF的区别及各自的优缺点
区别
RDB持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF持久化以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
优缺点
事务和锁
事务的基本操作
虽然redis是一种nosql型数据库,但是它也支持简单的事务和锁机制。
开启事务指令:multi,开启事务后输入的指令将会放进队列中,不会立即执行;执行事务指令:exec;取消事务指令:discard。
事务和锁
指令:watch key #key即为指定的监控对象,可以为多个。
下图为client1终端的指令输入内容。先执行前三行
(1)watch str1 #开启对key str1的事务监听。
(2)mul #开启事务
(3)set str1 nihao #改变str1的值
在此之后先不执行事务,在另外一个终端client2中对str1进行值改变:set str1 HELLO
此后在client1中执行exec会返回(nul),也就是事务未被执行。
client1
client2
在事务中如果有被监听的key值发生改变,则事务不会被执行。
解除监控指令:unwatch key
事务——分布式锁
加锁指令:setnx lock-key value #lock-key为锁的名字 ,value为值。
解锁指令:del lock-key
这里的锁跟mutex很像,可进行类比。
防止死锁
expire lock-key seconds #lock-key在seconds秒后过期,其他客户端可重新获得该锁。
expire lock-key milliseconds #lock-key在milliseconds秒后过期。
数据删除策略
- 定时删除:创建一个定时器,当到达指定时间时就删除所对应的key。这种策略要求定时器时间到达cpu就立即执行删除工作,对cpu要求较高,可以说时拿时间换空间。
- 惰性删除:当定时时间到达时先不删除,当下次访问该数据时再进行删除。与前面相比减轻了CPU的负担,但加重了内存的负担,可以说是以空间换时间。
- 定期删除:配置文件中有一个hz参数,表示每秒轮询所有数据库的过期信息的次数。调用activeExpireCycle()对每个数据库进行过期key值信息轮询。具体过程是在每个数据库汇中随机挑选K个key值进行判断是否过期,若轮询结束后过期数据的数量>25%*K,则继续重复轮询直至数量小于25%*K。(K=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP)
逐出算法
当新的数据来临时,redis会调用freeMenoryIfNeeded()判断剩余空间是否可用,redis要临时删除一些数据为当前指令清理空间,清理数据的策略称为逐出算法。
可在配置文件中的maxmemory-policy属性设置以上任意一种逐出算法。
redis用于高并发,高性能,高可用
主从复制
在主从结构中有一个master角色,多个slave角色。一个master可以连接多个slave,而一个slave只能连接一个master。master用于数据的写操作以及其他salve的数据同步,而每一个slave都只能进行读操作,不能进行写操作。
下面以6379端口运行的server为master,6380端口下运行的server为slave。
#设置redis-6380配置文件,使得其master为本机的6379端口
slaveof 127.0.0.1 6379
#开启6379端的redis-server和6380的redis-server
redis-server redis-6379
redis-server redis-6380
#在连个端口的客户端分别查看连接信息
info
6379:
6380:
全量复制
在建立起主从结构后,master创建一个bgsave快照,保存在此之前全部的数据,并将其通过socket发送给slave。于此同时建立起一个aof数据缓存队列,将发送快照期间的全部指令更新放入缓存队列。
部分复制
在快照数据数据传输完成之后转入部分复制,也就是在间隔时间内master将aof数据压入队列。由于队列的长度是有限的(可通过master的配置文件修改,属性为:repl-backlog-size),故应设置合理的缓冲区大小,不然可能会出现全量复制还没结束,缓冲区队列大小就满了导致数据丢失,故只能再次进行全量备份这样的循环中。
master数据同步
slave数据同步
心跳机制
在进行部分同步时slave会每隔一秒向master ping一下,目的主要有两个1:确保master没有掉线。2:将数据缓存队列的offset发送给master,目的是告知master slave的数据读取进度,好让master判断自身的下一步操作;master也会10秒ping一次slaver,确保slave处于在线状态。
哨兵模式
Redis Sentinel 是一个分布式的架构,它本身也是一个独立的 Redis 节点,只不过它不存储数据,只支持部分命令,它能够自动完成故障发现和故障转移,并通知应用方,从而实现高可用。
哨兵结构的搭建
先创建三个redis服务器,对应的端口号为:6379,6380,6381。其中6379为master,6380和 6381是6379的slave。
然后再创建三个监听6379服务器的sentinel。其中sentinel对应配置文件为
port 26379 #sentinel的端口号
daemonize no #守护进程方式
pidfile "/var/run/redis-sentinel.pid"
logfile ""
dir "/home/yujiewang/redis/sentinel.conf/data"
sentinel myid 29a28575c9cf493717e31b0e9ab0ce94d8043709 #启动成功后自己创建的一个ID,配置时是没有的。
sentinel deny-scripts-reconfig yes
sentinel monitor mymaster 127.0.0.1 6379 2 #指定监听的master服务器的IP和端口;
#最后一个2表示当有2个sentinel投票认为master离线,就断定master离线
当所有的sentinel都设置成功后,每一个sentinel的配置文件都会自动记下其他哨兵节点的ID信息以及IP和端口号。
哨兵26379配置文件中自动生成的其他sentinel信息:
配置成功后哨兵节点客户端的信息:
在这时,关闭master服务器(6379),三十秒过后哨兵会将6381自动更换为新的master,并将6379和6380变为6381的slave。再过一会儿发现6379仍然没上线,于是就踢出6379。
哨兵显示的master自动更换信息:
redis集群
redis cluster中一共分为16384个槽,对于每个key值,根据hash算法分配到一个对应的槽中。具体的算法是先对key进行crc16,再对算法输出值16384取模。redis cluster中,每个节点都会分配到16384个slot中的一部分,而且每个slot都必然有一个存放它的node。当集群节点发生变化时(如:节点的增加,删除),需要将slot进行重新分配。因此,使用redis cluster可以完成集群的纵向扩容。
如果某个slot对应的node宕机,并且没有备份的节点上位,那么整个集群都将会失效。因此,最好为每个集群中的master采用主从结构,确保整个集群工作的可靠性。
cluster搭建
配置一个三master三slave的cluster。
6379,6380,6381为master;6382-6384为从节点;
配置文件:
port 6379
daemonize no
dbfilename dump-6379.rdb
bind 127.0.0.1
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 10000
其他的配置文件直接修改一下端口就行了
sed 's/6379/6384/g' redis-6379 >redis-6384
......
开启6个redis服务器
然后利用ruby redis进行集群构建(注意需要用到ruby和gem,这里就不再赘述环境搭建)
redis-trib.rb create --replicas 1 \
127.0.0.1:6379\
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
#replicas后面的1是指每个master有1个slave
slot分配给3个master节点(基本平均)
将master1(6379)关闭,经过10s后,可在他的从节点(6382)看到如下信息:从节点变为master。