Redis入门 Redis 概述
Redis 是什么?
Redis(Remote Dictionary Server ),即远程字典服务 !
是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
免费和开源!是当下最热门的 NoSQL 技术之一!也被人们称之为结构化数据库!
Redis 能干嘛?
1、内存存储、持久化,内存中是断电即失、所以说持久化很重要(rdb、aof) 2、效率高,可以用于高速缓存 3、发布订阅系统 4、地图信息分析 5、计时器、计数器(浏览量!) 6、……..
Redis有什么特性?
1、多样的数据类型 2、持久化 3、集群 4、事务 5、……
Redis官方
1、官网:https://redis.io/
2、中文网:http://www.redis.cn/
Redis的数据回收策略 volatile-lru: 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰;
volatile-ttl: 从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰;
volatile-random: 从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰;
allkeys-lru: 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰;
allkeys-random: 从数据集(server.db[i].dict)中任意选择数据淘汰;
no-enviction: (驱逐)禁止驱逐数据;
Redis 应用部署
1、下载安装包:redis-5.0.8.tar.gz
2、解压Redis的安装包 至 /usr/local/src目录下
3、进入解压后的文件,可以看到我们redis的配置文件
4、编译 Redis 源码
1 2 3 [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8]
5、创建redis安装目录
1 2 3 4 5 6 7 8 9 10 11 [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] 总用量 0 drwxr-xr-x 2 root root 6 6月 20 11:58 bin drwxr-xr-x 2 root root 6 6月 20 11:58 conf drwxr-xr-x 2 root root 6 6月 20 11:58 log drwxr-xr-x 2 root root 6 6月 20 11:57 pid
6、拷贝Redis相关文件至此目录中
1 2 3 4 5 6 7 [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8] [root@redis01 redis-5.0.8]
文件
作用
redis.conf
redis配置文件
redis-server
redis服务端程序
redis-cli
redis客户端程序
redis-benchmark
用于进行测试redis性能测试的工具
redis-check-rdb
用于修复出现问题的dump.rdb文件
redis-check-aof
用于修复出现问题的aof文件
redis-sentinel
用于哨兵集群管理工具
7、配置Redis命令的软连接
8、修改Redis配置文件
1 2 3 4 5 6 7 8 [root@redis01 ~] 69 bind 192.168.2.1 88 protected-mode no 136 daemonize yes 158 pidfile "/usr/local/redis/pid/redis_6379.pid" 171 logfile "/usr/local/redis/log/redis_6379.log" 263 dir "/usr/local/redis/" 507 requirepass 123456
9、Redis配置文件详解
配置
作用
daemonize yes
以后台daemon方式运行redis
pidfile “redis.pid”
默认pid文件路径
port 6379
默认端口
bind 127.0.0.1
默认绑定本机所有ip地址,为了安全,可以只监听内网ip
timeout 300
客户端超时设置,单位为秒
loglevel verbose
设置日志级别,支持四个级别:debug、notice、verbose、warning
logfile stdout
日志记录方式,默认为标准输出,logs不写文件,输出到空设备/deb/null
logfile “redis.log”
可以指定日志文件路径
databases 16
开启数据库的数量
save 900 1
创建本地数据库快照;900秒内,执行1次写操作后触发快照
save 300 10
创建本地数据库快照;300秒内,执行10次写操作
save 60 10000
创建本地数据库快照;60秒内,执行10000次写操作
rdbcompression yes
启用数据库lzf压缩,也可以设置为no
dbfilename dump.rdb
本地快照数据库名称
dir “/usr/local/redis”
本地快照数据库存放目录(默认在启动redis的路径)
requirepass 123456
设置redis数据库连接密码
appendfsync everysec
设置日志同步的频率, 每秒执行同步, 还有两个参数always、no一般设置为everysec, 相当于MySQL事物日志的写方式
Slaveof
设置数据库为其他数据库的从数据库
Masterauth
主数据库连接需要的密码验证
vm-enabled
是否开启虚拟内存支持 (vm开头的参数都是配置虚拟内存的)
vm-swap-file
设置虚拟内存的交换文件路径
vm-max-memory
设置redis使用的最大物理内存大小
vm-page-size
设置虚拟内存的页大小
vm-pages
设置交换文件的总的page数量
vm-max-threads
设置使用swap存储同时使用的线程数量,通常设置值为核心数相同,如果设置为0,则会以串行方式,对数据的完整性有着极大的保证
Glueoutputbuf
把小的输出缓存存放在一起
hash-max-zipmap-entries
设置hash的临界值
Activerehashing
重新hash
10、编写启动脚本并启动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 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [root@redis01 ~] REDISPORT=6379 EXEC=/usr/local /redis/bin/redis-server REDIS_CLI=/usr/local /redis/bin/redis-cli PIDFILE=/usr/local /redis/pid/redis_6379.pid CONF="/usr/local/redis/conf/redis.conf" AUTH="123456" LISTEN_IP=$(netstat -utpln |grep redis-server |awk '{print $4}' |awk -F':' '{print $1}' ) case "$1 " in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" else echo "Starting Redis server..." $EXEC $CONF fi if [ "$?" ="0" ] then echo "Redis is running..." fi ;; stop) if [ ! -f $PIDFILE ] then echo "$PIDFILE does not exist, process is not running" else PID=$(cat $PIDFILE ) echo "Stopping ..." $REDIS_CLI -h $LISTEN_IP -p $REDISPORT -a $AUTH SHUTDOWN while [ -x ${PIDFILE} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" fi ;; restart|force-reload) ${0} stop ${0} start ;; *) echo "Usage: /etc/init.d/redis {start|stop|restart|force-reload}" >&2 exit 1 esac [root@redis01 ~] [root@redis01 ~] Starting Redis server... Redis is running... [root@redis01 ~] tcp 0 0 192.168.2.1:6379 0.0.0.0:* LISTEN 22704/redis-server
11、查看redis家目录生成的文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [root@redis01 ~] /usr/local /redis/ ├── bin │ ├── redis-benchmark │ ├── redis-check-aof │ ├── redis-check-rdb │ ├── redis-cli │ ├── redis-sentinel │ └── redis-server ├── conf │ └── redis.conf ├── dump.rdb ├── log │ └── redis_6379.log └── pid └── redis_6379.pid 4 directories, 10 files
12、使用redis-cli 连接Redis服务方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 [root@redis01 ~] Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe. 192.168.2.1:6379> keys * (empty list or set ) [root@redis01 ~] 192.168.2.1:6379> keys * (error) NOAUTH Authentication required. 192.168.2.1:6379> AUTH 123456 OK 192.168.2.1:6379> keys * (empty list or set ) 解决方法如下: 192.168.1.1:6379> CONFIG SET requirepass "123456" OK 192.168.1.1:6379> AUTH 123456 OK
配置文件详解
模块化,包含,类似于Nginx中的include
网络配置
1 2 3 port 6379 bind 127.0.0.1 protected-mode yes
SECURITY 安全配置
1 2 requirepass 123456 masterauth <master-password>
通用配置 GENERAL
1 2 3 4 5 6 daemonize yes pidfile "redis.pid" logfile "redis.log" loglevel notice databases 16 always-show-logo yes
rdb快照配置
1 2 3 4 5 6 7 save 900 1 save 300 10 save 60 10000 rdbcompression yes dbfilename dump.rdb dir ./ stop-writes-on-bgsave-error yes
AOF配置
1 2 3 4 5 appendonly no appendfilename "appendonly.aof" appendfsync always appendfsync everysec appendfsync no
限制 CLIENTS
1 2 3 4 5 6 7 8 9 10 maxclients 10000 timeout 300 maxmemory <bytes> maxmemory-policy noeviction 1、allkeys-random 随机删除 2、volatile-random 随机删除即将过期key 3、allkeys-lru 删除lru算法的key 4、volatile-ttl 删除即将过期的 5、noeviction 永不过期,返回错误 6、volatile-lru 只对设置了过期时间的key进行LRU(默认值)
REPLICATION 复制,集群配置
1 2 3 min-slaves-to-write 3 min-slaves-max-lag 10 slaveof IP PORT
虚拟内存配置
1 2 3 4 5 vm-enabled vm-swap-file vm-max-memory vm-page-size vm-pages
Redis 测试性能 redis-benchmark 是一个压力测试工具,官方自带的性能测试工具。
常用选项:
选项
描述
默认值
-h
指定服务器主机名
127.0.0.1
-p
指定服务器端口
6379
-s
指定服务器 socket
-c
指定并发连接数
50
-n
指定请求数
10000
-d
以字节的形式指定 SET/GET 值的数据大小
2
-k
1=keep alive 0=reconnect
1
-r
SET/GET/INCR 使用随机 key, SADD 使用随机值
-P
通过管道传输 请求
1
-q
强制退出 redis。仅显示 query/sec 值
–csv
以 CSV 格式输出
-l
生成循环,永久执行测试
-t
仅运行以逗号分隔的测试命令列表。
-I
Idle 模式。仅打开 N 个 idle 连接并等待。
简单测试下
1 2 redis-benchmark -h 192.168.2.1 -p 6379 -c 100 -n 100000
如何查看这些分析呢?
Redis 基础Key-Value
Redis 是单线程的!
明白Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了!所有就使用了单线程了!
Redis 是C 语言写的,官方提供的数据为 100000+ 的QPS,完全不比同样是使用 key-vale的Memecache差!
Redis 为什么单线程还这么快?
1、误区1:高性能的服务器一定是多线程的?
2、误区2:多线程(CPU上下文会切换!)一定比单线程效率高!
3、对CPU>内存>硬盘的速度要有所了解!
4、核心:redis 是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(CPU上下文会切换:耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案!
redis默认有16个数据库,默认使用的是第0个,可以使用 select 进行切换数据库!
后面如果遇到不会的命令,可以在官网查看帮助文档!
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 51 52 53 54 55 56 57 58 59 60 61 62 63 192.168.2.1:6379> select 3 OK 192.168.2.1:6379[3]> dbsize (integer ) 0 192.168.2.1:6379[3]> set name lemon OK 192.168.2.1:6379[3]> dbsize (integer ) 1 192.168.2.1:6379[3]> type name string 192.168.2.1:6379[3]> get name "lemon" 192.168.2.1:6379[3]> exists name (integer ) 1 192.168.2.1:6379[3]> exists name1 (integer ) 0 192.168.2.1:6379[3]> set AA aa OK 192.168.2.1:6379[3]> dbsize (integer ) 2 192.168.2.1:6379[3]> del AA (integer ) 1 192.168.2.1:6379[3]> dbsize (integer ) 1 192.168.2.1:6379[3]> select 1 OK 192.168.2.1:6379[1]> set age 20 OK 192.168.2.1:6379[1]> set height 1.8 OK 192.168.2.1:6379[1]> randomkey "height" 192.168.2.1:6379[1]> randomkey "age" 192.168.2.1:6379[1]> keys * 1) "height" 2) "age" 192.168.2.1:6379[1]> keys h* 1) "height" 192.168.2.1:6379[1]> expire age 10 (integer ) 1 192.168.2.1:6379[1]> ttl age (integer ) 3 192.168.2.1:6379[1]> ttl age (integer ) 0 192.168.2.1:6379[1]> ttl age (integer ) -2 192.168.2.1:6379[1]> keys * 1) "height" 192.168.2.1:6379[1]> rename height AA OK 192.168.2.1:6379[1]> keys * 1) "AA" 192.168.2.1:6379[1]> flushdb OK 192.168.2.1:6379[1]> keys * (empty list or set ) 192.168.2.1:6379[1]> flushall OK 192.168.2.1:6379[1]> select 3 OK 192.168.2.1:6379[3]> keys * (empty list or set )
Redis中操作之其他操作 1 2 3 4 5 6 7 8 192.168.2.1:6379> dbsize 192.168.2.1:6379> flushdb 192.168.2.1:6379> flushall 192.168.2.1:6379> save 192.168.2.1:6379> bgsave 192.168.2.1:6379> lastsave 192.168.2.1:6379> info 192.168.2.1:6379> slaveof
Redis五大数据类型
官网文档
String(字符串)
String是简单的 key-value 键值对,value 不仅可以是 String,也可以是数字;String在redis内部存储默认就是一个字符串,被redisObject所引用,当遇到incr,decr等操作时会转成数值型进行计算,此时redisObject的encoding字段为int。
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> set key1 v1 OK 192.168.2.1:6379> get key1 "v1" 192.168.2.1:6379> append key1 "hello" (integer ) 7 192.168.2.1:6379> get key1 "v1hello" 192.168.2.1:6379> strlen key1 (integer ) 7 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> set key2 0 OK 192.168.2.1:6379> get key2 "0" 192.168.2.1:6379> incr key2 (integer ) 1 192.168.2.1:6379> incr key2 (integer ) 2 192.168.2.1:6379> get key2 "2" 192.168.2.1:6379> decr key2 (integer ) 1 192.168.2.1:6379> decr key2 (integer ) 0 192.168.2.1:6379> get key2 "0" 192.168.2.1:6379> incrby key2 10 (integer ) 10 192.168.2.1:6379> incrby key2 10 (integer ) 20 192.168.2.1:6379> get key2 "20" 192.168.2.1:6379> decrby key2 10 (integer ) 10 192.168.2.1:6379> decrby key2 10 (integer ) 0 192.168.2.1:6379> get key2 "0" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> set key3 "hello,world" OK 192.168.2.1:6379> get key3 "hello,world" 192.168.2.1:6379> getrange key3 0 4 "hello" 192.168.2.1:6379> getrange key3 0 -1 "hello,world" 192.168.2.1:6379> set key4 abcdefg OK 192.168.2.1:6379> get key4 "abcdefg" 192.168.2.1:6379> setrange key4 1 xx (integer ) 7 192.168.2.1:6379> get key4 "axxdefg" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> setex key5 30 "hello" OK 192.168.2.1:6379> ttl key5 (integer ) 26 192.168.2.1:6379> get key5 "hello" 192.168.2.1:6379> setnx mykey "redis" (integer ) 1 192.168.2.1:6379> keys * 1) "mykey" 2) "key1" 3) "key5" 4) "key4" 5) "key3" 6) "key2" 192.168.2.1:6379> ttl key5 (integer ) -2 192.168.2.1:6379> keys * 1) "mykey" 2) "key1" 3) "key4" 4) "key3" 5) "key2" 192.168.2.1:6379> setnx mykey "MongoDB" (integer ) 0 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> mset k1 'v1' k2 'v2' k3 'v3' OK 192.168.2.1:6379> keys * 1) "k2" 2) "mykey" 3) "k1" 4) "key3" 5) "key1" 6) "key2" 7) "key4" 8) "k3" 192.168.2.1:6379> mget k1 k2 k3 1) "v1" 2) "v2" 3) "v3" -------------------------------------------------------------------------------------------------- getset 192.168.2.1:6379> getset db 'redis' (nil) 192.168.2.1:6379> get db "redis" 192.168.2.1:6379> getset db 'mongodb' "redis" 192.168.2.1:6379> get db "mongodb" --------------------------------------------------------------------------------------------------
List(列表)
概述: Redis列表是简单的字符串列表,可以类比到C++中的std::list,简单的说就是一个列表或者说是一个队列。可以从头部或尾部向Redis列表添加元素。列表的最大长度为2^32 - 1,也即每个列表支持超过40亿个元素。Redis list的实现为一个双向列表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
场景: Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如twitter的关注列表、粉丝列表等都可以用Redis的list结构来实现,再比如有的应用使用Redis的list类型实现一个简单的轻量级消息队列,生产者push,消费者pop/bpop。
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> rpush list1 1 (integer ) 1 192.168.2.1:6379> rpush list1 2 (integer ) 2 192.168.1.1:6379> lrange list1 0 -1 1) "1" 2) "2" 192.168.2.1:6379> lpush list1 one (integer ) 3 192.168.2.1:6379> lpush list1 two (integer ) 4 192.168.2.1:6379> lrange list1 0 -1 1) "two" 2) "one" 3) "1" 4) "2" 192.168.2.1:6379> lrange list1 1 2 1) "one" 2) "1" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> lrange list1 0 -1 1) "two" 2) "one" 3) "1" 4) "2" 192.168.2.1:6379> lpop list1 "two" 192.168.2.1:6379> rpop list1 "2" 192.168.2.1:6379> lrange list1 0 -1 1) "one" 2) "1" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> lrange list1 0 -1 1) "one" 2) "1" 192.168.2.1:6379> lindex list1 1 "1" 192.168.2.1:6379> lindex list1 0 "one" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> lpush list2 one two three three (integer ) 4 192.168.2.1:6379> llen list2 (integer ) 4 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> lrange list2 0 -1 1) "three" 2) "three" 3) "two" 4) "one" 192.168.2.1:6379> lrem list2 1 one (integer ) 1 192.168.2.1:6379> lrange list2 0 -1 1) "three" 2) "three" 3) "two" 192.168.2.1:6379> lrem list2 2 three (integer ) 2 192.168.2.1:6379> lrange list2 0 -1 1) "two" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> rpush mylist 'hello-1' 'hello-2' 'hello-3' 'hello-4' (integer ) 4 192.168.2.1:6379> ltrim mylist 1 2 OK 192.168.2.1:6379> lrange mylist 0 -1 1) "hello-2" 2) "hello-3" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> rpush mylist "hello" (integer ) 3 192.168.2.1:6379> lrange mylist 0 -1 1) "hello-2" 2) "hello-3" 3) "hello" 192.168.2.1:6379> rpoplpush mylist myotherlist "hello" 192.168.2.1:6379> lrange mylist 0 -1 1) "hello-2" 2) "hello-3" 192.168.2.1:6379> lrange myotherlist 0 -1 1) "hello" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> exists list3 (integer ) 0 192.168.2.1:6379> lset list3 0 'item' (error) ERR no such key 192.168.2.1:6379> lpush list3 'value1' (integer ) 1 192.168.2.1:6379> LRANGE list3 0 -1 1) "value1" 192.168.2.1:6379> lset list3 0 'item' OK 192.168.2.1:6379> LRANGE list3 0 -1 1) "item" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> rpush list4 "hello" "world" (integer ) 2 192.168.2.1:6379> linsert list4 before "world" "other" (integer ) 3 192.168.2.1:6379> lrange list4 0 -1 1) "hello" 2) "other" 3) "world" 192.168.2.1:6379> linsert list4 after 'world' 'new' (integer ) 4 192.168.2.1:6379> lrange list4 0 -1 1) "hello" 2) "other" 3) "world" 4) "new" --------------------------------------------------------------------------------------------------
Set(集合)
概述: 可以理解为一堆值不重复的列表,类似数学领域中的集合概念,且Redis也提供了针对集合的求交集、并集、差集等操作。set 的内部实现是一个 value永远为null的HashMap,实际就是通过计算hash的方式来快速排重的,这也是set能提供判断一个成员是否在集合内的原因。
场景: Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。又或者在微博应用中,每个用户关注的人存在一个集合中,就很容易实现求两个人的共同好友功能。
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> sadd myset "one" (integer ) 1 192.168.2.1:6379> sadd myset "two" "three" (integer ) 2 192.168.2.1:6379> smembers myset 1) "two" 2) "one" 3) "three" 192.168.2.1:6379> sismember myset 'one' (integer ) 1 192.168.2.1:6379> sismember myset 'ten' (integer ) 0 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> scard myset (integer ) 3 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> srem myset 'one' (integer ) 1 192.168.2.1:6379> scard myset (integer ) 2 192.168.2.1:6379> smembers myset 1) "two" 2) "three" -------------------------------------------------------------------------------------------------- 127.0.0.1:6379> smembers myset 1) "lovekuangshen2" 2) "lovekuangshen" 3) "kuangshen" 127.0.0.1:6379> srandmember myset "kuangshen" 127.0.0.1:6379> SRANDMEMBER myset "kuangshen" 127.0.0.1:6379> SRANDMEMBER myset "kuangshen" 127.0.0.1:6379> SRANDMEMBER myset "kuangshen" 127.0.0.1:6379> SRANDMEMBER myset 2 1) "lovekuangshen" 2) "lovekuangshen2" 127.0.0.1:6379> SRANDMEMBER myset 2 1) "lovekuangshen" 2) "lovekuangshen2" 192.168.2.1:6379> sadd myset "one" (integer ) 1 192.168.2.1:6379> smembers myset 1) "one" 2) "two" 3) "three" 192.168.2.1:6379> srandmember myset "three" 192.168.2.1:6379> srandmember myset "two" 192.168.2.1:6379> srandmember myset "one" 192.168.2.1:6379> srandmember myset 2 1) "two" 2) "one" 192.168.2.1:6379> srandmember myset 2 1) "two" 2) "three" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> smembers myset 1) "one" 2) "two" 3) "three" 192.168.2.1:6379> spop myset "two" 192.168.2.1:6379> spop myset "one" 192.168.2.1:6379> smembers myset 1) "three" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> sadd myset "hello" (integer ) 1 192.168.2.1:6379> smembers myset 1) "hello" 2) "three" 192.168.2.1:6379> smove myset myset2 "hello" (integer ) 1 192.168.2.1:6379> smembers myset 1) "three" 192.168.2.1:6379> smembers myset2 1) "hello" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> sadd set1 a b c (integer ) 3 192.168.2.1:6379> sadd set2 c d e (integer ) 3 192.168.2.1:6379> SDIFF set1 set2 1) "b" 2) "a" 192.168.2.1:6379> SINTER set1 set2 1) "c" 192.168.2.1:6379> SUNION set1 set2 1) "e" 2) "c" 3) "a" 4) "b" 5) "d" --------------------------------------------------------------------------------------------------
Zset(有序集合)
概述: 有序集合 类似 无序集合,它只是在set的基础上,增加了一个值,set k1 v1 zset k1 score1 v1
场景: Redis sorted set的使用场景与set类似,区别是set不是自动有序的,而sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。当你需要一个有序的并且不重复的集合列表,那么可以选择sorted set数据结构。
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 51 52 53 54 55 56 57 58 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> zadd myzset1 1 'one' (integer ) 1 192.168.2.1:6379> zadd myzset1 2 'two' 3 'three' (integer ) 2 192.168.2.1:6379> zrange myzset1 0 -1 1) "one" 2) "two" 3) "three" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> zadd salary 2500 zhangsan (integer ) 1 192.168.2.1:6379> zadd salary 3500 lisi (integer ) 1 192.168.2.1:6379> zadd salary 4500 wangwu (integer ) 1 192.168.2.1:6379> zrangebyscore salary -inf +inf 1) "zhangsan" 2) "lisi" 3) "wangwu" 192.168.2.1:6379> zrevrange salary 0 -1 1) "wangwu" 2) "lisi" 3) "zhangsan" 192.168.2.1:6379> zrangebyscore salary -inf +inf withscores 1) "zhangsan" 2) "2500" 3) "lisi" 4) "3500" 5) "wangwu" 6) "4500" 192.168.2.1:6379> zrangebyscore salary -inf 3500 withscores 1) "zhangsan" 2) "2500" 3) "lisi" 4) "3500" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> zrange salary 0 -1 1) "zhangsan" 2) "lisi" 3) "wangwu" 192.168.2.1:6379> zrem salary lisi (integer ) 1 192.168.2.1:6379> zrange salary 0 -1 1) "zhangsan" 2) "wangwu" 192.168.2.1:6379> zcard salary (integer ) 2 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> zadd myzset2 1 hello 2 world 3 good (integer ) 3 192.168.2.1:6379> zcount myzset2 1 3 (integer ) 3 192.168.2.1:6379> zcount myzset2 1 2 (integer ) 2
Hash(哈希)
概述: Map集合,key-map! 时候这个值是一个map集合! 本质和String类型没有太大区别,还是一个简单的key-vlaue!
场景: 假设有多个用户及对应的用户信息,可以用来存储以用户ID为key,将用户信息序列化为比如json格式做为value进行保存
hash变更的数据 user, 尤其是是用户信息之类的,经常变动的信息! hash 更适合于对象的存储,String更加适合字符串存储!
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 51 52 53 54 55 56 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> hset myhash1 field1 'cool' (integer ) 1 192.168.2.1:6379> hmset myhash1 field1 'hello' field2 'world' OK 192.168.2.1:6379> hget myhash1 field1 "hello" 192.168.2.1:6379> hmget myhash1 field1 field2 1) "hello" 2) "world" 192.168.2.1:6379> hgetall myhash1 1) "field1" 2) "hello" 3) "field2" 4) "world" 192.168.2.1:6379> hdel myhash1 field1 (integer ) 1 192.168.2.1:6379> hgetall myhash1 1) "field2" 2) "world" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> hmset myhash2 field1 'hello' field2 'world' OK 192.168.2.1:6379> hgetall myhash2 1) "field1" 2) "hello" 3) "field2" 4) "world" 192.168.2.1:6379> hlen myhash2 (integer ) 2 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> hexists myhash2 field1 (integer ) 1 192.168.2.1:6379> hexists myhash2 field3 (integer ) 0 -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> hkeys myhash2 1) "field1" 2) "field2" 192.168.2.1:6379> hvals myhash2 1) "hello" 2) "world" -------------------------------------------------------------------------------------------------- 192.168.2.1:6379> hset myhash2 field3 '5' (integer ) 1 192.168.2.1:6379> hincrby myhash2 field3 1 (integer ) 6 192.168.2.1:6379> hincrby myhash2 field3 -1 (integer ) 5 192.168.2.1:6379> hsetnx myhash2 field4 hello (integer ) 1 192.168.2.1:6379> hsetnx myhash2 field4 world (integer ) 0 --------------------------------------------------------------------------------------------------
Redis 事务
Redis事务的概念
Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
Redis事务实际上就是一次性、顺序性、排他性的执行一个队列中的一系列命令。所有的命令在事务中,并没有直接被执行!只有发起执行命令的时候才会执行!
Redis事务没有隔离级别的概念; 批量操作在发送 EXEC 命令前被放入队列缓存,并不会被实际执行,也就不存在事务内的查询要看到事务里的更新,事务外查询不能看到。
Redis不保证原子性; Redis中,单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行。
Redis事务相关命令
命令
描述
WATCH key key …
监视一或多个key, 如果在事务执行之前,被监视的key被其他命令改动,则事务被打断(类似乐观锁 )
MULTI
标记一个事务块的开始
EXEC
执行所有事务块内的命令
DISCARD
取消事务,放弃执行事务块内的所有命令
UNWATCH
取消 WATCH 命令对所有 key 的监视
Redis事务的3个阶段
开启事务(multi)
命令入队(……)
执行事务(exec)
Redis事务使用案例
正常执行事务!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 192.168.2.1:6379> multi OK 192.168.2.1:6379> set k1 'v1' QUEUED 192.168.2.1:6379> set k2 'v2' QUEUED 192.168.2.1:6379> get k2 QUEUED 192.168.2.1:6379> set k3 'v3' QUEUED 192.168.2.1:6379> exec 1) OK 2) OK 3) "v2" 4) OK 192.168.2.1:6379> keys * 1) "k3" 2) "k2" 3) "k1"
放弃事务!
1 2 3 4 5 6 7 8 9 10 11 12 192.168.2.1:6379> multi OK 192.168.2.1:6379> set k4 'v4' QUEUED 192.168.2.1:6379> set k5 'v5' QUEUED 192.168.2.1:6379> DISCARD OK 192.168.2.1:6379> keys * 1) "k3" 2) "k2" 3) "k1"
编译型异常 (源代码有问题! 命令有错!) ,事务中所有的命令都不会被执行!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 192.168.2.1:6379> multi OK 192.168.2.1:6379> set k4 'v4' QUEUED 192.168.2.1:6379> set k5 'v5' QUEUED 192.168.2.1:6379> getset k5 (error) ERR wrong number of arguments for 'getset' command 192.168.2.1:6379> set k6 'v6' QUEUED 192.168.2.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 192.168.2.1:6379> keys * 1) "k3" 2) "k2" 3) "k1"
运行时异常 (1/0), 如果事务队列中存在语法性,那么执行命令的时候,其他命令是可以正常执行的,错误命令抛出异常!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 192.168.2.1:6379> set k4 'v4' OK 192.168.2.1:6379> multi OK 192.168.2.1:6379> incr k4 QUEUED 192.168.2.1:6379> set k5 'v5' QUEUED 192.168.2.1:6379> get k5 QUEUED 192.168.2.1:6379> exec 1) (error) ERR value is not an integer or out of range 2) OK 3) "v5" 192.168.2.1:6379> keys * 1) "k2" 2) "k1" 3) "k5" 4) "k4" 5) "k3"
监控 ! Watch (面试常问!)
悲观锁:
很悲观,认为什么时候都会出问题,无论做什么都会加锁!
乐观锁:
很乐观,认为什么时候都不会出问题,所以不会上锁! 更新数据的时候去判断一下,在此期间是否有人修改过这个数据
Redis监控测试
正常执行成功!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 192.168.2.1:6379> set money 100 OK 192.168.2.1:6379> set out 0 OK 192.168.2.1:6379> watch money OK 192.168.2.1:6379> multi OK 192.168.2.1:6379> decrby money 20 QUEUED 192.168.2.1:6379> incrby out 20 QUEUED 192.168.2.1:6379> exec 1) (integer ) 80 2) (integer ) 20
模拟测试多线程修改值 , 使用 watch 可以当做redis的乐观锁操作!
1 2 3 4 5 6 7 8 9 10 192.168.2.1:6379> watch money OK 192.168.2.1:6379> multi OK 192.168.2.1:6379> decrby money 10 QUEUED 192.168.2.1:6379> incrby out 10 QUEUED 192.168.2.1:6379> exec (nil)
那怎么解决这个问题呢?
Redis 持久化 持久化概述
作用:
Redis的所有数据都是保存在内存中, 如果没有配置持久化, redis重启后数据就全丢失了, 于是需要开启redis的持久化功能, 将数据保存到磁盘上, 当redis重启后, 可以从磁盘中恢复数据。
那么不定期的通过异步方式保存到磁盘上(半持久化模式); 也可以把每一次数据变化都写入到一个append only file里面(全持久化模式)。 如若在服务器中开启了两种持久化的方式,默认执行AOF持久化方式。
实现方式:
RDB持久化:将Reids在内存中的数据库记录,定时dump到磁盘上,类似于快照功能 。
AOF持久化:原理是将Reids的操作日志以追加的方式写入文件,近似实时性 。
默认redis用的就是RDB方式的持久化,如果两种持久化都开启的话,则优先使用AOF
二者区别:
RDB 持久化是指在指定的时间间隔内将内存中的数据集快照写入磁盘,实际操作过程是fork一个子进程,先将数据集写入临时文件,写入成功后,再替换之前的文件,用二进制压缩存储。
AOF 持久化是以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录,以文本的方式记录,可以打开文件看到详细的操作记录。
应用场景:
RDB(Redis DataBase)
什么是RDB ?
在指定的时间间隔内将内存中的数据集快照写入磁盘, 也就是行话讲的Snapshot快照, 它恢复时是将快照文件直接读到内存里。
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况下不需要修改这个配置!
有时候在生产环境我们会将这个文件进行备份!(dump.rdb)
优点:
缺点:
需要一定的时间间隔进程操作!如果redis意外宕机了,这个最后一次修改数据就没有的了!
fork进程的时候,会占用一定的内容空间!!
rdb保存的文件是dump.rdb 都是在配置文件中快照中进行配置的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 218 save 900 1 219 save 300 10 220 save 60 10000 235 stop-writes-on-bgsave-error yes 241 rdbcompression yes 250 rdbchecksum no 253 dbfilename dump.rdb 263 dir /usr/local /redis/
测试一下:
只要 60s 内修改了 2 次 key,就会触发 RDB 操作
触发机制
1、save的规则满足的情况下,会自动触发rdb规则
2、执行 flushall 命令,也会触发我们的rdb规则!
3、退出redis,也会产生 rdb 文件!
恢复rdb文件
1、redis在启动的时候会自动检查dump.rdb 恢复其中的数据!
2、查看需要存在的位置
1 2 3 192.168.2.1:6379> config get dir 1) "dir" 2) "/usr/local/redis"
AOF(Append Only File)
什么是AOF ?
就相当于我们的所有命令都记录下来,history,恢复的时候就把这个文件全部在执行一遍!
以日志的形式来记录每个写操作, 将Redis执行过的所有指令记录下来(读操作不记录), 只许追加文件但不可以改写文件, redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF 文件的生成过程具体包括命令追加,文件写入,文件同步三个步骤。
AOF保存的是 appendonly.aof 文件
Redis 打开 AOF 持久化功能后,Redis 在执行完一个写命令后,都会将执行的写命令追回到 Redis 内部的缓冲区的末尾。这个过程是命令的追加过程。 接下来,缓冲区的写命令会被写入到 AOF 文件,这一过程是文件写入过程。对于操作系统来说,调用write函数并不会立刻将数据写入到硬盘,为了将数据真正写入硬盘,还需要调用fsync函数,调用fsync函数即是文件同步的过程。只有经过文件同步过程,AOF 文件才在硬盘中真正保存了 Redis 的写命令。appendfsync 配置选项正是用来配置将写命令同步到文件的频率的,各个选项的值的含义如表 1 所示。而值为no的话表示为写入AOF文件,但是不进行磁盘同步,根据linux系统默认进行磁盘同步,默认30s,效率是最高的。
优点:
每一次修改都同步,文件的完整会更加好!
每秒同步一次,可能会丢失一秒的数据
缺点:
相对于数据文件来说,aof远远大于 rdb,修复的速度也比 rdb慢!
AOF 运行效率也要比 rdb 慢,所以我们redis默认的配置就是rdb持久化!
aof保存的文件是 appendonly.aof 都是在配置文件中快照中进行配置的!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 218 219 220 672 appendonly yes 676 appendfilename "appendonly.aof" 702 appendfsync everysec 724 no-appendfsync-on-rewrite no 743 auto-aof-rewrite-percentage 100 744 auto-aof-rewrite-min-size 64mb
同步频率级别(推荐no | everysec)
频率级别
频率作用
always
每个Redis 命令都要同步写入硬盘;这个级别严重降低 Redis的性能
everysec
每秒执行一次同步,显示的将 写 多个命令同步到硬盘
no
让操作系统来决定如何进行同步
默认是不开启的,我们需要手动进行配置!我们只需要将 appendonly 改为yes就开启了 aof ; 重启 redis 就可以生效了!
触发机制
重写规则说明
aof 默认就是文件的无限追加,文件会越来越大!
如果 aof 文件大于了 64mb,Redis 会重新 fork一个新的进程来将我们的文件进行重写!
RDB转换AOF持久化
RDB持久化的数据,当改为AOF持久化的时候,数据是无法同步的,倘若想要RDB里边的数据,操作如下:
建议:不要轻易改变持久化的方式
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 [root@redis01 ~] 218 save 900 1 219 save 300 10 220 save 60 10000 672 appendonly no :wq! [root@redis01 ~] [root@redis01 ~] 192.168.2.1:6379> keys * 1) "name" 2) "age" 192.168.2.1:6379> save 192.168.2.1:6379> config set appendonly yes 192.168.2.1:6379> save 192.168.2.1:6379> shutdown save not connected> exit [root@redis01 ~] 218 219 220 672 appendonly yes :wq! [root@redis01 ~] [root@redis01 ~] 192.168.2.1:6379> keys * 1) "age" 2) "name" 192.168.2.1:6379> exit [root@redis01 ~] *2 $6 SELECT $1 0 *3 $3 SET $4 name $5 lemon *3 $3 SET $3 age $2 18
持久化总结 1、RDB 持久化方式能够在指定的时间间隔内对你的数据进行快照存储
2、AOF 持久化方式记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以Redis 协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大。
3、只做缓存,如果你只希望你的数据在服务器运行的时候存在,你也可以不使用任何持久化
4、同时开启两种持久化方式
5、性能建议
因为RDB文件只用作后备用途,建议只在Slave上持久化RDB文件,而且只要15分钟备份一次就够了,只保留 save 900 1 这条规则。
如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价一是带来了持续的IO,二是AOF rewrite 的最后将 rewrite 过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要硬盘许可,应该尽量减少AOF rewrite的频率,AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小100%大小重写可以改到适当的数值。
如果不Enable AOF ,仅靠 Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔IO,也减少了rewrite时带来的系统波动。代价是如果Master/Slave 同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个 Master/Slave 中的 RDB文件,载入较新的那个,微博就是这种架构。
Redis缓存穿透和雪崩
服务的高可用问题!
在这里我们不会详细的区分析解决方案的底层!
Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。
另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。
缓存穿透(查不到)
概念
缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次查询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。
解决方案
1、布隆过滤器
布隆过滤器是一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则 丢弃,从而避免了对底层存储系统的查询压力;
2、缓存空对象
当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源;
但是这种方法会存在两个问题:
1、如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
2、即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿(量太大,缓存过期!)
概述
这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。
当某个key在过期的瞬间, 有大量的请求并发访问, 这类数据一般是热点数据, 由于缓存过期, 会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。
解决方案
1、设置热点数据永不过期
从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。
2、加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
缓存雪崩
概念
缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!
产生雪崩的原因之一, 比如在写本文的时候, 马上就要到双十二零点, 很快就会迎来一波抢购, 这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候, 这批商品的缓存就都过期了。而对这批商品的访问查询, 都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。
解决方案
1、redis高可用
这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。(异地多活!)
2、限流降级
这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。
3、数据预热
数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。