二、redis启动流程







1.初始化server变量,设置redis相关的默认值



2.读入配置文件,同时接收命令行中传入的参数,替换服务器设置的默认值



3.初始化服务器功能模块。在这一步初始化了包括进程信号处理、客户端链表、共享对象、初始化数据、初始化网络连接等



4.从RDB或AOF重载数据



5.网络监听服务启动前的准备工作



6.开启事件监听,开始接受客户端的请求





启动的部分过程通过查看下图,会更直观。







redis3.0负载均衡 redis配置性能优化_Redis



三、Redis数据持久化方案






在使用redis时不少人都说一个问题,就是说redis宕机了怎么办?会不会数据丢失等等的问题。



现在来看看Redis提供的数据持久化解决方案,并通过原理分析优缺点。最终能得出Redis适合使用的应用场景。



1.RDB持久化方案



在Redis运行时,RDB程序将当前内存中的数据库快照保存到磁盘中,当Redis需要重启时,RDB程序会通过重载RDB文件来还原数据库。



从上述描述可以看出,RDB主要包括两个功能:



关于rdb的实现可以见src/rdb.c



a)保存(rdbSave)







rdbSave负责将内存中的数据库数据以RDB格式保存到磁盘中,如果RDB文件已经存在将会替换已有的RDB文件。保存RDB文件期间会阻塞主进程,这段时间期间将不能处理新的客户端请求,直到保存完成为止。










为避免主进程阻塞,Redis提供了rdbSaveBackground函数。在新建的子进程中调用rdbSave,保存完成后会向主进程发送信号,同时主进程可以继续处理新的客户端请求。






b)读取(rdbLoad)







当Redis启动时,会根据配置的持久化模式,决定是否读取RDB文件,并将其中的对象保存到内存中。










载入RDB过程中,每载入1000个键就处理一次已经等待处理的客户端请求,但是目前仅处理订阅功能的命令(PUBLISH 、 SUBSCRIBE 、 PSUBSCRIBE 、 UNSUBSCRIBE 、 PUNSUBSCRIBE),其他一律返回错误信息。因为发布订阅功能是不写入数据库的,也就是不保存在Redis数据库的。









RDB的缺点:









再说RDB缺点时,需要提到的是RDB有保存点的概念。在默认的redis.conf中可以看到这样的默认配置:















  1. #save <seconds> <changes>  

















  1. save 900 1        #如果15分钟内,有1个键被修改  

















  1. save 300 10      #如果6分钟内,有10个键被修改  

















  1. save 60 10000  #如果60秒内有10000个键被修改  











意思是当满足上面任意一个条件时,将会进行快照保存。为了保证IO读写性能不会成为Redis的瓶颈,一般都会创建一个比较大的值来作为保存点。









1.此时如果保存点设置过大,就会导致宕机丢失的数据过多。保存点设置过小,又会造成IO瓶颈









2.当对数据进行保存时,可能会由于数据集过大导致操作耗时,这会导致Redis可能在短时间内无法处理客户端请求。






2.AOF持久化方案



以协议文本的方式,将所有对数据库进行的写入命令记录到AOF文件,达到记录数据库状态的目的。



a)保存







1.将客户端请求的命令转换为网络协议格式










2.将协议内容字符串追加到变量server.aof_buf中









3.当AOF系统达到设定的条件时,会调用aof_fsync(文件描述符号)将数据写入磁盘





其中第三步提到的设定条件,就是AOF性能的关键点。目前Redis支持三种保存条件机制:1.AOF_FSYNC_NO:不保存此模式下,每执行一条客户端的命令,都会将协议字符串追加到server.aof_buf中,但不会执行写入磁盘。写入只发生在:     1.Redis被正常关闭      2.Aof功能关闭     3.系统写缓存已满,或后台定时保存操作被执行上面三种情况都会阻塞主进程,导致客户端请求失败。2.AOF_FSYNC_EVERYSECS:每一秒保存一次由后台子进程调用写入保存,不会阻塞主进程。如果发生宕机,那么最大丢失数据会在2s以内的数据。 这也是默认的设置选项3.AOF_FSYNC_ALWAYS:每执行一个命令都保存一次





这种模式下,可以保证每一条客户端指令都被保存,保证数据不会丢失。但缺点就是性能大大下降,因为每一次操作都是独占性的,需要阻塞主进程。















b)读取







