Background(什么场景下发明了Redis)
2008年,Redis的作者Salvatore Sanfilippo 在开发一个叫LLOOGG的网站时,需要实现一个高性能的队列功能,最开始用MySQL实现,但是后来无论怎么优化SQL都不能使网站的性能提上去,就自己写了一个数据库。
https://blog.csdn.net/chengqiuming/article/details/79113358
Why Redis
关键词: 内存, 快,单线程,NoSQL
1. 速度快
- Redis 所有的数据都放在内存中
- Redis 是用C语言实现的,速度一般来说更快。
- 单线程,避免了多线程切换时间,以及竞争问题。(单线程有个问题就是阻塞)
2. 支持多种数据结构
- String (int, embstr, raw)
- Hash(ziplist, hashtable )
- List (ziplist, linkedlist)
- Set (intset, hashtable)
- ZSet(skiplist, ziplist)
3. 丰富的功能
- TTL (e.g. Session 管理)
- Pub/Sub (简单的消息队列)
- Lua (复杂命令)
- 事务
- Pipeline (批量命令,减少网络开销)
4. 简单稳定
5. 多语言支持(Python, Java,PHP, C, C++, Nodejs, etc.)
Java内部有一些缓存类,为什么要使用Redis作为缓存?
- 程序宕机后缓存会消失。
- 多语言支持,在多服务系统中可以共同读取一个缓存。
6. 持久化 (RDB & AOF)
为什么使用Redis而不是用Memcache?
业务场景需要缓存的数据可以持久化,Redis可以持久化, Memcache不支持持久化。
7. 主从复制
数据可靠性
既然数据可以持久化,为什么还需要用到主从复制功能? 解决了什么场景的问题?
1. 故障恢复,持久化只是保证了数据不会丢失,但是在遇到故障的时候无法快速响应,只能等待重启,不能保证高可用。
2. Load Balance
8. 高可用和分布式(Sentinel)
在Redis节点出现故障的时候会自动转移节点。
什么是高可用?
Why Redis but not .... (Why we choose Redis?)
1. Why Redis but not java cache?
a. 缓存不能随着程序的宕机而消失。
场景:使用Redis作为用户Session数据缓存,服务器重启后依然可以记住用户登录状态,无需重新登录。
b. 如果服务器重启后,缓存的数据不再有意义,则不推荐使用Redis
场景:异步执行的TaskId,程序重启,这些任务不再被执行,存储这些TaskId将不再有意义。
c. 有多个程序或者服务共用一套缓存。
2. Why Redis but not Memcache?
a. 业务场景需要支持多种数据结构,Redis支持多种数据结构
b. 业务场景中Value的大小可能会很大(Redis 的value最大支持512M, Memcache的value最大支持1M)
c. 需要持久化
d. 过期策略可变(自己想到的例子,用户每次登陆,都会重新设置过期时间)
什么场景先建议使用Memcache而不是使用Redis(如果你只有这两个选择)
1. 图片视频(这里有个疑问,既然Memcache最大支持的value为1M,怎么存视频?)
2. 多读少写,大数据量。
3. Why Redis but not SQL?
1. 速度
2. 数据量不大。
什么场景不建议使用缓存,二是直接使用数据库。
1. 数据量很大,毕竟内存要比硬盘贵很多。
2. 对响应时间没有那么高的要求,毕竟多一个Redis就要多一个部署的服务。
3. 冷数据(不常用的数据没必要放在内存里)
Redis 的使用场景
1. Cache
数据查询:
如果有直接返回
如果没有查询数据库,写入缓存(可以添加过期时间),返回。
数据更新/删除:这里面坑比较多,可以根据实际业务场景判断先处理缓存还是先处理数据库的问题。
2. Rank (zset)
zadd key score members
e.g. zadd score 98 Alice
3. Count
incr key 的操作
4. MessageQueue
Pub/Sub,如果只是需要一个简单消息队列,没有特别复杂的规则,可以使用Redis,减少部署一个MQ的成本。
5. Distribution Lock (https://redis.io/topics/distlock)
加锁 主要是用到 setnx 这个命令
public class RedisTool {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "PX";
/**
* 尝试获取分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @param expireTime 超期时间
* @return 是否获取成功
*/
public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {
String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);
if (LOCK_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
解锁
public class RedisTool {
private static final Long RELEASE_SUCCESS = 1L;
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}
6. 共享Session
多个服务使用同一个Session,避免用户重复登录。如同一个服务部署在不同的服务器上(Load Balance)以及单点登录。
7. 限速限流
利用过期时间(如一分钟超时,如果key exist,不能重复发验证码请求)