环境搭建
基础环境
用户
1 2 3 4 5 6 7 8 9 10 11 12 13
# 增加用户,并赋予其密码 $ adduser redis $ passwd redis # ur password for redis user # 赋予用户可以 sudo 的权限 $ chmod u+w /etc/sudoers $ vim /etc/sudoers # 找到 `root ALL=(ALL) ALL` 这行,并在下面添加 redis 用户 redis ALL=(ALL) ALL $ chmod u-w /etc/sudoers # 切换到 redis 用户 $ su - redis
目录
1 2 3 4
$ cd /home/redis # 存放软件目录 & 安装目录 & 日志目录 & 持久化数据存放目录 $ mkdir install && mkdir software && mkdir logs && mkdir data
分布式集群
下载
1 2
$ cd ~/install $ wget http://download.redis.io/releases/redis-3.2.9.tar.gz
解压分发
1 2 3 4 5
$ tar zxvf redis-3.2.9.tar.gz -C ~/software $ cd ~/software/ $ scp -r redis-3.2.9/ redis@yuzhouwan02:/home/redis/software/ $ scp -r redis-3.2.9/ redis@yuzhouwan03:/home/redis/software/ $ ln -s redis-3.2.9/ redis
安装
1 2 3 4 5 6 7 8 9 10
$ cd ~/software/redis $ make -j8 # 输出如下信息,表示 make 成功 Hint: It's a good idea to run ' make test ' ;) make[1]: Leaving directory `/home/redis/software/redis-3.2.9/src' $ vim ~/.bash_profile REDIS_HOME=~/software/redis PATH=$PATH :$HOME /bin:$REDIS_HOME /src $ source ~/.bash_profile
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
$ mv redis.conf redis.conf.bak $ vim redis.conf bind 0.0.0.0 port 6379 # 启动 Redis 服务时,存储进程 pid 的位置 pidfile /home/redis/software/redis/redis.pid logfile "/home/redis/logs/redis.log" # 持久化文件存储目录 dir /home/redis/data # 开启集群模式 cluster-enabled yes # 集群模式下的节点配置信息 cluster-config-file nodes.conf # 集群中各节点间连接超时时间 cluster-node-timeout 5000 # 允许数据持久化追加 appendonly yes $ scp -r ~/software/redis/redis.conf redis@yuzhouwan02:/home/redis/software/redis/ $ scp -r ~/software/redis/redis.conf redis@yuzhouwan03:/home/redis/software/redis/
启动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
$ nohup redis-server redis.conf 1>/dev/null 2>&1 & $ ps -ef | grep redis redis 12781 9204 0 10:15 pts/0 00:00:00 redis-server 127.0.0.1:6379 [cluster] $ redis-cli -c -p 6379 127.0.0.1:6379> cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:1 cluster_size:0 cluster_current_epoch:0 cluster_my_epoch:0 cluster_stats_messages_sent:0 cluster_stats_messages_received:0 # 因为 redis 集群至少需要 6 个节点,所以如果只想部署在 3 台机器上的话,可以考虑在每一台上,启动两个 redis 进程,对应端口、日志路径、存储目录,则需要依次更改 $ cd ~/software && mkdir redis2 && cp redis/redis.conf redis2/ $ mkdir /home/redis/data2 $ vim redis2/redis.conf bind 0.0.0.0 port 6380 # 启动 Redis 服务时,存储进程 pid 的位置 pidfile /home/redis/software/redis2/redis.pid logfile "/home/redis/logs/redis2.log" # 持久化文件存储目录 dir /home/redis/data2 # 开启集群模式 cluster-enabled yes # 集群模式下的节点配置信息 cluster-config-file nodes2.conf # 集群中各节点间连接超时时间 cluster-node-timeout 5000 # 允许数据持久化追加 appendonly yes $ scp -r ~/software/redis2/redis.conf redis@yuzhouwan02:/home/redis/software/redis2/ $ scp -r ~/software/redis2/redis.conf redis@yuzhouwan03:/home/redis/software/redis2/ $ nohup redis-server redis2/redis.conf 1>/dev/null 2>&1 & # 如果启动不了,但是没有任何日志,需检查 redis2/redis.conf 是否和 redis/redis.conf 有路径、端口的冲突
集群配置
安装 Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
# Debian / Ubuntu $ apt-get install ruby # CentOS / Fedora / RHEL $ sudo yum install ruby # Offline # 下载 https://www.ruby-lang.org/en/downloads/ $ cd ~/install $ wget https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.1.tar.gz $ tar zxvf ruby-2.4.1.tar.gz -C ~/software $ cd ~/software/ruby-2.4.1 && mkdir ../ruby $ ./configure prefix=/home/redis/software/ruby $ make -j8 && make -j8 install $ vim ~/.bash_profile REDIS_HOME=~/software/redis RUBY_HOME=~/software/ruby PATH=$PATH :$HOME /bin:$REDIS_HOME /src:$RUBY_HOME /bin $ source ~/.bash_profile $ ruby -v ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]
安装 Redis 插件
1 2 3 4 5 6 7 8 9 10
# Online $ gem update --system $ gem install redis # 如果下载速度过慢,可考虑使用国内的 ruby 源 $ gem sources remove http://rubygems.org/ $ gem sources -a https://ruby.taobao.org/ # Offline $ cd ~/install $ wget https://rubygems.org/downloads/redis-3.2.2.gem
redis-trib 创建分布式集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
$ redis-trib.rb create --replicas 1 192.168.1.101:6379 192.168.1.102:6379 192.168.1.103:6379 192.168.1.101:6380 192.168.1.102:6380 192.168.1.103:6380 >>> Creating cluster >>> Performing hash slots allocation on 6 nodes... Using 3 masters: 192.168.1.101:6379 192.168.1.102:6379 192.168.1.103:6379 Adding replica 192.168.1.102:6380 to 192.168.1.101:6379 Adding replica 192.168.1.101:6380 to 192.168.1.102:6379 Adding replica 192.168.1.103:6380 to 192.168.1.103:6379 M: 4c0fb081525a5f1893479225576b75f03cca065d 192.168.1.101:6379 slots:0-5460 (5461 slots) master M: 4f9fc4536f6e4e30666264738d632b1bb54799f0 192.168.1.102:6379 slots:5461-10922 (5462 slots) master M: 4dde95537b69d2b23f2f9a4cd2a357a1e4af756e 192.168.1.103:6379 slots:10923-16383 (5461 slots) master S: 6ffea478b1e5d1187b85adc5b0bd11b6601dd556 192.168.1.101:6380 replicates 4f9fc4536f6e4e30666264738d632b1bb54799f0 S: 1213855d2c0d1b35020895af45dcd734da50ef2c 192.168.1.102:6380 replicates 4c0fb081525a5f1893479225576b75f03cca065d S: 94478f8642d5a0cef1a989a620f132221e35fc8a 192.168.1.103:6380 replicates 4dde95537b69d2b23f2f9a4cd2a357a1e4af756e Can I set the above configuration? (type 'yes' to accept): yes >>> Nodes configuration updated >>> Assign a different config epoch to each node >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join... >>> Performing Cluster Check (using node 192.168.1.101:6379) M: 4c0fb081525a5f1893479225576b75f03cca065d 192.168.1.101:6379 slots:0-5460 (5461 slots) master 1 additional replica(s) S: 6ffea478b1e5d1187b85adc5b0bd11b6601dd556 192.168.1.101:6380 slots: (0 slots) slave replicates 4f9fc4536f6e4e30666264738d632b1bb54799f0 M: 4dde95537b69d2b23f2f9a4cd2a357a1e4af756e 192.168.1.103:6379 slots:10923-16383 (5461 slots) master 1 additional replica(s) S: 1213855d2c0d1b35020895af45dcd734da50ef2c 192.168.1.102:6380 slots: (0 slots) slave replicates 4c0fb081525a5f1893479225576b75f03cca065d S: 94478f8642d5a0cef1a989a620f132221e35fc8a 192.168.1.103:6380 slots: (0 slots) slave replicates 4dde95537b69d2b23f2f9a4cd2a357a1e4af756e M: 4f9fc4536f6e4e30666264738d632b1bb54799f0 192.168.1.102:6379 slots:5461-10922 (5462 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
redis-cli
集群状态检查
1 2 3 4 5 6 7 8 9 10 11 12 13 14
# 至此,大功告成~ $ redis-cli -c -p 6379 127.0.0.1:6379> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:1 cluster_stats_messages_sent:376 cluster_stats_messages_received:376
关闭
遇到的坑
cannot load such file — zlib
描述
1 2 3 4 5 6 7
$ gem install -l redis-3.2.2.gem # 如果出现如下报错,则应该先安装 zlib-devel ERROR: Loading command : install (LoadError) cannot load such file -- zlib ERROR: While executing gem ... (NoMethodError) undefined method `invoke_with_build_args' for nil:NilClass
解决
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
$ yum install zlib-devel # 离线环境下,推荐先在有网环境中,使用 --downloadonly 的方式,把依赖包直接下载下来(也可在 https://www.rpmfind.net 里面找,但比较低效) $ yum reinstall --downloadonly --downloaddir=/opt/software/zlib zlib-devel zlib libc.so.6 glibc-common glibc libfreebl3.so zlib-1.2.3-27.el6 -y $ ll /opt/software/zlib -rw-r--r-- 1 root root 4558520 Feb 18 2016 glibc-2.12-1.166.el6_7.7.i686.rpm -rw-r--r-- 1 root root 14887196 Feb 18 2016 glibc-common-2.12-1.166.el6_7.7.x86_64.rpm -rw-r--r-- 1 root root 118976 Nov 9 2011 nss-softokn-freebl-3.12.9-11.el6.i686.rpm -rw-r--r-- 1 root root 73604 Sep 26 2011 zlib-1.2.3-27.el6.i686.rpm -rw-r--r-- 1 root root 73864 Sep 26 2011 zlib-1.2.3-27.el6.x86_64.rpm -rw-r--r-- 1 root root 44728 Sep 26 2011 zlib-devel-1.2.3-27.el6.x86_64.rpm # 可能存在 "循环依赖" 和 "依赖冲突" # 前者,可以通过 force 强制安装好,其中一个 $ rpm -ivh glibc-2.12-1.166.el6_7.7.i686 --nodeps --force # 后者,可以通过 nodeps 不删除被依赖包,进行卸载 # 【注意】/lib64/libz* 需要提前备份好,否则卸载掉 zlib 之后,会因为缺少 libz.so.1,导致 yum/rpm/ssh 等命令均失效! $ rpm -e --nodeps zlib-1.2.3-29.el6.x86_64 # 安装完成之后,将 Ruby 重新编译安装一遍,即可
ERR Slot 0 is already busy
描述
1 2
# 执行 redis-trib.rb 遇到 ERR Slot 0 is already busy (Redis::CommandError)
解决
1 2 3 4
# 通过 cluster reset soft 命令解决 $ redis-cli -h 127.0.0.1 -p 6379 cluster reset soft && redis-cli -h 127.0.0.1 -p 6380 cluster reset soft OK OK
ERR Invalid node address specified
描述
1 2 3
# 执行创建 Redis 集群 $ redis-trib.rb create --replicas 1 yuzhouwan01:6379 yuzhouwan02:6379 yuzhouwan03:6379 yuzhouwan01:6380 yuzhouwan02:6380 yuzhouwan03:6380 # 报错 ERR Invalid node address specified
解决
1 2
# 目前为止,只支持 IP,不支持 Hostname,因此将主机名替换为 IP 即可(PR#2323 中准备解决该问题) $ redis-trib.rb create --replicas 1 192.168.1.101:6379 192.168.1.102:6379 192.168.1.103:6379 192.168.1.101:6380 192.168.1.102:6380 192.168.1.103:6380
连接集群
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
private void init (DynamicPropUtils DP) { Object clusterListObj = DP.get(PROJECT_NAME, "redis.cluster.list" ); String clusterList; if (clusterListObj == null || StrUtils.isEmpty(clusterList = clusterListObj.toString())) { String error = String.format("Cannot get [%s-redis.cluster.list] from Dynamic PropUtils!" , PROJECT_NAME); _log.error(error); throw new RuntimeException(error); } String[] hostAndPort; Set<HostAndPort> jedisClusterNodes = new HashSet<>(); for (String clusters : clusterList.split("," )) { hostAndPort = clusters.split(":" ); jedisClusterNodes.add(new HostAndPort(hostAndPort[0 ], Integer.valueOf(hostAndPort[1 ]))); } JedisPoolConfig conf = new JedisPoolConfig(); conf.setMaxTotal(1000 ); conf.setMinIdle(50 ); conf.setMaxIdle(100 ); conf.setMaxWaitMillis(6 * 1000 ); conf.setTestOnBorrow(true ); pool = new JedisCluster(jedisClusterNodes, conf); }
集群操作
1 2 3 4 5 6
public Long putSet (String key, String... values) { return pool.sadd(key, values); } public Set<String> getSet (String key) { return pool.smembers(key); }
资源释放
1 2 3
public void close () throw IOException { if (pool != null ) pool.close(); }
1 2 3 4 5 6 7
# 通过 redis-cli 进行动态配置 $ redis-cli -h localhost -p 6380 CONFIG SET notify-keyspace-events AKE # AKE: 意味着通知所有事件 # 修改 redis.conf,并重启集群使其生效 $ vim redis.conf notify-keyspace-events AKE
服务端验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
$ redis-cli 127.0.0.1:6379> set yuzhouwan01 blog OK 127.0.0.1:6379> expire yuzhouwan01 3 (integer ) 1 127.0.0.1:6379> ttl yuzhouwan01 (integer ) 0 127.0.0.1:6379> ttl yuzhouwan01 (integer ) -2 $ redis-cli -h localhost -p 6379 --csv psubscribe '*' "pmessage" ,"*" ,"__keyspace@0__:yuzhouwan01" ,"set" "pmessage" ,"*" ,"__keyevent@0__:set" ,"yuzhouwan01" "pmessage" ,"*" ,"__keyspace@0__:yuzhouwan01" ,"expire" "pmessage" ,"*" ,"__keyevent@0__:expire" ,"yuzhouwan01" "pmessage" ,"*" ,"__keyspace@0__:yuzhouwan01" ,"expired" "pmessage" ,"*" ,"__keyevent@0__:expired" ,"yuzhouwan01"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
private String getClusterList (DynamicPropUtils DP) { Object clusterListObj = DP.get(PROJECT_NAME, "redis.cluster.list" ); String clusterList; if (clusterListObj == null || StrUtils.isEmpty(clusterList = clusterListObj.toString())) { String error = String.format("Cannot get [%s-redis.cluster.list] from Dynamic PropUtils!" , PROJECT_NAME); _log.error(error); throw new RuntimeException(error); } return clusterList; } private JedisPoolConfig buildConf () { // org.apache.commons.pool2.impl.BaseObjectPoolConfig JedisPoolConfig conf = new JedisPoolConfig(); conf.setMaxTotal(1000 ); conf.setMinIdle(50 ); conf.setMaxIdle(100 ); // conf.setMaxWaitMillis(6 * 1000); conf.setTestOnCreate(true ); conf.setTestOnBorrow(true ); conf.setTestOnReturn(true ); conf.setTestWhileIdle(true ); // conf.setTimeBetweenEvictionRunsMillis(1); conf.setNumTestsPerEvictionRun(30 ); return conf; } // 这里不要使用 JedisCluster,因为 cluster 模式下,jedis 只会连接一个节点,会导致监听不到其他节点上的事件 private void initPools (DynamicPropUtils DP) { String clusterList = getClusterList(DP); pools = new LinkedList<>(); String[] hostAndPort; for (String clusters : clusterList.split("," )) { hostAndPort = clusters.split(":" ); pools.add(new JedisPool(buildConf(), hostAndPort[0 ], Integer.valueOf(hostAndPort[1 ]))); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
try (RedisClusterConnPool pool = new RedisClusterConnPool(dp, true )) { List<JedisPool> jedis = pool.getPools(); JedisPubSub jedisPubSub = new JedisPubSub() { @Override public void onPSubscribe (String pattern, int subscribedChannels) { _log.info("onPSubscribe {} {}" , pattern, subscribedChannels); } @Override public void onPMessage (String pattern, String channel, String message) { _log.info("onPMessage: {}, Channel: {}, Message: {}" , pattern, channel, message); } }; for (JedisPool j : jedis) j.getResource().psubscribe(jedisPubSub, "__keyevent@*__:expired" /*"__key*__:*"*/ /*"*"*/ ); }
1 2 3 4 5 6 7 8
pool.put("yuzhouwan01" , "blog01" ); pool.expire("yuzhouwan01" , 2 ); Thread.sleep(2100 ); assertEquals(null , pool.get("yuzhouwan01" )); 2017 -07 -26 16 :18 :56.045 | INFO | onPMessage Pattern: *, Channel: __keyevent@0 __:set, Message: yuzhouwan01 | com.yuzhouwan.bigdata.redis.notification.RedisNotification.onPMessage | RedisNotification.java:61 2017 -07 -26 16 :18 :56.045 | INFO | onPMessage Pattern: *, Channel: __keyevent@0 __:expire, Message: yuzhouwan01 | com.yuzhouwan.bigdata.redis.notification.RedisNotification.onPMessage | RedisNotification.java:61 2017 -07 -26 16 :18 :57.064 | INFO | onPMessage Pattern: *, Channel: __keyevent@0 __:expired, Message: yuzhouwan01 | com.yuzhouwan.bigdata.redis.notification.RedisNotification.onPMessage | RedisNotification.java:61
实战技巧
匹配删除
1 2 3 4 5 6 7 8
$ redis-cli flushdb $ redis-cli --raw -n 1 keys "yuzhouwan*" | xargs -L1 -I{} redis-cli move {} 0 $ redis-cli 127.0.0.1:6379> keys yuzhouwan* (empty list or set ) # 也可以安装使用 redis-utils-cli(https://www.npmjs.com/package/redis-utils-cli) $ redis-utils del 127.0.0.1 yuzhouwan*
数据容灾
主从热备 + 二级缓存 + 从库 AOF(主从切换)
参考
全局流控
流控方案
Leaky
bucket 漏桶算法
桶内水溢出时,可以把消息放到队列中等待
Token
bucket 令牌桶算法
桶内令牌不足时,可以把消息放到队列中等待
实现方式
Guava
RateLimiter
Guava 无法做到全局流控
Redis Expire 机制
如果设置的超时时间过长,可能对内存有一定的损耗
Hystrix 限流
支持 自动降级、熔断与恢复、依赖隔离、异常记录、流量控制、实时监控,不过会存在一定的 代码侵入 和 性能损耗 的问题(1%~5%)
参考
Tips: Full code is here .
技术内幕
缓存穿透 vs. 缓存雪崩 vs. 缓存失效
缓存穿透
场景描述
一般的缓存系统,都是按照 key 去缓存查询,如果不存在对应的 value,就应该去后端系统查找(比如 DB)。如果 key 对应的 value 是一定不存在的,并且对该 key
并发请求量很大,就会对后端系统造成很大的压力,我们称之为缓存穿透
解决方案
对查询结果为空的情况也进行缓存,缓存过期时间设置短一点(避免消耗太多的缓存空间),或者该 key 对应的数据 insert 了之后清理缓存
对一定不存在的 key 进行过滤。可以把所有的可能存在的 key 放到一个大的 Bitmap 中,查询时通过该 Bitmap 过滤
排查是否是自身程序或者数据的问题,亦或是外部恶意攻击或者爬虫,导致大量访问不存在的 key 值
缓存雪崩
场景描述
缓存雪崩 ,是指当缓存服务器重启或者大量缓存集中在某一个时间段失效,这样在失效的时候,也会给后端系统(比如 DB)带来很大压力
解决方案
在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个 key 只允许一个线程查询数据和写缓存,其他线程等待
不同的 key,设置不同的过期时间,让缓存失效的时间点尽量均匀
做二级缓存,A1 为原始缓存,A2 为拷贝缓存。A1 失效时,可以访问 A2。A1 缓存失效时间设置为短期,A2 设置为长期
保证缓存层服务的高可用,后端组件做好限流措施,并提前预演缓存层失效的场景
缓存失效
场景描述
缓存失效 ,是指缓存集中在一段时间内失效,DB 的压力凸显
解决方案
这个没有完美解决办法,但可以分析用户行为,尽量让失效时间点均匀分布。大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线程(进程)写,从而避免失效时大量的并发请求落到底层存储系统上
其他
当发生大量的缓存穿透 ,例如对某个失效的缓存的大并发访问就造成了缓存雪崩
缓存失效 的同时发生雪崩效应 ,对底层系统的冲击将会非常大。这时候,可以使用双缓存机制,在工作缓存之外另外维护一层灾备缓存
双缓存 vs. 二级缓存
一般的,用 Keepalived 做双机热备,而用 J2Cache 来做二级缓存
Tips: Full code is here .
为什么 Redis 一致性算法使用 Raft ,而不是 Paxos ?
没有应用场景上的区别,主要是因为 Raft 算法更为简单 、好懂 和 易实现
协议上的简化
最后主要 RPC 只有两个,其他协议的二阶段、三阶段也都变成 看起来像是一阶段
Term 概念的强化
看起来似乎 Paxos 也有重选 Leader 的机制,但是强化概念,并增加一个 Term,包含有一个 Leader、Entry 与 Term 相关的属性等都大大简化了流程
Log 只会从 Leader 到 Follower 单向同步
实现一下,会发现减少了很多问题,代码实现的复杂度也下降了很多
性能
都比较高,性能对我们来说应该都不是瓶颈 从 TPS 的角度来看,Redis 和 Memcached 差不多,要略优于 MongoDB
操作的便利性
Redis 丰富一些,数据操作方面,Redis 更好一些,较少的网络 I/O 次数 Memcached 数据结构单一 MongoDB
支持丰富的数据表达,索引,最类似关系型数据库,支持的查询语言非常丰富
内存空间的大小和数据量的大小
Redis 在 2.0 版本后增加了自己的 VM 特性,突破物理内存的限制;可以对 K-V
设置过期时间 (类似 Memcached) Memcached 可以修改最大可用内存,采用
LRU 算法 MongoDB 适合大数据量的存储,依赖操作系统 VM 做内存管理,吃内存也比较厉害,服务不要和其他的放在一起
可用性(单点问题)
Redis 依赖客户端来实现分布式读写;主从复制时,每次从节点重新连接主节点都要依赖整个快照,无增量复制,因性能和效率问题,所以单点问题比较复杂 不支持自动 sharding,需要依赖程序设定一致 hash
机制 一种替代方案是,不用Redis本身的复制机制,采用自己做主动复制(多份存储),或者改成增量复制的方式(需要自己实现),一致性问题和性能的权衡
Memcached 本身没有数据冗余机制,也没必要;对于故障预防,采用依赖成熟的 hash 或者环状的算法,解决单点故障引起的抖动问题。 MongoDB 支持
master-slave、replicaset(内部采用 Paxos 选举算法,自动故障恢复)、auto sharding 机制,对客户端屏蔽了故障转移和切分机制。
可靠性(持久化)
Redis 对于数据持久化和数据恢复,Redis 支持(快照、AOF):依赖快照进行持久化,AOF 增强了可靠性的同时,对性能有所影响 Memcached
不支持,通常用在做缓存,提升性能 MongoDB 从 1.8 版本开始采用 binlog 方式支持持久化的可靠性
数据一致性(事务支持)
Redis 事务支持比较弱,只能保证事务中的每个操作连续执行 Memcached 在并发场景下,用 CAS 保证一致性 MongoDB 不支持事务
数据分析
MongoDB 内置了数据分析的功能(MapReduce),其他不支持
应用场景
Redis 数据量较小的更性能操作和运算上 Memcached 用于在动态系统中减少数据库负载,提升性能;做缓存,提高性能(适合读多写少,对于数据量比较大,可以采用 sharding) MongoDB
主要解决海量数据的访问效率问题
资料
Doc
Blog