AOF保存的是数据协议格式的数据,所以只要将AOF中的数据转换为命令,模拟客户端重新执行一遍,就可以还原所有数据库状态。










读取的过程是:









1.创建模拟的客户端









2.读取AOF保存的文本,还原数据为原命令和原参数。然后使用模拟的客户端发出这个命令请求。









3.继续执行第二步,直到读取完AOF文件


















AOF需要将所有的命令都保存到磁盘,那么这个文件会随着时间变得越来越大。读取也会变得很慢。









Redis提供了AOF的重写机制,帮助减少文件的大小。实现的思路是:















  1. LPUSH list 1 2 3 4 5  

















  1. LPOP list  

















  1. LPOP list  

















  1. LPUSH list 1  











最初保存到AOF文件的将会是四条指令。但经过AOF重写后,会变成一条指令:















  1. LPUSH list 1 3 4 5  











同时,考虑到为了在AOF重写时,不影响AOF的写入增加了AOF重写缓存的概念。









也就是说Redis在开启AOF时,除了将命令格式数据写入到AOF文件,同时也会写入到AOF重写缓存。这样AOF的写入、重写就做到了隔离,保证了重写时不会阻塞写入。









c)AOF重写流程







1.AOF重写完成会向主进程发送一个完成的信号










2.会将AOF重写缓存中的数据全部写入到文件中 









3.用新的AOF文件,覆盖原有的AOF文件。









d)AOF缺点







1.AOF文件通常会大于相同数据集的RDB文件











2.AOF模式下性能与RDB模式下性能高低,主要取决于AOF选用的fsync模式









下面给出客户端请求RedisServer时,server端持久化的部分操作图解。



redis3.0负载均衡 redis配置性能优化_客户端_02






四、Redis数据库的实现



Redis是一个键值对数据库,称为键空间。实现这种KV形式的存储,Redis使用了两种数据结构类型:1、字典
        Redis字典使用的是哈希表实现,原本不准备详细介绍Redis哈希表的实现。但发现Redis在实现哈希表时,
提供了一个很好的rehash方案,这个方案思路很好,甚至可以衍生到其他各个应用中使用,方案的名称叫“渐进式Rehash”。

        实现哈希表的方法大同小异,但为何各个开源软件总是去开发自己独有的哈希数据结构呢?
从研究PHP内核的哈希实现与Redis哈希实现,发现应用场景决定了必须定制才能更好的发挥性能。(关于PHP哈希实现可以参看:PHP内核中的神器之HashTable)
a)PHP主要应用于WEB场景,在WEB场景针对单次请求数据之间是隔离的,并且哈希的数量是有限的,那么进行一次rehash也是很快的。
所以PHP内核使用阻塞形式rehash,即rehash进行中将不能对当前哈希表进行任何操作。

b)在来看Redis,常驻进程,接收客户端请求处理各项事务,并且操作的数据是相关且数据量较大的,如果使用PHP内核的那种方式就会出现:
对哈希表进行rehash时,此时将阻塞所有客户端请求,并发性能会大大下降。



初始化字典图解:



redis3.0负载均衡 redis配置性能优化_客户端_03



新增字典元素图解:



redis3.0负载均衡 redis配置性能优化_Redis_04



Rehash执行流程:



redis3.0负载均衡 redis配置性能优化_Redis_05






注:有些部分可能理解有误,有错误的地方望指出。




这里我也转载一篇  redis优化配置和redis.conf说明 来凑数了:

1. Redis.conf 配置参数:

