文章目录
Redis概述
1、简介
Redis是当下最火爆流行的内存数据库之一,是典型的非关系型数据库,通过在内存中读写数据极大地提高了读写速度。
Redis为什么这么快?
- 完全基于内存,数据存在内存中;
- 数据结构简单,对数据的操作也简单;
- 使用单线程,避免不必要的上下文切换和竞争;
- 原子性操作
特点:
- key-value型开源数据库;
- 支持事务,发布订阅;
- 数据可持久化
客户端与Redis的通信过程:
2、应用场景
2.1 缓存
String类型
例如:热点数据缓存(例如报表、明星出轨),对象缓存、全页缓存、可以提升热点数据的访问数据。
2.2 数据共享分布式
String 类型,因为 Redis 是分布式的独立服务,可以在多个应用之间共享
例如:分布式Session
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2.3 分布式锁
String 类型setnx方法,只有不存在时才能添加成功,返回true
public static boolean getLock(String key) {
Long flag = jedis.setnx(key, "1");
if (flag == 1) {
jedis.expire(key, 10);
}
return flag == 1;
}
public static void releaseLock(String key) {
jedis.del(key);
}
2.4 全局ID
int类型,incrby,利用原子性
incrby userid 1000
分库分表的场景,一次性拿一段
2.5 计数器
int类型,incr方法
例如:文章的阅读量、微博点赞数、允许一定的延迟,先写入Redis再定时同步到数据库
2.6 限流
int类型,incr方法
以访问者的ip和其他信息作为key,访问一次增加一次计数,超过次数则返回false
2.7 位统计
String类型的bitcount(1.6.6的bitmap数据结构介绍)
字符是以8位二进制存储的
set k1 a
setbit k1 6 1
setbit k1 7 0
get k1
/* 6 7 代表的a的二进制位的修改
a 对应的ASCII码是97,转换为二进制数据是01100001
b 对应的ASCII码是98,转换为二进制数据是01100010
因为bit非常节省空间(1 MB=8388608 bit),可以用来做大数据量的统计。
*/
例如:在线用户统计,留存用户统计
setbit onlineusers 01
setbit onlineusers 11
setbit onlineusers 20
支持按位与、按位或等等操作
BITOPANDdestkeykey[key...] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey
BITOPORdestkeykey[key...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey
BITOPXORdestkeykey[key...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey
BITOPNOTdestkeykey ,对给定 key 求逻辑非,并将结果保存到 destkey
计算出7天都在线的用户
BITOP "AND" "7_days_both_online_users" "day_1_online_users" "day_2_online_users" ... "day_7_online_users"
2.8 购物车
String 或hash。所有String可以做的hash都可以做
- key:用户id;field:商品id;value:商品数量。
- +1:hincr;-1:hdecr;删除:hdel;全选:hgetall;商品数:hlen。
2.9 用户消息时间线timeline
list,双向链表,直接作为timeline就好了。插入有序
2.10 消息队列
List提供了两个阻塞的弹出操作:blpop/brpop,且可以设置超时时间
- blpop:blpop key1 timeout 移除并获取列表的第一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
- brpop:brpop key1 timeout 移除并获取列表的最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
上面的操作,其实就是java的阻塞队列
- 队列:先进先出:rpush blpop,左头右尾,右边进入队列,左边出队列
- 栈:先进后出:rpush brpop
2.11 抽奖
自带一个随机获得值
spop myset
2.12 点赞、签到、打卡
假如上面的微博ID是t1001,用户ID是u3001
用 like:t1001 来维护 t1001 这条微博的所有点赞用户:
- 点赞了这条微博:sadd like:t1001 u3001
- 取消点赞:srem like:t1001 u3001
- 是否点赞:sismember like:t1001 u3001
- 点赞的所有用户:smembers like:t1001
- 点赞数:scard like:t1001
2.13 商品标签
用 tags:i5001 来维护商品所有的标签:
- add tags:i5001 画面清晰细腻
- sadd tags:i5001 真彩清晰显示屏
- sadd tags:i5001 流程至极
2.14 商品筛选
// 获取差集sdiff set1 set2// 获取交集(intersection )sinter set1 set2// 获取并集sunion set1 set2
假如:iPhone11 上市了
sadd brand:apple iPhone11sadd brand:ios iPhone11sad screensize:6.0-6.24 iPhone11sad screentype:lcd iPhone11
赛选商品,苹果的、ios的、屏幕在6.0-6.24之间的,屏幕材质是LCD屏幕
sinter brand:apple brand:ios screensize:6.0-6.24 screentype:lcd
2.15 用户关注、推荐模型
follow 关注 fans 粉丝
相互关注:
sadd 1:follow 2sadd 2:fans 1sadd 1:fans 2sadd 2:follow 1
我关注的人也关注了他(取交集):
sinter 1:follow 2:fans
可能认识的人:
sdiff 2:follow 1:follow # 用户1可能认识的人(差集)sdiff 1:follow 2:follow # 用户2可能认识的人
2.16 排行榜
id 为6001 的新闻点击数加1:
zincrby hotNews:20190926 1 n6001
获取今天点击最多的15条:
zrevrange hotNews:20190926 0 15 withscores
3、Redis安装与配置
3.1 安装
3.1.1 Windows下,安装Redis
安装
进入解压路径后,执行如下命令:
redis-server redis.windows.conf
服务启动与停止
常用命令:
卸载服务redis-server --service-uninstall开启服务redis-server --service-start停止服务resid-server --service-stop
进入redis目录下运行
redis-cli.exe -h 127.0.0.1 -p 6379
3.1.2 Linux下,安装Redis
3.1.3 Docker下,安装Redis
先执行search命令,以redis为关键字搜索docker hub,查看是否有合适版本的redis镜像:
docker search redis
拉取docker镜像
docker pull redis:latest
新建docker容器
docker run -itd --name redis --port 6379:6379 redis:latest
启动docker容器
docker exec -d redis /bin/bash
3.2 Redis配置
在Redis的根目录下有一个配置文件(redis.conf),当然也可以通过Redis CONFIG命令获取和设置所有的Redis配置。
CONFIG命令的基本语法:
CONFIG GET CONFIG_SETTING_NAME
示例:
CONFIG GET loglevel1) "loglevel" 2) "notice"
要获取所有配置设置,请使用*
代替CONFIG_SETTING_NAME
如果要更新配置,可以直接编辑redis.conf文件,也可以通过CONFIG set命令更新配置。
以下是CONFIG SET
命令的基本语法:
CONFIG SET CONFIG_SETTING_NAME NEW_CONFIG_VALUE
示例:
CONFIG SET loglevel "notice" OK
Redis数据类型
Redis支持5种数据类型。
1、字符串
Redis中的字符串是一个字节序列。Redis中的字符串是二进制安全的,这意味着它们的长度不由任何特殊的终止字符决定。因此,可以在一个字符串中存储高达512
兆字节的任何内容。
字符串的内部编码可能为int、embstr或raw。
示例:
set name "yiibai.com"OK
在上面的示例中,set
和get
是Redis命令,name
是Redis中使用的键,yiibai.com
是存储在Redis中的字符串的值。
**注意:**Redis命令不区分大小写,如SET
,Set
和set
都是同一个命令。字符串值的最大长度为 512MB。
下表列出了一些用于在Redis中管理字符串的基本命令。
序号 | 命令 | 描述说明 |
---|---|---|
1 | SET key value | 此命令设置指定键的值 |
2 | GET key | 获取指定键的值 |
3 | GETRANGE key start end | 获取存储在键上的字符串的子字符串 |
4 | GETSET key value | 设置键的字符串值并返回其旧值 |
5 | GETBIT key offset | 返回在键处存储的字符串值中偏移处的位值 |
6 | MGET key1 [key2] | 获取所有给定键的值 |
7 | SETBIT key offset value | 存储在键上的字符串值中设置或清除偏移处的位 |
8 | SETEX key seconds value | 用键和到期时间来设置值 |
9 | SETNX key value | 设置键的值,仅当键不存在时 |
10 | SETRANGE key offset value | 在指定偏移处开始的键处覆盖字符串的一部分 |
11 | STRLEN key | 获取存储在键中的值的长度 |
12 | MSET key value [key value …] | 为多个键分别设置它们的值 |
13 | MSETNX key value [key value …] | 为多个键分别设置它们的值,仅当键不存在时 |
14 | PSETEX key milliseconds value | 设置键的值和到期时间(以毫秒为单位) |
15 | INCR key | 将键的整数值增加1 |
16 | INCRBY key increment | 将键的整数值按给定的数值增加 |
17 | INCRBYFLOAT key increment | 将键的浮点值按给定的数值增加 |
18 | DECR key | 将键的整数值减1 |
19 | DECRBY key decrement | 按给定数值减少键的整数值 |
20 | APPEND key value | 将指定值附加到键 |
2、散列/哈希
Redis散列/哈希(Hashes)是键值对的集合。Redis散列/哈希是字符串字段和字符串值之间的映射。因此,它们用于表示对象。
哈希的内部编码可以是ziplist或hashtable。
示例:
HMSET ukey username "yiibai" password "passswd123" points 200
在上述示例中,散列/哈希数据类型用于存储包含用户的基本信息的用户对象。这里HMSET
,HGETALL
是Redis的命令,而ukey
是键的名称。
每个散列/哈希可以存储多达2^32 - 1
个健-值对(超过40
亿个)。
下表列出了与哈希/散列相关的一些基本命令:
序号 | 命令 | 说明 |
---|---|---|
1 | HDEL key field2 [field2] | 删除一个或多个哈希字段 |
2 | HEXISTS key field | 判断是否存在散列字段 |
3 | HGET key field | 获取存储在指定键的哈希字段的值 |
4 | HGETALL key | 获取存储在指定键的哈希中的所有字段和值 |
5 | HINCRBY key field increment | 将哈希字段的整数值按给定数字增加 |
6 | HINCRBYFLOAT key field increment | 将哈希字段的浮点值按给定数值增加 |
7 | HKEYS key | 获取哈希中的所有字段 |
8 | HLEN key | 获取散列中的字段数量 |
9 | HMGET key field1 [field2] | 获取所有给定哈希字段的值 |
10 | HMSET key field1 value1 [field2 value2 ] | 为多个哈希字段分别设置它们的值 |
11 | 设置散列字段的字符串值 | |
12 | HSETNX key field value | 仅当字段不存在时,才设置散列字段的值 |
13 | HVALS key | 获取哈希中的所有值 |
3、列表
Redis列表只是字符串列表,按插入顺序排序。您可以向Redis列表的头部或尾部添加元素。
列表的内部编码可以是ziplist或linkedlist。
示例:
lpush alist redis lpush alist mongodb lpush alist sqlitelrange alist 0 101) "sqlite" 2) "mongodb" 3) "redis"
列表的最大长度为2^32 - 1
个元素(4294967295
,每个列表可容纳超过40
亿个元素)。
下表列出了与列表相关的一些基本命令:
序号 | 命令 | 说明 |
---|---|---|
1 | BLPOP key1 [key2 ] timeout | 删除并获取列表中的第一个元素,或阻塞,直到有一个元素可用 |
2 | BRPOP key1 [key2 ] timeout | 删除并获取列表中的最后一个元素,或阻塞,直到有一个元素可用 |
3 | BRPOPLPUSH source destination timeout | 从列表中弹出值,将其推送到另一个列表并返回它; 或阻塞,直到一个可用 |
4 | LINDEX key index | 通过其索引从列表获取元素 |
5 | LINSERT key BEFORE/AFTER pivot value | 在列表中的另一个元素之前或之后插入元素 |
6 | LLEN key | 获取列表的长度 |
7 | LPOP key | 删除并获取列表中的第一个元素 |
8 | LPUSH key value1 [value2] | 将一个或多个值添加到列表 |
9 | LPUSHX key value | 仅当列表存在时,才向列表添加值 |
10 | LRANGE key start stop | 从列表中获取一系列元素 |
11 | LREM key count value | 从列表中删除元素 |
12 | LSET key index value | 通过索引在列表中设置元素的值 |
13 | LTRIM key start stop | 修剪列表的指定范围 |
14 | RPOP key | 删除并获取列表中的最后一个元素 |
15 | RPOPLPUSH source destination | 删除列表中的最后一个元素,将其附加到另一个列表并返回 |
16 | RPUSH key value1 [value2] | 将一个或多个值附加到列表 |
17 | RPUSHX key value | 仅当列表存在时才将值附加到列表 |
4、集合
Redis集合是字符串的无序集合。在Redis中,可以添加,删除和测试成员存在的时间O(1)复杂性。
集合的内部编码可以是intset或hashtable。
示例:
sadd yiibailist redis sadd yiibailist mongodbsadd yiibailist sqlitesadd yiibailist sqlitesmembers yiibailist1) "sqlite" 2) "mongodb" 3) "redis"
一个集合中的最大成员数量为2^32 - 1
(即4294967295
,每个集合中元素数量可达40
亿个)个。
序号 | 命令 | 说明 |
---|---|---|
1 | SADD key member1 [member2] | 将一个或多个成员添加到集合 |
2 | SCARD key | 获取集合中的成员数 |
3 | SDIFF key1 [key2] | 减去多个集合 |
4 | SDIFFSTORE destination key1 [key2] | 减去多个集并将结果集存储在键中 |
5 | SINTER key1 [key2] | 相交多个集合 |
6 | SINTERSTORE destination key1 [key2] | 交叉多个集合并将结果集存储在键中 |
7 | SISMEMBER key member | 判断确定给定值是否是集合的成员 |
8 | SMOVE source destination member | 将成员从一个集合移动到另一个集合 |
9 | SPOP key | 从集合中删除并返回随机成员 |
10 | SRANDMEMBER key [count] | 从集合中获取一个或多个随机成员 |
11 | SREM key member1 [member2] | 从集合中删除一个或多个成员 |
12 | SUNION key1 [key2] | 添加多个集合 |
13 | SUNIONSTORE destination key1 [key2] | 添加多个集并将结果集存储在键中 |
14 | SSCAN key cursor [MATCH pattern] [COUNT count] | 递增地迭代集合中的元素 |
5、可排序集合
Redis可排序集合类似于Redis集合,是不重复的字符集合。 不同之处在于,排序集合的每个成员都与分数相关联,这个分数用于按最小分数到最大分数来排序的排序集合。虽然成员是唯一的,但分数值可以重复。
有序集合的内部编码可以是ziplist或skiplist。
示例:
zadd yiibaiset 0 rediszadd yiibaiset 0 mongodbzadd yiibaiset 1 sqlitezadd yiibaiset 1 sqliteZRANGEBYSCORE yiibaiset 0 1000 1) "mongodb" 2) "redis" 3) "sqlite"
因为 ‘sqlite
‘ 的排序值是 1 ,其它两个元素的排序值是 0 ,所以 ‘sqlite
‘ 排在最后一个位置上。
Redis命令
Redis命令是用于在Redis服务器上执行一些操作。
要在Redis远程服务器上运行命令,需要通过客户端redis-cli
连接到服务器:
语法:
redis-cli -h host -p port -a password
示例:
以下示例显示如何连接到Redis远程服务器,在主机(host)127.0.0.1
,端口(port)6379
上运行,并使用密码为 mypass
。
redis-cli -h 127.0.0.1 -p 6379 -a "mypass"
Redis键命令用于管理Redis中的键,以下是使用redis键命令的语法。
语法:
COMMAND KEY_NAME
示例:
SET akey redisOKDEL akey(integer) 1GET akey(nil)
在上面的例子中,DEL
是Redis的命令,而akey
是键的名称。如果键被删除,则命令的输出将为(integer) 1
,否则为(integer) 0
。
下表列出了与键相关的一些基本命令。
序号 | 命令 | 描述 |
---|---|---|
1 | DEL key | 此命令删除一个指定键(如果存在)。 |
2 | DUMP key | 此命令返回存储在指定键的值的序列化版本。 |
3 | EXISTS key | 此命令检查键是否存在。 |
4 | EXPIREAT key timestamp | 设置在指定时间戳之后键到期/过期。这里的时间是Unix时间戳格式。 |
5 | EXPIRE key seconds | 设置键在指定时间秒数之后到期/过期。 |
6 | PEXPIRE key milliseconds | 设置键的到期时间(以毫秒为单位)。 |
7 | PEXPIREAT key milliseconds-timestamp | 以Unix时间戳形式来设置键的到期时间(以毫秒为单位)。 |
8 | KEYS pattern | 查找与指定模式匹配的所有键。 |
9 | MOVE key db | 将键移动到另一个数据库。 |
10 | PERSIST key | 删除指定键的过期时间,得永生。 |
11 | PTTL key | 获取键的剩余到期时间。 |
12 | RANDOMKEY | 从Redis返回一个随机的键。 |
13 | RENAME key newkey | 更改键的名称。 |
14 | RENAMENX key newkey | 如果新键不存在,重命名键。 |
15 | TYPE key | 返回存储在键中的值的数据类型。 |
Redis发布订阅
Redis发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。
Redis 发布订阅(pub/sub)实现了消息系统,发送者(在redis术语中称为发布者)在接收者(订阅者)接收消息时发送消息。传送消息的链路称为信道。
在Redis中,客户端可以订阅任意数量的信道。
示例:
以下示例说明了发布用户概念的工作原理。 在以下示例中,一个客户端订阅名为“redisChat
”的信道。
SUBSCRIBE redisChat
现在,两个客户端在名称为“redisChat
”的相同信道上发布消息,并且上述订阅的客户端接收消息。
PUBLISH redisChat "Redis is a great caching technique"PUBLISH redisChat "Learn redis by yiibai"
下表列出了与Redis发布订阅相关的一些基本命令:
序号 | 命令 | 说明 |
---|---|---|
1 | PSUBSCRIBE pattern [pattern …] | 订阅一个或多个符合给定模式的频道 |
2 | PUBSUB subcommand [argument [argument …]] | 查看订阅与发布系统状态 |
3 | PUBLISH channel message | 将信息发送到指定的频道 |
4 | PUNSUBSCRIBE [pattern [pattern …]] | 退订所有给定模式的频道 |
5 | SUBSCRIBE channel [channel …] | 订阅给定的一个或多个频道的信息 |
6 | UNSUBSCRIBE [channel [channel …]] | 退订给定的频道 |
Redis的发布/订阅功能类似于传统的消息路由机制,发布者发布消息,订阅者接收消息,而连接发布者和订阅者之间的桥梁是订阅的Channel或Pattern。订阅者和发布者之间的关系是松耦合的,发布者不指定哪个订阅者才能接收消息,订阅者也不只接受特定发布者的消息。
示例:
> publish channel one0> subscribe channel one2
Redis事务
Redis事务允许在单个步骤中执行一组命令,以下是事务的两个属性:
- 事务中的所有命令作为单个隔离操作并按顺序执行。不可以在执行Redis事务的中间向另一个客户端发出的请求。
- Redis事务也是原子的。原子意味着要么处理所有命令,要么都不处理。
语法:
Redis事务由命令MULTI
启动,然后需要传递一个应该在事务中执行的命令列表,然后整个事务由EXEC
命令执行。
redis 127.0.0.1:6379> MULTI OK List of commands here redis 127.0.0.1:6379> EXEC
下表列出了与Redis事务相关的一些基本命令:
序号 | 命令 | 说明 |
---|---|---|
1 | DISCARD | 丢弃在MULTI之后发出的所有命令 |
2 | EXEC | 执行MULTI后发出的所有命令 |
3 | MULTI | 标记事务块的开始 |
4 | UNWATCH | 取消 WATCH 命令对所有 key 的监视 |
5 | WATCH key [key …] | 监视给定的键以确定MULTI / EXEC块的执行 |
Redis脚本
Redis脚本使用Lua解释器来执行。从Redis 2.6.0
版开始内置到Redis中,使用脚本的命令是EVAL
。
1、EVAL命令
EVAL script numkeys key [key ...] arg [arg ...]
参数说明:
- script:脚本内容或者脚本地址;
- numkeys:脚本中用到的key的数量,接下来的numkeys个数会作为key的参数,剩下的作为arg的参数;
- arg:其他参数,会被存入脚本环境中的ARGV数组中,角标从1开始;
使用示例:
EVAL "return 'hello world'" 0
2、SCRIPT LOAD命令
SCRIPT LOAD script
将一个脚本编译并且缓存起来,生成一个SHA1值并且返回,为了方便使用,参数script就是脚本内容货地址。
使用示例:
script load "return 'hello word'"
3、EVALSHA命令
EVALSHA sha numkeys key [key ...] arg [arg ..]
与EVAL类似,执行一段脚本,区别是通过脚本的sha1值执行,去脚本缓存中查询,然后执行,参数说明:
sha1:就是脚本对应的sha1值;
4、 Lua调用Redis指令
redis.call(“命令名称”,参数1,参数2):执行redis命令,执行遇到错误会直接返回;
redis.pcall(“命令名称”,参数1,参数2):执行redis命令,遇到错误时,错误会以Lua脚本的方式返回;
Redis内存
Redis的内存占用主要可划分为以下几个部分:
- 数据,会被统计在used_memory中;
- 进程本身运行需要占用的内存,不由jemalloc分配,不会被统计在used_memory中;
- 缓冲内存(包括客户端缓冲区、复制积压缓冲区、AOF缓冲区等),由jemalloc分配,会被统计在used_memory中;
- 内存碎片(是Redis在分配、回收物理内存的过程中产生的),不会被统计在used_memory中;
1、Redis内存查看
info命令可以显示Redis服务器的基本信息,如CPU、内存、持久化、客户端链接等信息。
命令:
info memory# Memoryused_memory:689376 # 分配的内存总量,包括虚拟内存used_memory_human:673.22Kused_memory_rss:652480 # redis进程占用操作系统的内存,不包括虚拟内存used_memory_rss_human:637.19Kused_memory_peak:690160used_memory_peak_human:673.98Ktotal_system_memory:0total_system_memory_human:0Bused_memory_lua:37888used_memory_lua_human:37.00Kmaxmemory:0maxmemory_human:0Bmaxmemory_policy:noevictionmem_fragmentation_ratio:0.95 # 内存碎片比率mem_allocator:jemalloc-3.6.0 # 内存分配器,默认使用jemalloc
2、Redis内存划分
优化内存占用:
- 利用jemalloc特性进行优化;
- 使用整型/长整型;
- 共享对象;
- 避免过度设计;
关注内存碎片率:
内存碎片率是一个非常重要的参数,对Redis内存的优化有很重要的意义。
如果内存碎片率过高(jemalloc在1.03左右比较正常),说明内存碎片过多,内存浪费较为严重,这时可以考虑重启Redis服务,在内存中对数据进行重排,以减少内存碎片。
如果内存碎片率小于1,说明Redis内存不足,部分数据使用了虚拟内存(swap),但由于虚拟内存的存取速度比物理内存要差很多(2-3个数量级),此时Redis的访问速度会变慢很多,因此必须考虑增大物理内存(增加服务器节点数量或提高单机内存),或减少Redis中的数据(选用合适的数据类型、利用共享对象或设置合理的数据回收策略等)。
3、Redis数据存储细节
4、Redis内存回收机制
过期策略:删除过期的key值。
淘汰策略:当内存使用达到maxmemory上限时,触发LRU(Least Recently Used,最近最少使用)淘汰清理内存数据。
Redis持久化
数据持久化指的是为了防止因为断电等因素而导致数据丢失,将数据保存到磁盘上,当服务器重启时,可以通过读取磁盘来恢复数据。
Redis支持两种持久化方式,Snapshotting(快照,默认方式)和Append only file(aof)。
1、 RDB快照方式
1.1 简介
RDB(Redis Database Backup file,Redis数据备份文件):
- RDB 文件是一个经过压缩的二进制文件(默认:dump.rdb),保存在磁盘中;
- 通过保存数据库中的键值对来记录数据库状态。
1.2 创建
当 Redis 持久化时,程序会将当前内存中的数据库状态保存到磁盘中。创建 RDB 文件主要有两个 Redis 命令:SAVE 和 BGSAVE。
SAVE是同步操作,执行命令时,会阻塞 Redis 服务器进程,拒绝客户端发送的命令请求。
BGSAVE是异步操作,执行命令时,子进程执行保存工作,服务器还可以继续让主线程处理客户端发送的命令请求。
1.3 载入
载入工作在服务器启动时自动执行。
服务器在载入 RDB 文件期间,会一直处于阻塞状态,直到载入工作完成为止。
1.4 主要设置
Redis 允许用户通过设置服务器配置的 save 选项,让服务器每隔一段时间自动执行一次 BGSAVE 命令。
1.4.1 设置保存条件
配置命令如下:
save 900 1save 300 10
在这种情况下,只要满足以下条件中的一个,BGSAVE 命令就会被执行:
- 服务器在 900 秒之内,对数据库进行了至少 1 次修改了;
- 服务器在 300 秒之内,对数据库进行了至少 10 次修改。
1.4.2 saveparams
服务器程序会根据 save 选项所设置的保存条件,设置服务器状态 redisServer 结构的 saveparams
属性。
-
saveparams
属性是一个数组; -
数组中的每一个元素都是一个
saveparam
结构; -
每个
saveparam
结构都保存了一个save
选项设置的保存条件。struct saveparam { // 秒数 time_t seconds; // 修改数 int changes;}
1.4.3 dirty
dirty 计数器记录距离上一次成功执行 SAVE 命令或 BGSAVE 命令之后,服务器对数据库状态进行了多少次修改(包括写入、删除、更新等操作)。
1.4.4 lastsave
UNINX
时间戳,记录了服务器上一次成功执行 SAVE 命令或者 BGSAVE 命令的时间。
1.5 检查保存条件是否满足
服务器周期性操作函数 serverCron
(该函数对正在运行的服务器进行维护)默认每隔 100 毫秒就会执行一次,其中一项工作就是检查 save 选项所设置的保存条件是否已经满足,满足的话就执行BGSAVE
命令。
1.6 默认配置
RDB文件的默认配置如下:
################################ SNAPSHOTTING ################################## Save the DB on disk:#在给定的秒数和给定的对数据库的写操作数下,自动持久化操作。# save <seconds> <changes># save 900 1save 300 10save 60 10000#bgsave发生错误时是否停止写入,一般为yesstop-writes-on-bgsave-error yes#持久化时是否使用LZF压缩字符串对象?rdbcompression yes#是否对rdb文件进行校验和检验,通常为yesrdbchecksum yes# RDB持久化文件名dbfilename dump.rdb#持久化文件存储目录dir ./
2、AOF持久化方式
2.1 简介
AOF全称为 Append Only File(追加日志文件)。日志是写后日志,Redis 是先执行命令,把数据写入内存,然后才记录日志。
- 通过保存 Redis 服务器所执行的写命令来记录数据库状态;
- 写入 AOF 文件的所有命令都是以 Redis 的命令请求协议格式保存的。
2.2 实现
AOF 持久化流程实现主要是通过以下流程来实现的:
2.3 命令追加
若 AOF 持久化功能处于打开状态,服务器在执行完一个命令后,会以协议格式将被执行的写命令追加到服务器状态的 aof_buf 缓冲区的末尾。
2.4 文件同步
服务器每次结束一个事件循环之前,都会调用 flushAppendOnlyFile
函数,这个函数会考虑是否需要将 aof_buf 缓冲区中的内容写入和保存到 AOF 文件里。
flushAppendOnlyFile
函数执行以下流程:
- WRITE:根据条件,将 aof_buf 中的缓存写入到 AOF 文件;
- SAVE:根据条件,调用 fsync 或 fdatasync 函数,将 AOF 文件保存到磁盘中。
2.5 Always
每条命令都会 fsync 到硬盘中,这样 redis 的写入数据就不会丢失。
2.6 everysec
每秒都会刷新缓冲区到硬盘中(默认值)
2.7 no
根据当前操作系统的规则决定什么时候刷新到硬盘中,不需要我们来考虑。
2.8 数据加载
大致过程:
- 创建一个不带网络连接的伪客户端;
- 从 AOF 文件中分析并读取出一条写命令;
- 使用伪客户端执行被读出的写命令;
- 一直执行步骤 2 和 3,直到 AOF 文件中的所有写命令都被处理完毕为止。
2.9 文件重写
为何需要文件重写:
- 为了解决 AOF 文件体积膨胀的问题;
- 通过重写创建一个新的 AOF 文件来替代现有的 AOF 文件,新的 AOF 文件不会包含任何浪费空间的冗余命令。
实现原理:
- 不需要对现有的 AOF 文件进行任何操作;
- 从数据库中直接读取键现在的值;
- 用一条命令记录键值对,从而代替之前记录这个键值对的多条命令。
2.10 后台重写
为不阻塞父进程,Redis 将 AOF 重写程序放到子进程里执行。 在子进程执行 AOF 重写期间,服务器进程需要执行三个流程:
- 执行客户端发来的命令;
- 将执行后的写命令追加到 AOF 缓冲区;
- 将执行后的写命令追加到 AOF 重写缓冲区。
2.11 默认配置
AOF 文件的默认配置如下:
############################## APPEND ONLY MODE ################################开启AOF持久化方式appendonly no#AOF持久化文件名appendfilename "appendonly.aof"#每秒把缓冲区的数据fsync到磁盘appendfsync everysec# appendfsync no#是否在执行重写时不同步数据到AOF文件no-appendfsync-on-rewrite no# 触发AOF文件执行重写的增长率auto-aof-rewrite-percentage 100#触发AOF文件执行重写的最小sizeauto-aof-rewrite-min-size 64mb#redis在恢复时,会忽略最后一条可能存在问题的指令aof-load-truncated yes#是否打开混合开关aof-use-rdb-preamble yes
3、如何选择持久化方式
- 对于大中型的应用,我们既想保证数据完整性又想保证高效率,就应该结合使用 RDB 和 AOF 两种方式;
- 如果只需要保证数据的完整性,保护数据不会丢失,那么优先使用 AOF 方式;
- 如果是处理大规模的数据恢复,追求更高更快的效率的话,优先使用 RDB 方式。
Redis集群
1、概述
2、主从模式
2.1 简介
简而言之:
- 主对外从对内,主可写从不可写;
- 主挂了,从不可为主。
Redis的主从复制机制没有动态选举Master节点的能力,主挂了服务就不可以再写数据。仅仅增加了应用读数据的并发量,同时做数据备份的能力。
**注意:**一般生产环境会采用 哨兵 或者 Redis Cluster 这种具备Master自动选举的方案。
优缺点
优点:
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离;
- 为了分担Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成;
- Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力;
- Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求;
- Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。
缺点:
- Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复;
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性;
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
2.2 配置方式
用户可以通过 SLAVEOF
命令或者配置的方式,让一个服务器去复制另一个服务器即成为它的从服务器。
主redis无需任何配置,从机需要修改redis.conf文件中如下配置项:
port 6378 # 如果是使用的一台机器注意端口要与主机不同# slaveof <masterip> <masterport># 表示当前【从服务器】对应的【主服务器】的IP是192.168.10.135,端口是6379。slaveof 192.168.137.6 6379
2.3 实现原理
Redis的从服务器在向主服务器发起同步时,一般会使用 SYNC
或 PSYNC
命令。
过程:
- 从服务器连接主服务器,发送SYNC命令;
- 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令;
- 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令;
- 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照;
- 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令;
- 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成)
- 主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)。
PSYNC命令具有完整同步(full resynchronization) 和 部分同步(partial resynchronization)两种模式:
- 完整同步用于处理初次复制情况:完整重同步的执行步骤是通过让主服务器创建并发送RDB文件,以及向从服务器发送保存在缓冲区里面的写命令来进行同步;
- 部分同步则用于处理断线后重复制情况:当从服务器在断线后重新连接主服务器时,如果条件允许,主服务器可以将主从服务器连接断开期间执行的写命令发送给从服务器,从服务器只要接收并执行这些写命令,就可以将数据库更新至主服务器当前所处的状态。
3、哨兵(Sentinel)模式
当主服务器中断服务后,可以将一个从服务器升级为主服务器,以便继续提供服务,但是这个过程需要人工手动来操作。 为此,Redis 2.8中提供了哨兵工具来实现自动化的系统监控和故障恢复功能。
哨兵的作用就是监控Redis系统的运行状况。它的功能包括以下两个:
- 监控主服务器和从服务器是否正常运行;
- 主服务器出现故障时自动将从服务器转换为主服务器。
3.1 工作方式
- 每个Sentinel(哨兵)进程以每秒钟一次的频率向整个集群中的Master主服务器,Slave从服务器以及其他Sentinel(哨兵)进程发送一个 PING 命令;
- 如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel(哨兵)进程标记为主观下线(SDOWN);
- 如果一个Master主服务器被标记为主观下线(SDOWN),则正在监视这个Master主服务器的所有 Sentinel(哨兵)进程要以每秒一次的频率确认Master主服务器的确进入了主观下线状态;
- 当有足够数量的 Sentinel(哨兵)进程(大于等于配置文件指定的值)在指定的时间范围内确认Master主服务器进入了主观下线状态(SDOWN), 则Master主服务器会被标记为客观下线(ODOWN);
- 在一般情况下, 每个 Sentinel(哨兵)进程会以每 10 秒一次的频率向集群中的所有Master主服务器、Slave从服务器发送 INFO 命令;
- 当Master主服务器被 Sentinel(哨兵)进程标记为客观下线(ODOWN)时,Sentinel(哨兵)进程向下线的 Master主服务器的所有 Slave从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次;
- 若没有足够数量的 Sentinel(哨兵)进程同意 Master主服务器下线, Master主服务器的客观下线状态就会被移除。若 Master主服务器重新向 Sentinel(哨兵)进程发送 PING 命令返回有效回复,Master主服务器的主观下线状态就会被移除。
3.2 优缺点
优点:
- 哨兵模式是基于主从模式的,所有主从的优点,哨兵模式都具有;
- 主从可以自动切换,系统更健壮,可用性更高。
缺点:
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
3.3 实现原理
3.3.1 Sentinel与主从服务器建立连接
Sentinel服务器启动之后便会创建于主服务器的命令连接,并订阅主服务器的sentinel:hello频道以创建订阅连接。
Sentinel默认会每10秒向主服务器发送 INFO 命令,主服务器则会返回主服务器本身的信息,以及其所有从服务器的信息。
根据返回的信息,Sentinel服务器如果发现有新的从服务器上线后也会像连接主服务器时一样,向从服务器同时创建命令连接与订阅连接。
3.3.2 判定主服务器是否下线
每一个Sentinel服务器每秒会向其连接的所有实例包括主服务器,从服务器,其他Sentinel服务器)发送 PING
命令,根据是否回复 PONG
命令来判断实例是否下线。
如果实例在收到 PING
命令的down-after-milliseconds毫秒内(根据配置),未有有效回复。则该实例将会被发起 PING
命令的Sentinel认定为主观下线。
当一台主服务器没某个Sentinel服务器判定为客观下线时,为了确保该主服务器是真的下线,Sentinel会向Sentinel集群中的其他的服务器确认,如果判定主服务器下线的Sentinel服务器达到一定数量时(一般是N/2+1),那么该主服务器将会被判定为客观下线,需要进行故障转移。
3.3.3 选举领头Sentinel
当有主服务器被判定客观下线后,Sentinel集群会选举出一个领头Sentinel服务器来对下线的主服务器进行故障转移操作。整个选举其实是基于RAFT一致性算法而实现的,大致的思路如下:
- 每个发现主服务器下线的Sentinel都会要求其他Sentinel将自己设置为局部领头Sentinel;
- 接收到的Sentinel可以同意或者拒绝;
- 如果有一个Sentinel得到了半数以上Sentinel的支持则在此次选举中成为领头Sentinel;
- 如果给定时间内没有选举出领头Sentinel,那么会再一段时间后重新开始选举,直到选举出领头Sentinel。
3.3.4 选举新的主服务器
领头服务器会从从服务中挑选出一个最合适的作为新的主服务器。挑选的规则是:
- 选择健康状态的从节点,排除掉断线的,最近没有回复过 INFO命令的从服务器;
- 选择优先级配置高的从服务器;
- 选择复制偏移量大的服务器(表示数据最全)。
挑选出新的主服务器后,领头服务器将会向新主服务器发送 SLAVEOF no one
命令将其真正升级为主服务器,并且修改其他从服务器的复制目标,将旧的主服务器设为从服务器,以此达到故障转移的目标。
4、Redis-Cluster集群
Redis的哨兵模式基本已经可以实现高可用,读写分离 ,但是在这种模式下每台Redis服务器都存储相同的数据,很浪费内存,所以在Redis3.0上加入了cluster模式,实现的Redis的分布式存储,也就是说每台Redis节点上存储不同的内容。
Redis-Cluster采用无中心结构,特点如下:
- 所有的Redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽;
- 节点的fail是通过集群中超过半数的节点检测失效时才生效;
- 客户端与Redis节点直连,不需要中间代理层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
4.1 工作方式
在Redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,Redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。
Redis缓存设计
缓存的主要用途有两个方面:高性能和高并发。
缓存的应用宽泛,可将用于数据高速交换的存储介质称之为缓存。
缓存在使用过程中不可避免会遇到一些问题,对于高频的问题可将其大概归为了7类:
1、缓存集中失效
当业务系统查询数据时,首先会查询缓存,如果缓存中数据不存在,然后查询DB再将数据预热到Cache中,并返回。缓存的性能比 DB 高 50~100 倍以上。
很多业务场景,如:秒杀商品、微博热搜排行、或者一些活动数据,都是通过跑任务方式,将DB数据批量、集中预热到缓存中,缓存数据有着近乎相同的过期时间。当过这批数据过期时,会一起过期,此时,对这批数据的所有请求,都会出现缓存失效,从而将压力转嫁到DB,DB的请求量激增,压力变大,响应开始变慢。
解决方案:
从缓存的过期时间入口,将原来的固定过期时间,调整为过期时间=基础时间+随机时间,让缓存慢慢过期,避免瞬间全部过期,对DB产生过大压力。
2、缓存穿透
不是所有的请求都能查到数据,不论是从缓存中还是DB中。假如黑客攻击了一个论坛,用了一堆肉鸡访问一个不存的帖子id
。按照常规思路,每次都会先查缓存,缓存中没有,接着又查DB,同样也没有,此时不会预热到Cache中,导致每次查询,都会cache miss
。
由于DB的吞吐性能较差,会严重影响系统的性能,甚至影响正常用户的访问。
解决方案:
方案一:查存DB 时,如果数据不存在,预热一个特殊空值
到缓存中。这样,后续查询都会命中缓存,但是要对特殊值,解析处理。
方案二:构造一个BloomFilter
过滤器,初始化全量数据,当接到请求时,在BloomFilter
中判断这个key是否存在,如果不存在,直接返回即可,无需再查询缓存和DB
3、缓存雪崩
缓存雪崩是指部分缓存节点不可用,进而导致整个缓存体系甚至服务系统不可用的情况。
分布式缓存设计一般选择一致性Hash
,当有部分节点异常时,采用 rehash
策略,即把异常节点请求平均分散到其他缓存节点。但是,当较大的流量洪峰到来时,如果大流量 key 比较集中,正好在某 1~2 个缓存节点,很容易将这些缓存节点的内存、网卡过载,缓存节点异常 Crash,然后这些异常节点下线,这些大流量 key 请求又被 rehash 到其他缓存节点,进而导致其他缓存节点也被过载 Crash,缓存异常持续扩散,最终导致整个缓存体系异常,无法对外提供服务。
解决方案:
方案一:增加实时监控,及时预警。通过机器替换、各种故障自动转移策略,快速恢复缓存对外的服务能力;
方案二:缓存增加多个副本,当缓存异常时,再读取其他缓存副本。为了保证副本的可用性,尽量将多个缓存副本部署在不同机架上,降低风险。
解决方法:
- 缓存均匀过期
- 加互斥锁
- 缓存永不过期
4、缓存热点
对于突发事件,大量用户同时去访问热点信息,这个突发热点信息所在的缓存节点就很容易出现过载和卡顿现象,甚至 Crash
,我们称之为缓存热点。
这个在新浪微博经常遇到,某大V明星出轨、结婚、离婚,瞬间引发数百千万的吃瓜群众围观,访问同一个key,流量集中打在一个缓存节点机器,很容易打爆网卡、带宽、CPU的上限,最终导致缓存不可用。
解决方案:
- 首先能先找到这个
热key
来,比如通过Spark
实时流分析,及时发现新的热点key
; - 将集中化流量打散,避免一个缓存节点过载。由于只有一个key,我们可以在key的后面拼上
有序编号
,比如key#01
、key#02
…key#10
多个副本,这些加工后的key位于多个缓存节点上; - 每次请求时,客户端随机访问一个即可;
可以设计一个缓存服务治理管理后台,实时监控缓存的SLA,并打通分布式配置中心,对于一些hot key
可以快速、动态扩容。
5、缓存大Key
当访问缓存时,如果key对应的value过大,读写、加载很容易超时,容易引发网络拥堵。另外缓存的字段较多时,每个字段的变更都会引发缓存数据的变更,频繁的读写,导致慢查询。如果大key过期被缓存淘汰失效,预热数据要花费较多的时间,也会导致慢查询。
所以在设计缓存时,要注意缓存的粒度
,既不能过大,如果过大,容易导致网络拥堵;也不能过小,如果太小,查询频率会很高,每次请求都要查询多次。
解决方案:
方案一:设置一个阈值,当value的长度超过阈值时,对内容启动压缩,降低kv的大小;
方案二:评估大key
所占的比例,由于很多框架采用池化技术
,如:Memcache,可以预先分配大对象空间。真正业务请求时,直接拿来即用;
方案三:颗粒划分,将大key拆分为多个小key,独立维护,成本会降低不少;
方案四:大key要设置合理的过期时间,尽量不淘汰那些大key;
6、缓存数据一致性
缓存是用来加速的,一般不会持久化储存。所以,一份数据通常会存在DB
和缓存
中,由此会带来一个问题,如何保证这两者的数据一致性。另外,缓存热点问题会引入多个副本备份,也可能会发生不一致现象。
解决方案:
方案一:当缓存更新失败后,进行重试,如果重试失败,将失败的key写入MQ消息队列,通过异步任务补偿缓存,保证数据的一致性;
方案二:设置一个较短的过期时间,通过自修复的方式,在缓存过期后,缓存重新加载最新的数据;
7、数据并发竞争预热
互联网系统典型的特点就是流量大,一旦缓存中的数据过期、或因某些原因被删除等,导致缓存中的数据为空,大量的并发线程请求(查询同一个key)就会一起并发查询数据库
,数据库的压力陡然增加。
如果请求量非常大,全部压在数据库,可能把数据库压垮,进而导致整个系统的服务不可用。
解决方案:
方案一:
引入一把全局锁
,当缓存未命中时,先尝试获取全局锁,如果拿到锁,才有资格去查询DB
,并将数据预热到缓存中。虽然,client端发起的请求非常多,但是由于拿不到锁,只能处于等待状态,当缓存中的数据预热成功后,再从缓存中获取;
方案二:缓存数据创建多个备份,当一个过期失效后,可以访问其他备份;
Redis分布式锁
Java连接Redis
1、POM依赖
<dependency> <groupId>com.suning.framework</groupId> <artifactId>snf-redis-client</artifactId> <version>2.5.1</version> <exclusions> <exclusion> <groupId>com.suning.framework</groupId> <artifactId>snf-scm-client</artifactId> </exclusion> <exclusion> <groupId>com.suning.framework</groupId> <artifactId>snf-sedis</artifactId> </exclusion> </exclusions></dependency><dependency> <groupId>com.suning.framework</groupId> <artifactId>snf-sedis</artifactId> <version>1.4.1</version> <exclusions> <exclusion> <groupId>com.suning.framework</groupId> <artifactId>snf-statistics</artifactId> </exclusion> </exclusions></dependency>
2、Bean注入
3、使用示例
参考
1、Redis概述
- Redis 架构原理及应用实践
- 深度剖析Redis底层数据结构 (qq.com)
- 看完后,我才明白 Redis 为什么默认 16 个数据库? (qq.com)
- 来,手撸一个简版 Redis (qq.com)
- 学会这几个 Redis 技巧,让你的程序快如闪电! (qq.com)
- 你知道Redis都有哪些监控指标吗? (qq.com)
- Redis 如何简化实现微服务的设计模式? (qq.com)
2、Redis安装与配置
3、Redis数据类型
4、Redis命令
5、Redis发布订阅
6、Redis事务
7、Redis脚本
10、Redis内存
11、Redis持久化
12、Redis集群
13、Redis缓存设计
- 经理让我复盘上次Redis缓存雪崩事故 (qq.com)
- 面试官:你对Redis缓存了解吗?面对这11道面试题你是否有很多问号? (qq.com)
- 为什么 Redis 要比 Memcached 更火? (qq.com)
- 4 种数据库缓存最终一致性的优缺点对比?最终选择方案四! (qq.com)
- Nginx+Redis 搭建高性能缓存利器 (qq.com)
- 8 张图 | 剖析 Redis 与MySQL 一致性的爱恨情仇 (qq.com)
14、Redis分布式锁
- 聊聊redis分布式锁的8大坑
- Redis分布式锁,你用对了吗?
- Redis分布式锁
- 聊聊分布式锁——Redis和Redisson的方式 (qq.com)
- 分布式锁用 Redis 还是 Zookeeper? (qq.com)
- 聊聊Redis分布式锁 (qq.com)
- SpringBoot + Redis:模拟 10w 人的秒杀抢单!
15、Java连接Redis
- Java开发中对Redis的基本操作总结_lixiaoxiong的专栏-CSDN博客
- Java 使用 Redis | 菜鸟教程 (runoob.com)
- Spring和Redis整合详解_feiyangtianyao的专栏-CSDN博客
- Jedis下的ShardedJedis - 南望孤笑 - 博客园 (cnblogs.com)
- Spring和Redis整合详解_feiyangtianyao的专栏-CSDN博客
- Spring整合Redis(spring-data-redis) - phil_jing - 博客园 (cnblogs.com)
- Error creating bean with name ‘redisConnectionFactory‘ defined in class path resource_阿来小同学的博客-CSDN博客
- SpringBoot+Redis分布式锁:模拟抢单 (qq.com)
- 给你一个亿的keys,Redis如何统计? (qq.com)
- Spring Boot + Redis 实现各种操作,写得太好了吧! (qq.com)
- 天猫二面:内存耗尽后 Redis 会发生什么? (qq.com)
- Redis 的这些拓展方案 (qq.com)
- Redis 新特性篇:多线程模型解读 (qq.com)
- Redis 实战篇:通过 Geo 类型实现附近的人邂逅女神 (qq.com)
- 每天一道面试题:Redis的淘汰策略有哪几种? (qq.com)
- 分享一个基于 Jedis 的 Redis 工具类 (qq.com)
- 面试官:生成订单30分钟未支付,则自动取消,该怎么实现? (qq.com)
- 多个项目共用同一个redis_优化 Redis 的使用策略
- Redis 的这些拓展方案,用过一条的就是大牛! (qq.com)
- 拼多多面试:如何用 Redis 统计独立用户访问量? (qq.com)
- Redis 如何简化实现微服务的设计模式? (qq.com)