安装Redis
docker-compose安装—单节点
version: "3.3"
services:
master:
image: redis:6.0
container_name: redis
environment:
- "discovery.type=single-node"
command: redis-server --port 6379 --requirepass liuy13141@ --appendonly yes
restart: always
ports:
- 6379:6379
volumes:
- /home/redis/data:/data
Redis命令
select 1; 选择1号DB
DBSIZE; 当前选择DB大小
set name “liuy” 设置key:value
get name 获取key 的value
keys * 查看所有Key
flushall 清空全部DB的key
flushdb 清空当前DB的Key
Redis单线程
redis基于内存操作,CPU不是Redis性能瓶颈。
Redis瓶颈:机器的内存和网络贷款
每秒10w+的QPS
数据操作速度:CPU>内存>硬盘
Redis单线程为什么快?
redis所有数据都存放内存中,所有单线程操作起来快,多线程会存在CPU上下文切换耗时操作,对于内存系统而言,没有上下文切换就是效率最高。多次读写都在一个CPU上。
数据类型
-
String
- 在key原有value后面追加value
APPEND key value .
- 获取字符串长度
STRLEN key
-
incr key 自增1
decr key 自减1
INCRBY key 10 自增步长10
DECRBY key 10 自减步长10 - 获取字符串范围值
GETRANGE key 0 9 0~-1表示获取全部
- 替换指定位置开始的字符串
SETRANGE key 1 “liuy”
- 分布式操作
setex key 20 “liuy” 设置 过期时间
setnx key “liuy” 如果key不存在创建key,如果存在创建失败
msetnx key1 value1 key2 value2 同时设置多个值,原子操作要不全部成功,要么就失败。 - 同时获取多个值
mget key1 key2
- 设置对象
mset user:uuid:name zhangsan user:uuid:age :22
设置对象值user:{id}:{filed} - 先get再set
getset key value 先get再set 如果不存在返回nil,存在返回原来值,再设置新值。 更新操作
- 在key原有value后面追加value
-
List 适合两端操作,消息队列
- 插入list
LPUSH Llist one/two/three 从头开始插入
RPUSH Rlist one/two/three 从尾开始插入 - 出list
Lpop Llist -->one 从头开始移出
Rpop Llist -->three从尾开始移出 - 获取list所有值
LRANGE list 0 -1
- 通过下标获取值
Lindex list 1 获取下标1的list值
- 返回list长度
Llen list 返回list长度
- 移除list中指定值
lrem list 2 one 移除list中2个为one的值
- 通过下标截取指定长度list
ltrim list 1 2 list只保留截取的元素
- 移除list最后一个元素,push到另外一个list中
rpoplpush list newlist; 返回移除的元素
- 替换list指定下标的值
lset list 0 “123” 如果下标0存在就更新值,不存在报错:index超出
- 将value插入到指定元素的前面或后面
linsert list before “world” “other” 把other插入到list前面
linsert list after"world" “other” 把other插入到list后面面
- 插入list
-
Set set不可重复 共同好友
- 添加
sadd myset value
- 查看指定set所有值
SMEMBERS myset
- 判断value是否存在
SISMEMBER myset value 存在–1 不存在–0
- 获取set集合中元素个数
scard myset
- 移除set集合指定value
srem myset hello 移除集合中的hello
- 随机获取元素
SRANDMEMBER myset
SRANDMEMBER myset 2 随机两个元素 - 随机移除元素
spop myset
- 移除指定值到另一个set集合
smove myset myset2 “movevalue”
- 共同好友,并集
SDIFF setkey1 setkey2 差集
SINTER setkey1 setkey2 交集 共同好友
SDIFF setkey1 setkey2 并集
- 添加
-
Hash Map 集合–key:value 处理对象
- 插入
hset hashname hashkey hashvalue
hmset hashname hashkey hashvalue hashkey hashvalue - 获取
hget hashname hashkey
hmget hashname hashkey hashkey
hgetall hashname - 删除指定key,对应value也没有了
hdel hashname hashkey
- 获取hash表key数量
hlen hashname
- 判断hash中指定key存在
hexists hashname hashkey
- 获取所有key,value
hkeys hashname
hvals hashname
- 插入
-
Zset 排序集合 排行榜
- 添加
zadd ZsetName 1 value
zadd ZsetName 2 value1 3 value2 - 排序
ZrangeBySCORE ZsetName -inf +inf 从小到大
ZrangeBySCORE ZsetName -inf 5000 withscores 升序到5000并显示key
ZREVRANGE ZsetName 0 -1 降序 - 移除Zset指定元素
zrem ZsetName value
- 获取Zset中元素个数
zcard ZsetName
- 添加
设置key有效期
EXISTS key 判断key存在 1 存在-0 不存在
move key 移除 key
EXPIRE key 10 设置key10秒过期
ttl key key剩余时间过期
type key 当前key类型
Geospatial 地理位置
底层采用Zset,可用zset命令操作
经纬度查询
- 添加地理位置 纬度,经度 ,名称
geoadd china:city 39.90 116.40 beijin
- 获取指定城市的经度纬度
geopos china:city beijing chongqin 返回坐标值
- 获取两地的直线距离
geodist china:city beijing shanghai km
- 附近的人(半径查询)
georadius china:city 110 30 1000 km 【withdist 距中心距离】【withcoord 经纬度】【count 1 查询个数】
以110,30 经纬度为中心 1000km半径内的城市 - 找指定元素周围坐标 —定位
geoRadiusByMember china:city beijing 1000km 北京1000km内的东西
Hyperloglog 基数统计(允许容错)
优点: 占用内存固定,264不同的元素技术,12KB内存。
一个人访问一个网站多次,记作一次。
PFadd mykey1 p1 p2 p3 p4 p5 p6 创建第一组用户
PFadd mykey2 p2 p7 p3 p8 p5 p10 创建第二组用户
PFMERGE mykey3 mykey1 mykey2 合并1和2 ==> key3
PFCOUNT key3 并集数量
Bitmaps(统计两种状态的数据)
位运算储存
记录是否签到,只需要46字节就能存储365天数据
365天=365bit 1B=8bit 365/8=46个B
setbit person1 1 1; 用户1 第一天签到
setbit person1 2 1; 用户1 第二天签到
setbit person1 3 0; 用户1 第三天未签到
setbit person1 4 1; 用户1 第四天签到
查看某天签到情况
getbit person1 3—>0
getbit person1 4—>1
统计签到天数
bitcount person1—>3
事务
Redis事务:一组命令的集合,一个事务中所有命令的都会被序列化,在事务执行过程中,顺序执行,保证一次性,顺序性,排他性。
Redis没有事务隔离级别概念。
Redis事务中,并不是直接执行,要发起执行命令才执行。
Redis只保证单条命令的原子性,事务不保证。
事务过程: 开启事务(multi),命令入队,执行事务(exec)。
执行完成后 ,一组事务就结束。
放弃事务(DISCARD) ,队列中命令全部取消
- 编译时异常(代码问题),事务中所有命令都不会执行
- mulyi 开启事务
- set key1 v1 :入队成功
- getset key1 :命令报错,仍可继续加入事务队列 .
- exec :事务执行时会编译报错,所有命令都不会执行
- 运行时异常(1/0) ,其他命令正常执行,错误命令抛出异常
- mulyi 开启事务
- set key “v1” :入队成功
- incr key:将key1的值自增,此时命令编译不会报错(执行时:String格式自增会报错)。
- exec :事务执行时会抛出错误,但其他命令正常执行
乐观锁
通过比较version,判断是否有人修改数据。
监视过程中,如果有其他线程修改监视的值,那后面的事务一定失败;
- UNWATCH 事务执行失败,先解锁
- watch money; 获取最新值,监视money;
- multi 开启事务
- DECRBY money 10 取钱10
- INCRBY money 100 存钱 100
- exec; 比较监视器中的值
jedis与lettuce
jedis:采用直连,多线程操作,不安全;如果需要避免线程不安全,使用jedis pool 连接池,类似BIO模式。
lettuce:采用netty,实例可多线程中共享,不存在线程不安全;可减少线程数据,类似NIO模式。
Redis常用配置
######网络配置##########
# 指定 redis绑定的IP地址,如果不进行设置,那么将处理所有请求
bind 127.0.0.1
#保护模式,默认开启。
protected-mode yes
# 端口号
port 6379
######通用配置##########
# 守护进程方式运行,默认no
daemonize yes
#以后台方式运行指定pid进程文件
pidfile /var/run/redis_6379.pid
# 日志
#debug(记录所有日志信息,适用开发阶段)
#verbose(记录大部分日志信息)
#notice(适当的日志信息,适合生产环境,默认级别)
#warn(只有记录非常重要的信息)
loglevel notice
# 日志文件的位置
logfile /usr/local/redis.log
############快照###############
#持久化,规定时间内执行多少次操作,就会持久化到文件
save 900 1 #900s 内,至少一个key进行修改,则进行持久化
save 300 10 #300s 内,至少10个key进行修改,则进行持久化
save 60 10000 #60s 内,至少10000个key进行修改,则进行持久化
#持久化出错后,是否继续工作 默认开启
stop-writes-on-bgsave-error yes
#压缩RDB文件,消耗一定的CPU资源 默认开启
rdbcompression yes
#保存RDB文件时,进行错误检查校验 默认开启
rdbchecksum yes
# RDB、AOF生成的当前目录
dir ./
############安全###############
# 设置密码 默认没有密码
# 密码得有点难度,redis认证密码太快了
requirepass foobared
#######附加模式##############
#aof持久化模式,默认关闭
appendonly no
appendfilename "appendonly.aof"
#同步数据
#always 每次修改都会sync同步,消耗性能
#everysec 每秒执行一次,可能丢失这一秒数据
#no 不执行sync,由操作系统同步数据,速度最快
appendfsync everysec
Redis持久化
RBD快照(默认)
持久化:指定时间间隔将内存的数据集快照写入硬盘,恢复时将快照文件直接读到内存中。
RDB持久化过程:单独创建(fork)一个子进程进行持久化,将内存中的数据写入临时RDB文件中,待持久化过程结束,再用这个临时文件替换上次的持久化文件。
整个持久化过程中,主进程不进行任何IO操作,保证性能。
因为RDB需要时间,所有最后一次的持久化数据可能丢失。
设置密码config set requirepass “”
触发机制
- save规则满足情况,触发RDB
- 执行flushall命令,触发RDB
- 退出redis,触发RDB
恢复RDB文件
- config get dir #找到redis启动目录
- xxx.rdb 放到redis启动目录中,自动检查rdb恢复其中数据。
优点
- 适合大规模的数据恢复
- 对数据完整性要求不高
缺点
- 需要一定时间间隔进程操作
- 如果redis意外宕机,最后一次修改的数据就丢失了
- fork子进程的会占用一定的内存空间
AOF
AOF持久化过程:单独创建(fork)一个子进程进行持久化,根据内存中数据快照,将改变数据的每个写命令以日志的形式记录,只追加到AOF文件中,redis重启会读取文件重新构建数据(所有指令全部执行一次)。
修复aof文件
- redis-check-aof --fix appendonly.aof
优点:
- 每次修改都同步,文件完整性更好
- 每秒同步一次,可能丢失一秒数据
- 从不同步,效率最高
缺点: - aof远远大于rdb文件,修复速度比rdb慢。
- aof运行效率比rdb慢,会进行大量IO操作。
扩展:
-
RDB持久化方式能指定时间间隔内对数据进行快照存储。
-
AOF持久化方式记录每次对服务器的写操作,当服务器重启时重新执行这些命令来恢复数据,AOF命令以Redis协议追加保存每次写的操作到文件末尾,Redis还能对AOF文件进行后台重写,使得AOF文件体积不至于过大。
-
只做缓存,可以不用持久化。
-
同时开启两种持久化方式
- Redis重启会优先载入AOF文件来恢复数据,通常情况AOF文件保存数据比RDB的数据集更完整。
- RDB更适合备份数据库,AOF文件不断变化可能出现BUG,不易备份。
-
性能
- RDB文件只做后备用途,建议在Slave上持久化RDB文件 。
- 开启AOF,好处:最恶劣情况也只是丢失不操作2秒数据,代价:带来持续的IO操作,rewrite产生新数据带来的阻塞。
- 关闭AOF,仅靠Master-Slave Repllcation实现高可用,好处:省掉IO,减少rewrite带来系统波动。代价:主从同时宕机,会丢失十几分钟数据。启动脚本会比较主从中RBD文件,载入较新的那个。
Redis主从复制(一主三从)
默认情况:每台redis都是主机。只需要配置从机。
主从复制:将一台主服务器上的数据复制到其他从服务器,数据复制单向的,只能从主节点到从节点,Master以写为主,Slave以读为主。
作用:
- 数据冗余:实现数据的热备份,持久化之外的一种数据冗余方式。
- 故障恢复:主节点故障,从节点提供服务,实现快速故障恢复;实际上一种服务的冗余。
- 负载均衡:主从复制基础上,配合读写分离,主节点负者写服务,从节点负者读服务,分担服务器负载。
- 高可用的基石:哨兵和集群的实施基础。
环境配置:
通过命令配置,只生效一次,重启即失效。
- 拷贝配置文件
cp redis.conf redis6379.conf
cp redis.conf redis6380.conf
cp redis.conf redis6381.conf
cp redis.conf redis6382.conf - 修改port、pidfile、logfile、dbfilename
pidfile /var/run/redis_6379.pid
port 6379
logfile “redis_6379.log”
dbfilename dump6379.rdb - 只配置从机
slaveof 127.0.0.1 6379 从机连接到6379端口的主机
slaveof no one 自己就是主机
info replication 查看redis信息 - 完成主写从读
复制原理:
- Slave启动连接master后发送一个Sync同步命令
- Master接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集的命令,在后台进程执行完毕后,master将整个数据文件传送到slave,并完成一次完全同步。
- 全量复制:slave接收到数据文件后,将其存盘并加载到内存。自动执行。
- 增量复制:master继续将的所有收集到的修改命令依次传给slave,完成同步。
Redis哨兵(自动选举主机)
- 基础配置文件sentinel.conf
sentinel monitor redis 127.0.0.1 6379 1
redis 被监控名称
host
port
1 主机挂了,自动投票选出主机 - 启动哨兵
redis-sentinel sentinel.conf
优点:
- 哨兵集群,基于主从复制模式
- 主从切换,故障可以转移,系统可用性更好
- 哨兵模式就是主从模式升级,自动选取主机
缺点: - Redis不好在线扩容,集群容量达到上限,在线扩容麻烦。
- 哨兵模式配置多。
Redis缓存穿透和雪崩
缓存穿透
缓存穿透:redis缓存中没有命中,再向数据库中查询,也没有。导致查询失败。大量请求都这种状态,会给数据库造成很大压力,这就是缓存穿透。
解决方案:
- 布隆过滤器
一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先校验,不符合则丢弃,避免对底层存储系统造成压力。
- 缓存空对象
即使查到的数据为空,也缓存起来。
浪费空间,造成数据不一致问题。
缓存击穿
缓存击穿:缓存中的热点数据在大并发情况下,突然失效,造成大量并发直接请求服务器。好像在墙上凿了一个洞。
解决方案:
- 热点数据key永不过期
- 分布式锁
对于每个key同时只能一个线程去查询后端服务,其他线程没有获得分布式锁就等待。将高并发压力转换到分布式锁。
缓存雪崩
缓存服务断网
解决方案:
- redis高可用
异地搭建集群
- 限流降级
缓存失效后,通过加锁或者队列控制读数据库的线程数量。
- 数据预热
正式部署前,将热点数据预先访问一遍,设置不同过期时间,让失效时间尽量均匀。