redis可以自动刷新分布式缓存redis吗

其他回答(3)
当然可以。
园豆:14063
使用消息队列作为应用和数据库之间的缓冲区主要是为了解决碎片化请求造成产生大量连接的问题,而有了消息队列,可以使用数据库支持的批量插入、更新机制,可以用更少的连接完成相同的任务。
园豆:44856
园豆:44856
&&&您需要以后才能回答,未注册用户请先。浅谈小白如何读懂Redis高速缓存与持久化并存及主从高可用集群
一、简介 
Redis是一个基于键值(K-V)的高速缓存软件,和他具有相同功能的软件有memcached,但其支持更为复杂的数据结构,例如:List,set,sorted set,同时redis具有持久性功能。redis究竟是什么?对于不同的应用场合,对redis的理解也不相同,如下有三种不同的理解。
①key value store(键值存储),是一个以键值形式存储的,用来作为唯一的存储,同时借助于sentinel实现一定意义上的高可用。
②memory cached(内存缓存),是一个把数据存储在内存中的高速缓存,在应用中用来实现高效的响应用户请求。
③data structrue server(数据结构服务),支持对复杂数据库结构的高速操作例如:list,string,hash,set,stored等,提供某特殊业务操作。
redis的优势:
①丰富的操作,例如:hash,list,set,stored,sets.
②内建复制(replication)及其集群(cluster)
③就地更新操作,而无需停机重启生效
④支持持久缓存,常用的有RDB和AOF
基本架构图:
原理:redis工作时,将启动一个fork函数创建一个子进程,复制当前进程,存为副本,父进程任然接受并处理客服端请求,而子进程则将内存中的数据文件写入磁盘中的临时文件,当子进程完成所有的写入操作时会将原来的件替换成最新生成的临时文件。
二、redis持久性介绍
redis持久性分为两种:RDB(Redis datebase)和AOF(append only file),同时,RDB和AOF可同时使用,但BGSAVE和BGWRITEAOF不会同时执行,在redis服务器启动用于恢复数据时将会优先使用AOF。持久的功能是用于恢复,但持久本身不能取代备份,还应该制定备份策略,对redis进行数据库备份,保证数据的完整性。
RDB:此方式基于快照实现,该持久化方式是在redis内部有一个定时器,每隔固定时间去检查当前数据发生改变的次数与时间是否满足配置的持久性触发的条件,如果满足则通过操作系统启动一个fork函数调用来创建出一个字进程,这个子进程默认会与父进程共享相同的地址空间,这时就可以通过子进程来遍历整个内存来进行存储操作,而主进程则仍然可以提供服务,当有写入时由操作系统按照内存页(page)为单位来进行copy-on-write保证父子进程之间不会互相影响。该持久化的主要缺点是定时快照只是代表一段时间内的内存映像,所以系统重启会丢失上次快照与重启之间所有的数据。
编辑/etc/redis.conf可查看相应的配置参数及其意义
################################SNAPSHOTTING################################
#SavetheDBondisk:
#WillsavetheDBifboththegivennumberofsecondsandthegiven
#numberofwriteoperationsagainsttheDBoccurred.
#Intheexamplebelowthebehaviourwillbetosave:
#after900sec(15min)ifatleast1keychanged
#after300sec(5min)ifatleast10keyschanged
#after60secifatleast10000keyschanged
#Note:youcandisablesavingcompletelybycommentingoutall&save&lines.
#Itisalsopossibletoremoveallthepreviouslyconfiguredsave
#pointsbyaddingasavedirectivewithasingleemptystringargument
#likeinthefollowingexample:
save9001#表示900秒内有一个值发生改变则就会触发存储
save30010#表示300秒内有10个值发生改变则就会触发存储
save6010000#表示60内有10000个值发生改变则就会触发储存
stop-writes-on-bgsave-erroryes
rdbcompressionyes
rdbcheksumyes
dbfilenamedump.rdb#数据库文件名
dir/var/lib/redis#redis安装路径
AOF:redis主进程通过fork创建子进程,子进程根据redis内存中的数据库重构后将此存储于临时文件中,父进程继承客服端的请求,并会把这些请求中的操作继续追加至原来的AOF文件,额外的这些新的写请求还会被放置于一个缓冲队列中,父进程把缓冲中的命令写到临时文件中,子进程重写完成会通知父进程,父进程用临时文件替换原来的AOF老文件。
AOF方式实际类似的基于语句的binlog方式,即每条会使Redis内存数据发生改变的命令都会追加到一个log文件中,也就是说这个log文件就是Redis的持久化数据。
AOF的方式的主要缺点是追加log文件可能导致体积过大,当系统重启恢复数据时如果是aof的方式则加载数据会非常慢,几十G的数据可能需要几小 时才能加载完,当然这个耗时并不是因为磁盘文件读取速度慢,而是由于读取的所有命令都要在内存中执行一遍。另外由于每条命令都要写log,所以使用aof 的方式,Redis的读写性能也会有所下降。
AOF对日志文件的写入操作时采用追加的模式进行,因此写入的过程中如果发生断电,机器宕机等情况发生,也不会对已存在数据文件造成破坏。
#aof相关参数:
auto-aof-rewrite-percentage100#当目前的AOF文件大小超过上一次重写时的AOF文件大小的百分之多少时会再次进行重写,如果之前没有重写过,则以启动时的AOF文件大小为依据
auto-aof-rewrite-min-size64mb#允许重写的最小AOF文件大小
appendonlyyes#开启AOF
appendfilename&appendonly.aof&
appendfsynceveryse每秒执行同步
no-appendfsync-on-rewriteno
Redis持久化磁盘IO方式及其带来的问题
有Redis线上运维经验的人会发现Redis在物理内存使用比较多,但还没有超过实际物理内存总容量时就会发生不稳定甚至崩溃的问题,有人认为是基于快照方式持久化的fork系统调用造成内存占用加倍而导致的,这种观点是不准确的,因为fork用的copy-on-write机制是基于操作系统页这个单位的,也就是只有有写入的脏页会被复制,但是一般你的系统不会在短时间内所有的页都发生了写入而导致复制,那么是什么原因导致Redis崩溃的呢?
答案是Redis的持久化使用了Buffer IO造成的,所谓Buffer IO是指Redis对持久化文件的写入和读取操作都会使用物理内存的Page Cache,而大多数数据库系统会使用Direct IO来绕过这层Page Cache并自行维护一个数据的Cache,而当Redis的持久化文件过大(尤其是快照文件),并对其进行读写时,磁盘文件中的数据都会被加载到物理内 存中作为操作系统对该文件的一层Cache,而这层Cache的数据与Redis内存中管理的数据实际是重复存储的,虽然内核在物理内存紧张时会做 Page Cache的剔除工作,但内核很可能认为某块Page Cache更重要,而让你的进程开始Swap ,这时你的系统就会开始出现不稳定或者崩溃了。我们的经验是当你的Redis物理内存使用超过内存总容量的3/5时就会开始比较危险了。
三、redis主从复制
redis主从复制和大部分主从类似,一个master可以有多个slave,支持链式复制,master以非阻塞的方式同步数据至slave。启动一个slave后,slave会向主发送同步命令,请求同步主库上的数据,master将启动一个后台的子进程,将数据快照保存至在数据文件中,把数据文件发送给slave,slave将数据文件保存至本地中,在本地重建数据库后载入内存,同步完成。
redis主从的特点:
1、redis使用异步复制,从服务器会以每秒一次的频率向主服务器报告复制流的处理进度
2、一个主服务器可以有多个从服务器,从服务器也可以有自己的从服务器(级联复制)
3、复制功能不会阻塞主服务器,即使一个或多个从服务器正在进行初次同步,主服务器也可以继续处理命令请求
4、复制功能可以用于数据冗余,也可以通过让多个从服务器处理只读命令请求来提升扩展性
5、Redis从节点默认为只读,无须手动配置,redis的主从集群可以实现分担压力的效果,但是无法做到高可用,如果master宕掉,服务就不可用了,所以使用redis的sentinel可以实现HA的功能。
主从实战配置:
编辑/etc/redis.conf配置文件,将bind改为本机IP地址,重启服务即可。
10.1.10.65
10.1.10.66
主节点相关配置参数如下:
[root@node1~]#redis-cli-h10.1.10.65-p6379
10.1.10.65:6379&INFO
redis_version:3.0.2
redis_git_sha1:
redis_git_dirty:0
redis_build_id:6be7fc9e6b88f79
redis_mode:standalone
os:3.10.0-327.el7.x86_64x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:2540
run_id:05cd262aebf7d10c4
tcp_port:6379
uptime_in_seconds:103
uptime_in_days:0
lru_clock:5327264
config_file:/etc/redis.conf
connected_clients:1
client_longest_output_list:0
client_biggest_input_buf:0
blocked_clients:0
used_memory:1922496
used_memory_human:1.83M
used_memory_rss:7675904
used_memory_peak:1922496
used_memory_peak_human:1.83M
used_memory_lua:36864
mem_fragmentation_ratio:3.99
mem_allocator:jemalloc-3.6.0
#Persistence
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
total_connections_received:2
total_commands_processed:33
instantaneous_ops_per_sec:0
total_net_input_bytes:1103
total_net_output_bytes:100
instantaneous_input_kbps:0.02
instantaneous_output_kbps:0.01
rejected_connections:0
sync_full:1
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:1946
migrate_cached_sockets:0
#Replication
role:master#当前节点的角色
connected_slaves:1#从节点的个数
slave0:ip=10.1.10.66,port=6379,state=online,offset=43,lag=0#从节点IP地址及端口、状态信息,偏移量等
master_repl_offset:43
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:42
used_cpu_sys:0.17
used_cpu_user:0.03
used_cpu_sys_children:0.01
used_cpu_user_children:0.00
cluster_enabled:0
db0:keys=2,expires=0,avg_ttl=0
10.1.10.65:6379&
配置node2为从节点,并打印相关信息
[root@node2~]#redis-cli-h10.1.10.66-p6379
10.1.10.66:6379&slaveof10.1.10.656379#表示主节点IP地址和端口
10.1.10.66:6379&INFOreplication
#Replication
role:slave#当前角色
master_host:10.1.10.65#主节点IP地址
master_port:6379#主节点端口号
master_link_status:up#主节点状态信息
master_last_io_seconds_ago:4
master_sync_in_progress:0
slave_repl_offset:1093
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
10.1.10.66:6379&configgetslaveof#查询当前从的主节点IP地址及端口信息
1)&slaveof&
2)&10.1.10.656379&
10.1.10.66:6379&
在主中插入数据,查看从中是否已经同步完成
#在主中插入键值对,查看其是否插入成功
10.1.10.65:6379&setport8080
10.1.10.65:6379&getport
10.1.10.65:6379&setip10.1.1.1
10.1.10.65:6379&keys*
10.1.10.65:6379&getport
10.1.10.65:6379&getip
&10.1.1.1&
10.1.10.65:6379&
#在从中查看是否已同步完成,如下结果显示在主中插入数据从节点立刻能get到数据,说明配置成功
10.1.10.66:6379&keys*
10.1.10.66:6379&getip
&10.1.1.1&
10.1.10.66:6379&getport
10.1.10.66:6379&
四、redis高可用管理工具sentinel
Sentinel是一个管理redis实例的工具,它可以对现有的redis进行监控、通知、故障自动转移,sentinel不断的检测redis实例是否可以正常的工作,通过API向其他程序报告redis的转台,如redis master不能工作,则会自动启动故障转移进程,将其中一个slave提升为master,其他slave将从新设置新的master服务器,而故障的master再次启动后会被sentinel自动降级为slave。
基本架构图:
Sentinel作用如下:
1、监控:sentinel会不断的检查你的主服务器和从服务器是否运行正常
2、当被监控的某个redis服务器出现问题时,sentinel可以通过API向管理员或者其他应用程序发送通知
3、故障自动转移:当一个主服务器不能正常工作时,sentinel会开始一次自动故障转移操作,他会将其中一个从服务器升级为新的主服务器,并将其他从服务器改为复制新的主服务器;当客户端试图连接失效的主服务器时,集群也会向客户端返回新主服务器的地址,使得集群可以使用新主服务器代替失效服务器。
redis sentinel在监控redis实例时有两种redis宕机状态S_DOWN和O_DOWN:
S_DOWN:当sentinel在指定的超时时间内没有收到一个正确的ping回复值,则认为是S_DOWN
O_DOWN:O_DOWN的条件是有足够多的sentinel认为该redis实例是S_DOWN。
注意:O_DOWN只能是发生在主服务器,sentinel和其他从服务器不会发生O_DOWN
Sentinel监控管理redis实战配置:
本实验在一台服务器上可完成实验,本实验使用node1附加上面配合的主从完成sentinel高可用测试实验。
#创建数据目录并修改相应的配置文件,如下所示:
mkdir/redis/db/{1,2,3}
chown-Rredis.redis/redis/db*
#修改配置文件主要有如下:
配置文件实例一:
bind0.0.0.0
pidfile&/var/run/redis.pid&
logfile&/var/log/redis/redis.log&
dir&/redis/db1&
配置文件实例二:
bind0.0.0.0
pidfile&/var/run/redis2.pid&
logfile&/var/log/redis/redis2.log&
dir&/redis/db2&
配置文件实例三:
bind0.0.0.0
pidfile&/var/run/redis3.pid&
logfile&/var/log/redis/redis3.log&
dir&/redis/db3&
#配置sentinel监控配置文件如下
logfile&/var/log/redis/sentinel.log&
sentinelmonitormymaster10.1.10.6563811
sentineldown-after-millisecondsmymaster50000
sentinelfailover-timeoutmymaster60000
sentinelconfig-epochmymaster1
sentinelleader-epochmymaster1
sentinelknown-slavemymaster10.1.10.656379
sentinelcurrent-epoch1
分别使用不同的配置文件启动redis服务
[root@node1redis]#redis-server/etc/redis/redis.conf
[root@node1redis]#redis-server/etc/redis/redis.conf.2
[root@node1redis]#redis-server/etc/redis/redis.conf.3
[root@node1redis]#ss-tnl#其端口全部监听
StateRecv-QSend-QLocalAddress:PortPeerAddress:Port
LISTEN2.1:53*:*
LISTEN0128*:22*:*
LISTEN.0.1:631*:*
LISTEN.0.1:25*:*
LISTEN032:::21:::*
LISTEN0128:::22:::*
LISTEN:::*
LISTEN:::*
[root@node1redis]#
使用slaveof命令把主节点设置为本机的6381端口
10.1.10.65:6381&INFOreplication
#Replication
role:master
connected_slaves:3
slave0:ip=10.1.10.65,port=6380,state=online,offset=10551,lag=1
slave1:ip=10.1.10.65,port=6379,state=online,offset=10551,lag=1
slave2:ip=10.1.10.66,port=6379,state=online,offset=10551,lag=1
master_repl_offset:10551
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:10550
10.1.10.65:6381&
启动sentinel监控器节点状态
[root@node1~]#redis-server/etc/redis/redis-sentinel.conf
[root@node1~]#redis-cli-h10.1.10.65-p26379
10.1.10.65:26379&INFO#如下信息表明了个节点的信息
redis_version:3.0.2
redis_git_sha1:
redis_git_dirty:0
redis_build_id:6be7fc9e6b88f79
redis_mode:sentinel
os:Linux3.10.0-327.el7.x86_64x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:3769
run_id:9fa79d20fe4fed732a1dc8a257a01f
tcp_port:26379
uptime_in_seconds:46
uptime_in_days:0
lru_clock:5332425
config_file:/etc/redis/redis-sentinel.conf
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=10.1.10.65:6381,slaves=3,sentinels=1
10.1.10.65:26379&sentinelmasters
1)1)&name&
2)&mymaster&
4)&10.1.10.65&
8)&ea1bbae4badc6d71ce&
10)&master&
11)&pending-commands&
13)&last-ping-sent&
15)&last-ok-ping-reply&
17)&last-ping-reply&
19)&down-after-milliseconds&
20)&50000&
21)&info-refresh&
23)&role-reported&
24)&master&
25)&role-reported-time&
26)&106722&
27)&config-epoch&
29)&num-slaves&
31)&num-other-sentinels&
33)&quorum&
35)&failover-timeout&
36)&60000&
37)&parallel-syncs&
10.1.10.65:26379&sentinelmasters#查看主节点信息状态
1)1)&name&
2)&mymaster&
4)&10.1.10.65&
8)&ea1bbae4badc6d71ce&
10)&master&
11)&pending-commands&
13)&last-ping-sent&
15)&last-ok-ping-reply&
17)&last-ping-reply&
19)&down-after-milliseconds&
20)&50000&
21)&info-refresh&
23)&role-reported&
24)&master&
25)&role-reported-time&
26)&257144&
27)&config-epoch&
29)&num-slaves&
31)&num-other-sentinels&
33)&quorum&
35)&failover-timeout&
36)&60000&
37)&parallel-syncs&
10.1.10.65:26379&
模拟redis-server 6381除故障可以将此进程kill掉,查看主节点是否转移
10.1.10.65:26379&info
redis_version:3.0.2
redis_git_sha1:
redis_git_dirty:0
redis_build_id:6be7fc9e6b88f79
redis_mode:sentinel
os:Linux3.10.0-327.el7.x86_64x86_64
arch_bits:64
multiplexing_api:epoll
gcc_version:4.4.7
process_id:3769
run_id:9fa79d20fe4fed732a1dc8a257a01f
tcp_port:26379
uptime_in_seconds:435
uptime_in_days:0
lru_clock:5332814
config_file:/etc/redis/redis-sentinel.conf
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=10.1.10.65:6380,slaves=3,sentinels=1#主节点已经自动转移至本机的6380上
10.1.10.65:26379&
查看redis-server的6380端口是否成为主节点
10.1.10.65:6380&INFOreplication
#Replication
role:master
connected_slaves:3
slave0:ip=10.1.10.65,port=6379,state=online,offset=35484,lag=1
slave1:ip=10.1.10.66,port=6379,state=online,offset=35484,lag=1
slave2:ip=10.1.10.65,port=6381,state=online,offset=35484,lag=1
master_repl_offset:35484
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:35483
10.1.10.65:6380&
总结:Redis持久性中的RDB是基于快照方式,意外重启会丢失数据,而AOF对日志文件的写入操作时采用追加的模式进行,因此写入的过程中如果发生断电,机器宕机等情况发生,也不会对已存在数据文件造成破坏。在考虑数据的完整性可根据自己的业务可同时使用AOF和RDB,保证了数据的完整性,但是redis持久性并不代表备份,还需制定相关的备份方案,对redis已有的数据进行备份。重新启动Redis,在redis服务器启动用于恢复数据时,会优先考虑使用AOF。redis缓存技术学习&&categories:&&tags:&&author:1 什么是redisredis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。2 性能怎么样Redis是一个高性能的key-value内存数据库。官方性能测试结果: set操作每秒110000次,get操作每秒81000次。3 可不可以存对象和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)和zset(有序集合)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作。4 Redis与memcache的最大区别Replication(树形)data types(String、Lists、Sorted Sets、Hashes)persistence (snapshot、aof)很多开发者都认为Redis不可能比Memcached快,Memcached完全基于内存,而Redis具有持久化保存特性,即使是异步的,Redis也不可能比Memcached快。但是测试结果基本是Redis占绝对优势。一直在思考这个原因,目前想到的原因有这几方面。Libevent。和Memcached不同,Redis并没有选择libevent。Libevent为了迎合通用性造成代码庞大(目前Redis代码还不到libevent的1/3)及牺牲了在特定平台的不少性能。Redis用libevent中两个文件修改实现了自己的epoll event loop(4)。业界不少开发者也建议Redis使用另外一个libevent高性能替代libev,但是作者还是坚持Redis应该小巧并去依赖的思路。一个印象深刻的细节是编译Redis之前并不需要执行./configure。CAS问题。CAS是Memcached中比较方便的一种防止竞争修改资源的方法。CAS实现需要为每个cache key设置一个隐藏的cas token,cas相当value版本号,每次set会token需要递增,因此带来CPU和内存的双重开销,虽然这些开销很小,但是到单机10G+ cache以及QPS上万之后这些开销就会给双方相对带来一些细微性能差别(5)。5单台Redis的存放数据必须比物理内存小Redis的数据全部放在内存带来了高速的性能,但是也带来一些不合理之处。比如一个中型网站有100万注册用户,如果这些资料要用Redis来存储,内存的容量必须能够容纳这100万用户。但是业务实际情况是100万用户只有5万活跃用户,1周来访问过1次的也只有15万用户,因此全部100万用户的数据都放在内存有不合理之处,RAM需要为冷数据买单。这跟操作系统非常相似,操作系统所有应用访问的数据都在内存,但是如果物理内存容纳不下新的数据,操作系统会智能将部分长期没有访问的数据交换到磁盘,为新的应用留出空间。现代操作系统给应用提供的并不是物理内存,而是虚拟内存(Virtual Memory)的概念。基于相同的考虑,Redis 2.0也增加了VM特性。让Redis数据容量突破了物理内存的限制。并实现了数据冷热分离。6 Redis的VM实现是重复造轮子Redis的VM依照之前的epoll实现思路依旧是自己实现。但是在前面操作系统的介绍提到OS也可以自动帮程序实现冷热数据分离,Redis只需要OS申请一块大内存,OS会自动将热数据放入物理内存,冷数据交换到硬盘,另外一个知名的“理解了现代操作系统(3)”的Varnish就是这样实现,也取得了非常成功的效果。作者antirez在解释为什么要自己实现VM中提到几个原因(6)。主要OS的VM换入换出是基于Page概念,比如OS VM1个Page是4K, 4K中只要还有一个元素即使只有1个字节被访问,这个页也不会被SWAP, 换入也同样道理,读到一个字节可能会换入4K无用的内存。而Redis自己实现则可以达到控制换入的粒度。另外访问操作系统SWAP内存区域时block进程,也是导致Redis要自己实现VM原因之一。7 用get/set方式使用Redis作为一个key value存在,很多开发者自然的使用set/get方式来使用Redis,实际上这并不是最优化的使用方法。尤其在未启用VM情况下,Redis全部数据需要放入内存,节约内存尤其重要。假如一个key-value单元需要最小占用512字节,即使只存一个字节也占了512字节。这时候就有一个设计模式,可以把key复用,几个key-value放入一个key中,value再作为一个set存入,这样同样512字节就会存放10-100倍的容量。这就是为了节约内存,建议使用hashset而不是set/get的方式来使用Redis8使用aof代替snapshotRedis有两种存储方式,默认是snapshot方式,实现方法是定时将内存的快照(snapshot)持久化到硬盘,这种方法缺点是持久化之后如果出现crash则会丢失一段数据。因此在完美主义者的推动下作者增加了aof方式。aof即append only mode,在写入内存数据的同时将操作命令保存到日志文件,在一个并发更改上万的系统中,命令日志是一个非常庞大的数据,管理维护成本非常高,恢复重建时间会非常长,这样导致失去aof高可用性本意。另外更重要的是Redis是一个内存数据结构模型,所有的优势都是建立在对内存复杂数据结构高效的原子操作上,这样就看出aof是一个非常不协调的部分。其实aof目的主要是数据可靠性及高可用性,在Redis中有另外一种方法来达到目的:Replication。由于Redis的高性能,复制基本没有延迟。这样达到了防止单点故障及实现了高可用。9 Redis是否支持集群支持redis主从复制配置和使用都非常简单。通过主从复制可以允许多个slave server拥有和master server相同的数据库副本。下面是关于redis主从复制的一些特点 1.master可以有多个slave 2.除了多个slave连到相同的master外,slave也可以连接其他slave形成图状结构 3.主从复制不会阻塞master。也就是说当一个或多个slave与master进行初次同步数据时,master可以继续处理client发来的请求。相反slave在初次同步数据时则会阻塞不能处理client的请求。 4.主从复制可以用来提高系统的可伸缩性,我们可以用多个slave 专门用于client的读请求,比如sort操作可以使用slave来处理。也可以用来做简单的数据冗余 5.可以在master禁用数据持久化,只需要注释掉master 配置文件中的所有save配置,然后只在slave上配置数据持久化。 下面介绍下主从复制的过程 当设置好slave服务器后,slave会建立和master的连接,然后发送sync命令。无论是第一次同步建立的连接还是连接断开后的重新连 接,master都会启动一个后台进程,将数据库快照保存到文件中,同时master主进程会开始收集新的写命令并缓存起来。后台进程完成写文件 后,master就发送文件给slave,slave将文件保存到磁盘上,然后加载到内存恢复数据库快照到slave上。接着master就会把缓存的命 令转发给slave。而且后续master收到的写命令都会通过开始建立的连接发送给slave。从master到slave的同步数据的命令和从 client发送的命令使用相同的协议格式。当master和slave的连接断开时slave可以自动重新建立连接。如果master同时收到多个 slave发来的同步连接命令,只会使用启动一个进程来写数据库镜像,然后发送给所有slave。在多台服务器上简单实现Redis的数据主从复制Redis的主从复制功能非常强大,一个master可以拥有多个slave,而一个slave又可以拥有多个slave,如此下去,形成了强大的多级服务器集群架构。下面我演示下怎样在多台服务器上进行Redis数据主从复制。这里我假设有两台服务器,一台是Windows操作系统(局域网IP:192.168.3.82),一台是Linux操作系统(局域网IP:192.168.3.90),在两个操作系统都安装redis,Windows操作系统使用cygwin工具进行安装,命令为:view sourceprint?$ tar xzf redis-2.2.2.tar.gz$ cd redis-2.2.2$ make可以通过”make test”命令判断是否安装成功。这里我使用1个master以及2个slave(master在Windows下,一个slave在Windows下,一个slave在Linux下),基本流程是:1. 在Windows服务器上创建两个目录,Demo1,Demo2,其中Demo1用来存放Master服务,Demo2用来存放Slave服务,在Master服务中的配置文件修改:view sourceprint?bind 192.168.3.82在Slave服务中的配置文件修改:view sourceprint?port 6381(服务端口号要分开)bind 192.168.3.82slaveof 192.168.3.82 6379 (设置master的Host以及Port)2. 在Linux服务器上创建一个目录,Demo,Demo存放Slave服务,在服务中的配置文件修改:view sourceprint?bind 192.168.3.90slaveof 192.168.3.82 6379(设置master的Host以及Port)这样就完成了所有的配置。3. 现在运行这3个服务,通过命令:view sourceprint?./redis-server redis.conf来启动redis服务。注意到,当我启动master,然后启动一个slave的时候,可以发现slave上:会发送一个SYNC请求,从Master上面进行相应,而且它支持自动重连,即当master掉线的情况下,它会处于等待请求的状态。而Master上:能够接受Slave的应答,并且开始持久化操作,说明在Slave每次去连接Master的时候,都会去持久化磁盘。4. 现在开始写一个客户端程序,使用到ServiceStack.Redis.dll的.NET组件:view sourceprint?using ServiceStack.Rstatic void Main(string[] args){IRedisClientFactory factory = new RedisCacheClientFactory();IRedisClient client = factory.CreateRedisClient(&#.3.82″, 6379);client.Set&STRING&(“username”, “leepy”);string username = client.Get&STRING&(“username”);client.Save();Console.WriteLine(“username: {0}”, username);Console.ReadLine();}运行结果:数据Set的时候,数据保存在内存中,当调用Save方法时候,将数据保存在磁盘中。其中你会发现在3个服务目录中,都出现了dump.rdb,说明Master的文件都同步到Slave中去了。用UE编辑器打开文件查看: 从Redis源码中,可以发现rdb文件采用的是lzf压缩算法进行实现,默认lzf压缩算法是开启的。10安装与使用一、 下载安装 Wget /files/redis-2.2.7.tar.gz二、.安装部署Redis代码
1. tar zxvf2. redis-2.2.7.tar.gz3. cd redis-2.2.7.tar.gz4. maketar zxvfredis-2.2.7.tar.gzcd redis-2.2.7.tar.gzmake可以将redis.conf 复制到 /etc/下Redis代码
1. cp redis.conf /etc/2. cp src/redis-server src/redis-cli src/redis-benchmark /usr/local/rediscp redis.conf /etc/cp src/redis-server src/redis-cli src/redis-benchmark /usr/local/redis启动redisRedis代码1. /usr/local/redis/redis-server redis.confPort默认端口是 6379简单的测试:存值:./redis-cli set hx aaa取值:./redis-cli get hx要配置参数的意义:· daemonize:是否以后台daemon方式运行· pidfile:pid文件位置· port:监听的端口号· timeout:请求超时时间· loglevel:log信息级别· logfile:log文件位置· databases:开启数据库的数量· save * *:保存快照的频率,第一个*表示多长时间,第三个*表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。· rdbcompression:是否使用压缩· dbfilename:数据快照文件名(只是文件名,不包括目录)· dir:数据快照的保存目录(这个是目录)· appendonly:是否开启appendonlylog,开启的话每次写操作会记一条log,这会提高数据抗风险能力,但影响效率。· appendfsync:appendonlylog如何同步到磁盘(三个选项,分别是每次写都强制调用fsync、每秒启用一次fsync、不调用fsync等待系统自己同步)Redis命令总结Redis提供了丰富的命令(command)对数据库和各种数据类型进行操作,这些command可以在Linux终端使用。在编程时,比如使用Redis 的Java语言包,这些命令都有对应的方法,比如上面例子中使用的sadd方法,就是对集合操作中的SADD命令。下面将Redis提供的命令做一总结。连接操作相关的命令quit:关闭连接(connection)auth:简单密码认证对value操作的命令exists(key):确认一个key是否存在del(key):删除一个keytype(key):返回值的类型keys(pattern):返回满足给定pattern的所有keyrandomkey:随机返回key空间的一个keyrename(oldname, newname):将key由oldname重命名为newname,若newname存在则删除newname表示的keydbsize:返回当前数据库中key的数目expire:设定一个key的活动时间(s)ttl:获得一个key的活动时间select(index):按索引查询move(key, dbindex):将当前数据库中的key转移到有dbindex索引的数据库flushdb:删除当前选择数据库中的所有keyflushall:删除所有数据库中的所有key对String操作的命令set(key, value):给数据库中名称为key的string赋予值valueget(key):返回数据库中名称为key的string的valuegetset(key, value):给名称为key的string赋予上一次的valuemget(key1, key2,…, key N):返回库中多个string(它们的名称为key1,key2…)的valuesetnx(key, value):如果不存在名称为key的string,则向库中添加string,名称为key,值为valuesetex(key, time, value):向库中添加string(名称为key,值为value)同时,设定过期时间timemset(key1, value1, key2, value2,…key N, value N):同时给多个string赋值,名称为key i的string赋值value imsetnx(key1, value1, key2, value2,…key N, value N):如果所有名称为key i的string都不存在,则向库中添加string,名称key i赋值为value iincr(key):名称为key的string增1操作incrby(key, integer):名称为key的string增加integerdecr(key):名称为key的string减1操作decrby(key, integer):名称为key的string减少integerappend(key, value):名称为key的string的值附加valuesubstr(key, start, end):返回名称为key的string的value的子串对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之间的元素(下标从0开始,下同)ltrim(key, start, end):截取名称为key的list,保留start至end之间的元素lindex(key, index):返回名称为key的list中index位置的元素lset(key, index, value):给名称为key的list中index位置的元素赋值为valuelrem(key, count, value):删除count个名称为key的list中值为value的元素。count为0,删除所有值为value的元素,count&0从头至尾删除count个值为value的元素,count&0从尾到头删除|count|个值为value的元素。lpop(key):返回并删除名称为key的list中的首元素rpop(key):返回并删除名称为key的list中的尾元素blpop(key1, key2,… key N, timeout):lpop命令的block版本。即当timeout为0时,若遇到名称为key i的list不存在或该list为空,则命令结束。如果timeout&0,则遇到上述情况时,等待timeout秒,如果问题没有解决,则对key i+1开始的list执行pop操作。brpop(key1, key2,… key N, timeout):rpop的block版本。参考上一命令。rpoplpush(srckey, dstkey):返回并删除名称为srckey的list的尾元素,并将该元素添加到名称为dstkey的list的头部对Set操作的命令sadd(key, member):向名称为key的set中添加元素membersrem(key, member) :删除名称为key的set中的元素memberspop(key) :随机返回并删除名称为key的set中一个元素smove(srckey, dstkey, member) :将member元素从名称为srckey的集合移到名称为dstkey的集合scard(key) :返回名称为key的set的基数sismember(key, member) :测试member是否是名称为key的set的元素sinter(key1, key2,…key N) :求交集sinterstore(dstkey, key1, key2,…key N) :求交集并将交集保存到dstkey的集合sunion(key1, key2,…key N) :求并集sunionstore(dstkey, key1, key2,…key N) :求并集并将并集保存到dstkey的集合sdiff(key1, key2,…key N) :求差集sdiffstore(dstkey, key1, key2,…key N) :求差集并将差集保存到dstkey的集合smembers(key) :返回名称为key的set的所有元素srandmember(key) :随机返回名称为key的set的一个元素对zset(sorted set)操作的命令zadd(key, score, member):向名称为key的zset中添加元素member,score用于排序。如果该元素已经存在,则根据score更新该元素的顺序。zrem(key, member) :删除名称为key的zset中的元素memberzincrby(key, increment, member) :如果在名称为key的zset中已经存在元素member,则该元素的score增加increment;否则向集合中添加该元素,其score的值为incrementzrank(key, member) :返回名称为key的zset(元素已按score从小到大排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”zrevrank(key, member) :返回名称为key的zset(元素已按score从大到小排序)中member元素的rank(即index,从0开始),若没有member元素,返回“nil”zrange(key, start, end):返回名称为key的zset(元素已按score从小到大排序)中的index从start到end的所有元素zrevrange(key, start, end):返回名称为key的zset(元素已按score从大到小排序)中的index从start到end的所有元素zrangebyscore(key, min, max):返回名称为key的zset中score &= min且score &= max的所有元素zcard(key):返回名称为key的zset的基数zscore(key, element):返回名称为key的zset中元素element的scorezremrangebyrank(key, min, max):删除名称为key的zset中rank &= min且rank &= max的所有元素zremrangebyscore(key, min, max) :删除名称为key的zset中score &= min且score &= max的所有元素zunionstore / zinterstore(dstkeyN, key1,…,keyN, WEIGHTS w1,…wN, AGGREGATE SUM|MIN|MAX):对N个zset求并集和交集,并将最后的集合保存在dstkeyN中。对于集合中每一个元素的score,在进行AGGREGATE运算前,都要乘以对于的WEIGHT参数。如果没有提供WEIGHT,默认为1。默认的AGGREGATE是SUM,即结果集合中元素的score是所有集合对应元素进行SUM运算的值,而MIN和MAX是指,结果集合中元素的score是所有集合对应元素中最小值和最大值。对Hash操作的命令hset(key, field, value):向名称为key的hash中添加元素field&—&valuehget(key, field):返回名称为key的hash中field对应的valuehmget(key, field1, …,field N):返回名称为key的hash中field i对应的valuehmset(key, field1, value1,…,field N, value N):向名称为key的hash中添加元素field i&—&value ihincrby(key, field, integer):将名称为key的hash中field的value增加integerhexists(key, field):名称为key的hash中是否存在键为field的域hdel(key, field):删除名称为key的hash中键为field的域hlen(key):返回名称为key的hash中元素个数hkeys(key):返回名称为key的hash中所有键hvals(key):返回名称为key的hash中所有键对应的valuehgetall(key):返回名称为key的hash中所有的键(field)及其对应的value持久化save:将数据同步保存到磁盘bgsave:将数据异步保存到磁盘lastsave:返回上次成功将数据保存到磁盘的Unix时戳shundown:将数据同步保存到磁盘,然后关闭服务远程服务控制info:提供服务器的信息和统计monitor:实时转储收到的请求slaveof:改变复制策略设置config:在运行时配置Redis服务器在192.168.134.96 上安装了redis的master在192.168.134.97 上安装了slave 绑定了192.168.134.9611 redis事务redis对事务的支持目前还比较简单。redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令。 由于redis是单线程来处理所有client的请求的所以做到这点是很容易的。一般情况下redis在接受到一个client发来的命令后会立即处理并 返回处理结果,但是当一个client在一个连接中发出multi命令有,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一 个队列中。当从此连接受到exec命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给client.然后此连接就 结束事务上下文。下面可以看一个例子redis& multi OK redis& incr a QUEUED redis& incr b QUEUED redis& exec 1. (integer) 1 2. (integer) 1从这个例子我们可以看到incr a ,incr b命令发出后并没执行而是被放到了队列中。调用exec后俩个命令被连续的执行,最后返回的是两条命令执行后的结果 我们可以调用discard命令来取消一个事务。接着上面例子redis& multi OK redis& incr a QUEUED redis& incr b QUEUED redis& discard OK redis& get a  redis& get b 可以发现这次incr a incr b都没被执行。discard命令其实就是清空事务的命令队列并退出事务上下文。 虽说redis事务在本质上也相当于序列化隔离级别的了。但是由于事务上下文的命令只排队并不立即执行,所以事务中的写操作不能依赖事务中的读操作结果。看下面例子redis& multi OK redis& get a QUEUED redis& get b QUEUED redis& exec 1.  2. 发现问题了吧。假如我们想用事务实现incr操作怎么办?可以这样做吗?redis& get a  redis& multi OK redis& set a 2 QUEUED redis& exec 1. OK redis& get a, 结论很明显这样是不行的。这样和 get a 然后直接set a是没区别的。很明显由于get a 和set a并不能保证两个命令是连续执行的(get操作不在事务上下文中)。很可能有两个client同时做这个操作。结果我们期望是加两次a从原来的1变成3. 但是很有可能两个client的get a,取到都是1,造成最终加两次结果却是2。主要问题我们没有对共享资源a的访问进行任何的同步 也就是说redis没提供任何的加锁机制来同步对a的访问。 还好redis 2.1后添加了watch命令,可以用来实现乐观锁。看个正确实现incr命令的例子,只是在前面加了watch aredis& watch a OK redis& get a  redis& multi OK redis& set a 2 QUEUED redis& exec 1. OK redis& get a, watch 命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败。也可以调用watch多次监视多个key.这 样就可以对指定的key加乐观锁了。注意watch的key是对整个连接有效的,事务也一样。如果连接断开,监视和事务都会被自动清除。当然了 exec,discard,unwatch命令都会清除连接中的所有监视. redis的事务实现是如此简单,当然会存在一些问题。第一个问题是redis只能保证事务的每个命令连续执行,但是如果事务中的一个命令失败了,并不回滚其他命令,比如使用的命令类型不匹配。redis& set a 5 OK redis& lpush b 5 (integer) 1 redis& set c 5 OK redis& multi OK redis& incr a QUEUED redis& incr b QUEUED redis& incr c QUEUED redis& exec 1. (integer) 6 2. (error) ERR Operation against a key holding the wrong kind of value 3. (integer) 6可以看到虽然incr b失败了,但是其他两个命令还是执行了。 最 后一个十分罕见的问题是 当事务的执行过程中,如果redis意外的挂了。很遗憾只有部分命令执行了,后面的也就被丢弃了。当然如果我们使用的append-only file方式持久化,redis会用单个write操作写入整个事务内容。即是是这种方式还是有可能只部分写入了事务到磁盘。发生部分写入事务的情况 下,redis重启时会检测到这种情况,然后失败退出。可以使用redis-check-aof工具进行修复,修复会删除部分写入的事务内容。修复完后就 能够重新启动了。12 redis的持久化redis是一个支持持久化的内存数据库,也就是说redis需要经常将内存中的数据同步到磁盘来保证持久化。redis支持两种持久化方式,一种是 Snapshotting(快照)也是默认方式,另一种是Append-only file(缩写aof)的方式。下面分别介绍 Snapshotting 快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久 化的方式。我们可以配置redis在n秒内如果超过m个key被修改就自动做快照,下面是默认的快照保存配置 save 900 1 #900秒内如果超过1个key被修改,则发起快照保存 save 300 10 #300秒内容如超过10个key被修改,则发起快照保存 save 60 10000 下面介绍详细的快照保存过程 1.redis调用fork,现在有了子进程和父进程。 2. 父进程继续处理client请求,子进程负责将内存内容写入到临时文件。由于os的写时复制机制(copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时os会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程的地址空间内的数 据是fork时刻整个数据库的一个快照。 3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。 client 也可以使用save或者bgsave命令通知redis做一次快照持久化。save操作是在主线程中保存快照的,由于redis是用一个主线程来处理所有 client的请求,这种方式会阻塞所有client请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不 是增量的只同步脏数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘io操作,可能会严重影响性能。 另外由于快照方式是在一定间隔时间做一次的,所以如果redis意外down掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用aof持久化方式。下面介绍 Append-only file aof 比快照方式有更好的持久化性,是由于在使用aof持久化方式时,redis会将每一个收到的写命令都通过write函数追加到文件中(默认是 appendonly.aof)。当redis重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于os会在内核中缓存 write做的修改,所以可能不是立即写到磁盘上。这样aof方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis我们想要 通过fsync函数强制os写入到磁盘的时机。有三种方式如下(默认是:每秒fsync一次) appendonly yes //启用aof持久化方式 # appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用 appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,推荐 # appendfsync no //完全依赖os,性能最好,持久化没保证 aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用incr test命令100次,文件中必须保存全部的100条命令,其实有99条都是多余的。因为要恢复数据库的状态其实文件中保存一条set test 100就够了。为了压缩aof的持久化文件。redis提供了bgrewriteaof命令。收到此命令redis将使用与快照类似的方式将内存中的数据 以命令的方式保存到临时文件中,最后替换原来的文件。具体过程如下 1. redis调用fork ,现在有父子两个进程 2. 子进程根据内存中的数据库快照,往临时文件中写入重建数据库状态的命令 3.父进程继续处理client请求,除了把写命令写入到原来的aof文件中。同时把收到的写命令缓存起来。这样就能保证如果子进程重写失败的话并不会出问题。 4.当子进程把快照内容写入已命令方式写到临时文件中后,子进程发信号通知父进程。然后父进程把缓存的写命令也写入到临时文件。 5.现在父进程可以使用临时文件替换老的aof文件,并重命名,后面收到的写命令也开始往新的aof文件中追加。 需要注意到是重写aof文件的操作,并没有读取旧的aof文件,而是将整个内存中的数据库内容用命令的方式重写了一个新的aof文件,这点和快照有点类似。近期文章
分类目录选择分类目录书路&&(10)儿童画&&(121)&&&儿童作品&&(82)&&&儿童画教程&&(24)原创&&(127)&&&0基础编程&&(23)&&&android&&(9)&&&hadoop&&(18)&&&java原创&&(2)&&&livewriter&&(11)&&&nginx&&(52)资料&&(1,043)&&&android资料&&(83)&&&java资料&&(74)&&&linux资料&&(36)&&&mq&&(24)&&&mysql资料&&(34)&&&nginx资料&&(17)&&&svn&&(9)&&&wordpress&&(48)&&&搜索资料&&(45) 文章归档 选择月份 2017年九月 &(2) 2017年八月 &(1) 2017年七月 &(14) 2017年六月 &(23) 2017年五月 &(35) 2017年四月 &(38) 2017年三月 &(20) 2017年二月 &(5) 2017年一月 &(9) 2016年十二月 &(8) 2016年十一月 &(1) 2016年十月 &(10) 2016年九月 &(5) 2016年八月 &(8) 2016年七月 &(6) 2016年六月 &(9) 2016年五月 &(14) 2016年四月 &(6) 2016年三月 &(21) 2016年二月 &(11) 2016年一月 &(15) 2015年十二月 &(18) 2015年十一月 &(14) 2015年十月 &(6) 2015年九月 &(6) 2015年八月 &(7) 2015年七月 &(11) 2015年六月 &(19) 2015年五月 &(27) 2015年四月 &(19) 2015年三月 &(35) 2015年二月 &(38) 2015年一月 &(20) 2014年十二月 &(8) 2014年十一月 &(8) 2014年十月 &(3) 2014年九月 &(3) 2014年八月 &(3) 2014年七月 &(4) 2014年六月 &(3) 2014年五月 &(7) 2014年四月 &(9) 2014年三月 &(8) 2014年二月 &(8) 2014年一月 &(11) 2013年十二月 &(11) 2013年十一月 &(9) 2013年十月 &(40) 2013年九月 &(79) 2013年八月 &(50) 2013年七月 &(68) 2013年六月 &(50) 2013年五月 &(59) 2013年四月 &(65) 2013年三月 &(59) 2013年二月 &(20) 2013年一月 &(59) 2012年十二月 &(52) 2012年十一月 &(91) 2012年十月 &(23)}

我要回帖

更多关于 redis可以缓存什么 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信