#是否作为守护进程运行
 daemonize yes
 #如以后台进程运行,则需指定一个pid,默认为/var/run/redis.pid
 pidfile redis.pid
 #绑定主机IP,默认值为127.0.0.1
 #bind 127.0.0.1
 #Redis默认监听端口
 port 6379
 #客户端闲置多少秒后,断开连接,默认为300(秒)
 timeout 300
 #日志记录等级,有4个可选值,debug,verbose(默认值),notice,warning
 loglevel verbose
 #指定日志输出的文件名,默认值为stdout,也可设为/dev/null屏蔽日志
 logfile stdout
 #可用数据库数,默认值为16,默认数据库为0
 databases 16
 #保存数据到disk的策略
 #当有一条Keys数据被改变是,900秒刷新到disk一次
 save 900 1
 #当有10条Keys数据被改变时,300秒刷新到disk一次
 save 300 10
 #当有1w条keys数据被改变时,60秒刷新到disk一次
 save 60 10000
 #当dump .rdb数据库的时候是否压缩数据对象
 rdbcompression yes
 #本地数据库文件名,默认值为dump.rdb
 dbfilename dump.rdb
 #本地数据库存放路径,默认值为 ./
 dir /var/lib/redis/
  
 ########### Replication #####################
 #Redis的复制配置
 # slaveof <masterip> <masterport> 当本机为从服务时,设置主服务的IP及端口
 # masterauth <master-password> 当本机为从服务时,设置主服务的连接密码
 #连接密码
 # requirepass foobared
 #最大客户端连接数,默认不限制
 # maxclients 128
 #最大内存使用设置,达到最大内存设置后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理后,任到达最大内存设置,将无法再进行写入操作。
 # maxmemory <bytes>
 #是否在每次更新操作后进行日志记录,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认值为no
 appendonly no
 #更新日志文件名,默认值为appendonly.aof
 #appendfilename
 #更新日志条件,共有3个可选值。no表示等操作系统进行数据缓存同步到磁盘,always表示每次更新操作后手动调用fsync()将数据写到磁盘,everysec表示每秒同步一次(默认值)。
 # appendfsync always
 appendfsync everysec
 # appendfsync no
  
 ################ VIRTUAL MEMORY ###########
 #是否开启VM功能,默认值为no
 vm-enabled no
 # vm-enabled yes
 #虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
 vm-swap-file /tmp/redis.swap
 # 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的 (Redis的索引数据就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0。
 vm-max-memory 0
 vm-page-size 32
 vm-pages 134217728
 vm-max-threads 4
  
 ############# ADVANCED CONFIG ###############
 glueoutputbuf yes
 hash-max-zipmap-entries 64
 hash-max-zipmap-value 512
 #是否重置Hash表
 activerehashing yes

注意:Redis官方文档对VM的使用提出了一些建议:

当你的key很小而value很大时,使用VM的效果会比较好.因为这样节约的内存比较大.

当你的key不小时,可以考虑使用一些非常方法将很大的key变成很大的value,比如你可以考虑将key,value组合成一个新的value.

最好使用Linux ext3 等对稀疏文件支持比较好的文件系统保存你的swap文件.

vm-max-threads这个参数,可以设置访问swap文件的线程数,设置最好不要超过机器的核数.如果设置为0,那么所有对swap文件的操作都是串行的.可能会造成比较长时间的延迟,但是对数据完整性有很好的保证.

 

2. 调整系统内核参数

如果内存情况比较紧张的话,需要设定内核参数:

echo 1 > /proc/sys/vm/overcommit_memory

这里说一下这个配置的含义:/proc/sys/vm/overcommit_memory
该文件指定了内核针对内存分配的策略,其值可以是0、1、2。
0,表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1,表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2,表示内核允许分配超过所有物理内存和交换空间总和的内存
Redis 在dump数据的时候,会fork出一个子进程,理论上child进程所占用的内存和parent是一样的,比如parent占用的内存为 8G,这个时候也要同样分配8G的内存给child, 如果内存无法负担,往往会造成redis服务器的down机或者IO负载过高,效率下降。所以这里比较优化的内存分配策略应该设置为 1(表示内核允许分配所有的物理内存,而不管当前的内存状态如何)

 

 

一、 运行服务

# redis-server /etc/redis/redis.conf 开启
 # redis-cli shutdown 关闭

二、 测试

1) 可在后台启动redis服务后,用redis-benchmark命令测试

2) 通过redis-cli命令实际操作测试

 

三、 保存/备份

数据备份可以通过定期备份该文件实现。
因为redis是异步写入磁盘的,如果要让内存中的数据马上写入硬盘可以执行如下命令:
redis-cli save 或者 redis-cli -p 6380 save(指定端口)
注意,以上部署操作需要具备一定的权限,比如复制和设定内核参数等。
执行redis-benchmark命令时也会将内存数据写入硬盘。 

四、 开启端口号

1) 打开/etc/sysconfig/iptables,

2) 在-【A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT】后面,加上 【-A INPUT -p tcp -m state --state NEW -m tcp --dport 6379 -j ACCEPT】  //这里的6379是Redis默认端口号

3) 保存,重启防火墙:/etc/init.d/iptables restart