redis学习
2021-09-21 23:28:48 0 举报
AI智能生成
很详细了,从redis的基本使用。使用场景,以及使用过程中出现的问题和对应的解决方案。后续随着知识的更新,还会持续更新
作者其他创作
大纲/内容
redis发布订阅模式
一种消息通信模式,发送者发送消息,订阅者接收消息
客户端可以订阅任意数量的频道
redis新的数据类型
Bitmaps
特性
第一次初始化bitmaps时,如果偏移量非常大,那么整个初始化过程执行比较慢,可能会造成Redis进程的阻塞
在统计网站访问量的时候,假如访问量非常小,那么大部分都是0,没有完全发挥统计作用。bitmap就是比较占用空间
主要对活跃用户量进行存储,能够极大的节省很多内存空间
这个类型可以实现对位的操作。本身不是一种数据类型,实际上就是字符串,但是可以对字符串的位进行操作
可以把bitmaps想象成以位为单位的数组,每个单元只存储0或1,数据的下标在bitmaps中叫偏移量
命令
setbit key offset val
val只能是1或者0,因为是位操作,一字节8位
getbit key offset
bitcount
统计字符串的为值被设置为1的bit数。一般情况下给定的整个字符串都会被计数
bitop and (or not xor ) destkey
复合操作,主要做多个bitmaps的交集and,并集or,not(非),xor(异或)操作。并将结果保存在destkey中
HyperLogLog
特性
统计的时候做计数并去重,得到基数数量,就是去重后的统计数量
mysql中的distinct count,redis中的hash,set,bitmaps也可以处理。但是随着数据量的不断增加,占用空间越来越大,对于非常大的数据集,不切实际
提供了一种降低一定精确度来平衡存储空间的方案
优点:在输入元素的数量或者体积非常大时,计算基数所需要的空间总是固定的,并且是很小的
命令
pfadd key element1 element2
添加指定元素到HyperLogLog中
pfcount key1 key2
计算HLL的近似基数
pfmerge destkey srckey1 srckey2
将一个或者多个HLL合并后的结果存在另一个HLL中,比如每月可以使用每天的活跃用户数合并计算得到
Geospatial
特性
该类型就是元素的二维坐标,在地图上的经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度hash等常见操作
一般会下载城市数据,直接通过java程序一次性导入
当坐标位置超过经纬度的有效范围时,返回错误
已经添加的数据,无法再次添加
geoadd key longitude latitude member [ ]
geopos key memeber
获取指定地区的坐标值
geodist key m1 m2 [ m |km | ft | mi]
两个位置之间的直线距离,mi英里,ft英尺
georadius key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC
以给定的经纬度为中心,找出某一半径内的元素
redis事务操作
特性
redis事务是一个单独的隔离操作:事务中的所有命令都会序列化,按照顺序执行。且执行过程中,不允许被其他客户端发过来的命令打断。 redis事务的主要作用就是串联多个命令防止别的命令插队
redis事务三特性
单独的隔离操作:所有的命令都会序列化,按顺序执行。事务在执行过程中不会被其他客户端发过来的命令打断
没有隔离级别:队列中的命令没有提交前不会实际执行
不保证原子性:假如队列中的个别命令有运行时错误,比如类型不对,而不是语法错误。那么执行的时候,其他命令都会执行成功
命令
multi
开启事务,把命令依次放入队列
exec
执行
discard
放弃队列中的命令
watch key1 key2
在执行multi之前执行watch,可以监视一个或者多个key,相当于获取数据的版本号。如果在事务执行过程中,发现这个key被改动,那么事务将被打断
unwatch 取消对命令的监视
事务失败
组队的时候,命令报错误,则所有命令在执行的时候都会失败
组队的时候没有报错,仅仅有错的命令在执行的时候失败,其他正确的命令成功
事务冲突
悲观锁
假设每次获取数据都会产生线程安全问题,每次都加锁,阻塞其他线程
乐观锁
每次拿数据都认为别人不会操作,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制
适用于多读的场景,提高吞吐量
redis就是利用这种check-and-set机制实现的
由于版本号比较失败后,直接造成扣除失败,就会造成库存遗留问题
使用lua脚本,redis对lua脚本的执行具有原子性,可以把操作封装到lua脚本中
redis持久化
rdb(redis database)持久化
fork一个与当前进程一样的子进程,把内存中数据写到一个临时文件中,持久化完成后,替换原来的持久化文件。主进程不进行任何IO操作。写时复制技术
特性
在指定的时间间隔内将内存中的数据集快照写入到磁盘,恢复的时候将快照文件直接读到内存中
fork的时候,内存中的数据被克隆了一份,大致2倍的膨胀
适合大规模的数据恢复,对数据完整性和一致性要求不高
如果down机,会丢失最后一次快照后的所有修改
rdb文件紧凑,节约空间,恢复快
save命令同步持久化,bgsave通过子进程异步持久化
配置
save 配置快照存储的时间间隔
dbfilename 配置rdb的文件名
其他看配置文件就行
通过config get 配置项名 可以获取配置信息,支持通配符*
aof(append only file)持久化
特性
以日志的形式记录每个写操作,增量保存,只追加文件。启动的时候重放该文件完成恢复
默认不开启。如果两种方式同时开启,启动的时候读取aof文件
自带redis-check-aof工具可以修复损坏的aof文件
备份机制更加稳健,丢失数据频率更低
比RDB占用更多空间,恢复备份慢,每次读写都同步的话,有一定的性能压力
aof缓冲区同步到磁盘的策略
appendfsync always
appendfsync everysec
appendfsync no 同步时机交给操作系统
写命令先追加到缓冲区,再通过缓冲区持久化到磁盘
rewrite重写压缩
aop采用文件追加方式,文件会越来越大,为了避免这种情况,新增了重写机制,当aof文件的大小超过所设定的阈值时,redis就会启动aof文件的内容压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
fork一条新的进程重写文件。
重写过程
官方推荐
两个都启用
不建议单独使用aof,因为可能出现bug
如果对数据不敏感,单独使用RDB
只是纯粹做缓存,可以都不用
redis主从复制
特性
读写分离
容灾快速恢复
从机中只能做读操作
配置方式
客户端直接执行slaveof host port
slaveof no one 重新由从服务器变成主服务器
故障场景
从服务器挂掉之后,重新连接到主服务器,会从头开始复制
主服务器挂掉之后,从服务器不会改变主从配置,当主服务器启动之后,恢复正常主从复制
原理
复制过程
从连接到主的时候,进行全量复制,全量复制完成后,主对从进行增量复制
哨兵模式
步骤
创建sentinel.conf配置文件
添加sentinel monitor masterName masterHost port num
num表示至少多少个哨兵同意才能迁移
redis-sentinel sentinel.conf启动哨兵
主服务器挂掉之后,哨兵选择一个从服务器作为主服务器,获得哨兵投票最多的从服务器成为主,原来的主服务器虽然挂掉,但是作为从服务器使用
选择主服务器的条件
选择优先级靠前的 slave-priority redis.conf中配置
越小优先级越高
选择偏移量最大的,即获得原主机数据最全的
选择runid最小的
redis每次启动会随机生成一个40位的runid
复制延时
由于所有的写操作都是先在master上操作,然后同步更新到slave上,所以从master同步到slave上有一定的延迟,当系统很忙的时候,延迟问题会更加严重,slave机器数量的增加也会使这个问题更加严重
redis集群
特性
无中心化集群,避免了应用程序中对主机地址的配置
实现了对redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在N个节点中,每个节点存储总数据的1/N
不支持mset类似的批量操作
使用mset name{key} lucy 相当于使用大括号内的内容作为计算插槽的key
插槽概念
一个redis集群包含16384个插槽,数据库的每个键都属于这16384个插槽中的一个
集群使用公式CRC16(key)%16384 来计算键key属于哪个插槽
集群中的每个负责处理一部分插槽
无中心化集群,当发现key计算的插槽不在本机时,会redirect到插槽所在范围的主机
查看key所在的插槽:cluster keyslot key
查看插槽值里面有多少key:cluster countkeysinslot slotValue
只能查看本拥有的插槽范围
cluster getkeysinslot slotValue num
返回插槽中指定数量的key
集群配置步骤
打开集群模式: cluster-enabled yes
指定节点配置文件名:cluster-config-file nodeName.conf
设置节点失联时间: cluster-node-timeout
如果节点在超时时间内没有操作,集群自动切换主从
启动各个节点
在src目录下,执行redis-cli --cluster create --cluster-replicas 1 host:port host:post
redis使用的是ruby脚本执行的。-replicas 1 表示每个主节点配置一个从节点
连接的时候要以集群方式连接 redis-cli -c -p 6379
cluster nodes 查看集群信息
故障恢复
主服务器宕机之后,它的从服务器自动升级为主服务器
宕机的主服务器重启后,自动变为从机
主从都挂掉,这一段插槽都不能提供服务
cluster-require-full-coverage 为yes,整个集群都挂掉
cluster-require-full-coverage为no,只有该插槽不能使用
好处
分摊压力
无中心配置相对简单
实现扩容
缺点
redis集群中多键操作不支持比如交集,并集等。但是mset只能通过组进行多键操作
redis集群中,多键操作在redis事务中不支持,lua脚本也不支持
由于集群方案出现比较晚,很多公司采用代理或者客户端分片的集群方案想要过度到redis cluster,需要整体迁移而不是逐步过渡,复杂度大
redis可能遇到的问题
缓存穿透
大量请求过来,redis没有命中数据,或者命中率很低,导致直接查询数据库,数据库崩溃
产生原因
redis查询不到数据,访问数据库,数据库返回空值
出现很多非正常url访问(url的结尾是个id值),数据库肯定没有这个值,但是一直被查询
一般是恶意攻击造成
解决方案
设置空值
数据库查询不到,缓存空值,设置一个短期过期值
设置可访问白名单
使用bitmaps类型定义一个可以访问的名单,名单id作为bitmaps的偏移量
每次访问和bitmaps中的id进行比较,如果访问id不在bitmaps里面,进行拦截,不允许访问
布隆过滤器
优化过的bitmaps,底层差不多。
实际上是很长的二级制向量(很长的位数组,只存0,1)和一些随机映射函数(hash函数)
因为hash冲突,有一定误识别率,删除困难
足够大的布隆过滤器,也占用很大内存
redis实时监控
监控redis中的命中率,如果急剧下降,需要排查访问对象和访问的数据,与运维配合,可以设置黑名单限制服务
缓存击穿
特点
数据库的访问压力瞬间增大
但是redis里面没有出现大量key过期,redis平稳运行,没有压力
是由于某个key过期删除,而大量访问使用了这个key,导致直接查询数据库
解决方案
预先设置热门数据
在redis访问高峰之前,把一些热门数据提前存入到redis
并且加大这些热门数据key时长
实时监控
现场监控哪些数据热门,实时调整key的过期时长
使用锁
缓存过期失效后使用排他锁,查询数据库,同步缓存
没获取排他锁的线程直接返回失败
分布式系统中使用分布式锁
或者使用cas进行操作也是可行的,限于单机
缓存雪崩
特点
在极少时间内,大量key集中过期
数据库压力变大,崩溃,缓存崩溃,服务器崩溃
解决方案、
构建多级缓存结构
nginx缓存,redis缓存,其他缓存(ehcache,guavacache)
复杂
使用锁或者队列
用加锁或者队列的方式保证不会有大量的线程对数据库一次性进行读写
从而避免失效时大量的并发请求落到底层存储系统
在分布式系统中可以使用redis的分布式锁
不适合高并发吗,我觉得还可以
设置过期标志更新缓存
记录缓存数据是否过期,如果快过期会触发通知另外的线程在后台更新实际key的缓存
将缓存失效时间分散开
比如我们可以在原有的失效时间基础上加一个随机值,比如1-5分钟随机
这样每个缓存的过期时间重复率降低,很难引发集体失效事件
分布式锁
解决多进程之间的同步访问问题
分布式锁可用至少确保锁的实现同时满足四个条件
互斥性
任意时刻,只有一个客户端能持有锁
不会发生死锁
即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端最终能获得锁。锁超时
加解锁必须同一个客户端,不能把比人加的锁解了
加锁具有原子性,解锁具有原子性
一般方案
基于数据库主键的唯一性
已经插入一个主键id之后不能再次插入相同主键id的行
基于redis
setnx的已设值后,不可再次设值,即不可更新的设值
无法设值过期时间
可以使用expire设置过期时间,但是操作不具有原子性了。万一设置过期时间前宕机了。。。
set 命令同时支持nx与ex参数,可解决setnx不可设置过期时间问题
释放非自己的超时锁问题
获得锁之后,服务器意外阻塞,超时,redis自动释放锁
此时锁被其他进程获取,阻塞的服务器唤醒之后,继续执行,并且释放了锁,此时释放了他人的锁
uuid防止误删锁
加锁的时候设置一个uuid值,释放的时候比较uuid,只有redis中的uuid与本地一致,才释放
先比较,才释放。这不是一个原子操作啊,也是有可能出现问题,有可能正在比较着,还没有删呢,锁过期释放,被其他进程拿到锁,还是有可能误删
lua脚本支持原子操作
在lua脚本比较删除
基于zookeeper
redison
nosql概述
分类
KV键值对
redis,memedcache
文档型数据库
MongoDB,es
列存储数据库
Hbase
图关系数据库
neo4j
作为关系性数据库的补充,应对海量用户和海量数据,降低响应时间,数据最终还是要落地到磁盘
性能测试工具
redis-benchmark
关系型数据库的缺点
性能瓶颈
磁盘IO性能很低
扩展瓶颈
数据关系复杂,扩展性差,不便于大规模集群
redis使用场景
热点数据加速查询
任务队列:秒杀,抢购,购票排队
即时信息查询,如排行榜,各类网站访问统计,在线人数,公交到站信息
时效性信息:验证码控制,投票机制
分布式数据共享
消息队列
分布式锁
相关操作命令
type key
del key
unlink key
非阻塞删除,先将key从keyspace元数据中删除,真正的删除在后面的异步操作
expire key
ttl key 查看还有多少时间通过期
exists key
keys pattren
select 1 切换到1号库
dbsize 查看当前数据库key的数量
flushdb清空当前库
flushall 清空全部库
debug object key 查看value的内部存储类型
info replication 查看主从复制配置信息
原子性操作
redis中命令都是单线程操作的,一旦一个指令开始执行不会被打断,这保证了原子性操作。
只有顶级key才可以设置过期时间
value数据类型
string
基本特性
二进制安全:不对字符串做任何解析,当做原始的字符串处理。比如c语言中就不是二进制安全的,会对字符串中的\0做解析,当做字符串的结尾
redis最基本的二级制类型
字符串value最多512m
命令
strlen
setnx
使用set指令可以同时设置过期时间和nx
setex
apend
incr
incrby
decrby
mset
msetnx
只有所有的key都不存在才会成功
getset
新值换旧值
内部存储数据结构
简单动态字符串 缩写SDS
可以修改的字符串,类似于java中的ArrayList,采用预分配冗余空间的方式减少内存的频繁分配
字符串少于1M,加倍扩容。超过1M,每次只扩容1M,最多512M
list
基本特性
单键多值
简单字符串列表
底层是双向链表实现
命令
lpush
从左边开始放
rpush
从右边开始放
lpop
从左边吐出值,get and remove操作
rpop
从右边吐出值
rpoplpush
队列1右边取出,队列2左边放入
lrange key 0 -1
仅查看列表所有值
lindex
按照索引下标获取元素
llen
列表长度
linsert key before/after value newvalue
在value前面/后面插入newvalue的值
lrem
从左边删除n个value值
lset key index value
将下标为index的值替换成value
ltrim
截取指定下标的列表,保留截取的部分
底层存储数据结构quiklist
当元素较少的情况下使用一块连续内存存储,ziplist压缩列表,空间紧凑,减少链表中使用指针的空间浪费。当数据量较多的时候,使用quicklist,每个节点都是一个ziplist。
set
基本特性
自动排重
string类型的无序集合
底层是一个value为null的hashtable表,所以删除,查找的时间复杂度都是O(1)
命令
sadd
smembers
取出该集合所有的值,不删除
sismember key value
集合中是否存在value值
scard
返回集合元素个数
srem key v1 v2
删除几个中的元素
spop
随机吐出一个值,从集合中删除
srandmember key n
从集合中随机取出n个值,不删
smove src desc value
把集合中的一个值从一个集合移动到另一个集合
sinter
返回几个交集
sunion
返回集合并集
sdiff key1 key2
返回差集,key1有的,key2中没有的
hash
基本特性
string的field和value的映射表
键值对集合
存储场景
命令
hset key field value field value
往集合添加值,测试发现也能批量
hget key field
hmset key field1 value1 field2 value2
批量设置
hexists
field是否存在
hkeys
hvals
hincrby key field increment
增加域的值
hsetnx key field value
当且仅当field不存在,可以设值成功
底层数据结构
当field-value长度较短且个数较少时,使用ziplist。否则使用hashtable
zset
基本特性
无重复元素
有序
与set的不同之处,每个元素都关联了一个评分 score,被用来按照从最低到最高的方式排序集合中的成员。成员唯一,但是评分可以重复
因为元素有序。可以按照评分或者次序按照范围获取元素
命令
zadd key score value
添加一个元素,同时添加score
zrange key start stop [withscores]
返回有序集合key中,指定下标内的元素
zrangebyscore key min max
指定分数范围内的元素
zrevrangebyscore key max min
从大到小进行排序输出
zincrby key socreIncr value
对应元素积分增加
zrem
删除指定元素
zcount
统计指定分数内的元素数
zrank
返回集合中的排名
zcard
返回集合中成员数量
底层存储数据结构
hash:作用就是关联元素和score,保障元素value的唯一性
跳跃表
给元素排序,根据score的范围获取元素
分层,有点二分法的意思
0 条评论
下一页