Redis是一个速度非常快的非关系数据库(non-relational database),它可以存储键(key)与5种不同类型的值(value)之间的映射(mapping),可以将存储在内存的键值对数据持久化到硬盘,可以使用复制特性来扩展读性能,还可以使用客户端分片来扩展写性能。
下面的表格我们来对比下Redis和关系型数据库以及其他的NoSQL之间的区别:
名称 | 类型 | 数据存储选项 | 查询类型 | 附加功能 |
---|---|---|---|---|
Redis | 内存存储的非关系数据库 | 字符串、列表、集合、散列表、有序集合 | 每种数据类型都有自己的专属命令,另外还有批量操作和不完全的事务支持 | 发布/订阅、主从复制、持久化,脚本 |
Memcached | 内存存储的键值缓存 | 键值之间的映射 | 创建、读取、更新、删除以及其他几个命令 | 多线程 |
CouchBase | 面向文档的非关系数据库 | 键值无模式面向文档 | 创建、读取、更新、删除、批量以及N1QL等 | XDCR、view以及N1QL |
MongoDB | 硬盘存储的非关系型文档存储 | 每个数据库可以包含多个表,每个表可以包含多个无schema的BSON文档 | 创建、读取、跟新、删除以及条件查询 | 支持Map-reduce操作,主从复制,分片 |
MySQL | 关系型数据库 | 每个数据库可以包含多个表,每个表可以包含多个行;可以处理多个表视图;支持空间和第三方扩展 | select、insert、update 、delete、函数以及存储过程 | 支持ACID(innodb引擎)、主从复制/主主复制 |
PostgreSQL | 关系型数据库 | 每个数据库可以包含多个表,每个表可以包含多行;可以处理多个表的视图;支持空间和第三方扩展;支持可定制类型 | select、insert、update、delete、内置函数、自定义存储过程 | 支持ACID、主从复制以及有第三方支持的多主复制 |
Redis可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为string(字符串)、list(列表)、set(集合)、散列)Redis 和zset(有序集合)。有一部分Redis命令对于这5种结构都是通用的,如DEL、TYPE、RENAME等;但也有一部分Redis命令只能对特定的一种或者两种结构使用。
结构类型 | 结构存储的值 | 结构的读写能力 |
---|---|---|
STRING | 可以是字符串、整数或者浮点数 | 对整个字符串或者字符串的其中一部分执行操作;对整数和浮点数执行自增或者自减操作 |
HASH | 包含键值对的无序散列表 | 添加、获取、移除单个键值对;获取所有键值对 |
SET | 包含字符串的无序收集器,并且被包含的每个字符串都是唯一的 | 添加、获取、移除单个元素;检查一个元素是否存在于集合中;计算交际、并集、差集;从集合中随机获取要元素 |
LIST | 一个链表,链表上的每个节点都包含了一个字符串 | 从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单个或者多个元素;根据查找或者移除元素 |
ZSET | 字符串成员与浮点数分值之间的有序映射,元素的排列顺序由分值的大小决定 | 添加、获取、删除单个元素;根据分值范围或者成员来获取元素 |
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试。
list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。
使用pub/sub主题订阅者模式,可以实现 1:N 的消息队列。
redis 过期策略是:定期删除+惰性删除。
所谓定期删除,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。
假设 redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的灾难。实际上 redis 是每隔 100ms 随机抽取一些 key 来检查和删除的。
但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个 key 的时候,redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。
获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。
但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,咋整?
答案是:走内存淘汰机制。最常用的是allkeys-lru,即当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
简单的LRU实现。
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int CACHE_SIZE;
/**
* 传递进来最多能缓存多少数据
* @param cacheSize 缓存大小
*/
public LRUCache(int cacheSize) {
// true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
return size() > CACHE_SIZE;
}
}