6. Redis6新数据类型
1. Bitmaps
- 合理使用操作位可以有效地提高内存使用率和开发使用率
- 本身是一个字符串,不是数据类型,数组的每个单元只能存放0和1,数组的下标在Bitmaps叫做偏移量
- 节省空间,一般存储活跃用户比较多
127.0.0.1:6379> setbit users:20210101 1 1 (integer) 0 127.0.0.1:6379> setbit users:20210101 6 1 (integer) 0 127.0.0.1:6379> setbit users:20210101 11 1 (integer) 0 127.0.0.1:6379> setbit users:20210101 15 1 (integer) 0 127.0.0.1:6379> setbit users:20210101 19 1 (integer) 0 127.0.0.1:6379> getbit users:20210101 0 (integer) 0 127.0.0.1:6379> getbit users:20210101 1 (integer) 1 127.0.0.1:6379> bitcount users:20210101 (integer) 5
取交集(按位与):
2020-11-04日访问网站的userid=1,2,5,9
2020-11-03日访问网站的userid=0.1.4.9
127.0.0.1:6379> setbit unique:users:20201104 1 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201104 2 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201104 5 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201104 9 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201103 0 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201103 1 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201103 4 1 (integer) 0 127.0.0.1:6379> setbit unique:users:20201103 9 1 (integer) 0 127.0.0.1:6379> bitop and unqiue:users:and:20201104_03 setbit unique:users:20201103 setbit unique:users:20201104 (integer) 2
2. HyperLogLog
- 统计网页中页面访问量
- 只会根据输入元素来计算基数,而不会储存输入元素本身,不能像集合那样,返回输入的各个元素
- 基数估计是在误差可接受的范围内,快速计算(不重复元素的结算)
127.0.0.1:6379> pfadd program "java" (integer) 1 127.0.0.1:6379> pfadd program "php" (integer) 1 127.0.0.1:6379> pfadd program "java" //重复元素不添加 (integer) 0 127.0.0.1:6379> pfadd program "java" "c++" "mysql" (integer) 1 127.0.0.1:6379> pfcount program (integer) 4
127.0.0.1:6379> pfadd k1 "a" "b" (integer) 1 127.0.0.1:6379> pfcount k1 (integer) 2 127.0.0.1:6379> pfadd k2 "1" "11" "111" (integer) 1 127.0.0.1:6379> pfcount k2 (integer) 3 //合并 127.0.0.1:6379> pfmerge kres k1 k2 OK 127.0.0.1:6379> pfcount kres (integer) 5
3. Geospatial
提供经纬度设置,查询范围,距离查询等
两极无法直接添加,有效的经度从-180到180度;有效的纬度从-85.05112878到85.05112878度
//插入数据 127.0.0.1:6379> geoadd china:city 141.47 31.23 shanghai (integer) 1 127.0.0.1:6379> geoadd china:city 103.50 29.63 chongqing 114.05 22.52 shenzhen 116.38 39.90 beijing (integer) 3 127.0.0.1:6379> geopos china:city shanghai 1) 1) "141.47000044584274292" 2) "31.22999903975783553" //获取两城市之间的距离 127.0.0.1:6379> geodist china:city beijing shanghai km "2455.6228" 127.0.0.1:6379> geodist china:city beijing chongqing km "1636.7270" //获取指定经纬度一定范围内的城市 127.0.0.1:6379> georadius china:city 110 30 1000 km 1) "chongqing" 2) "shenzhen"
10. Redis事务_锁机制
Redis事务
Redis事务是一个单独的隔离操作,事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用就是串联多个命令防止别的命令插队。
mUlti,exec,discard
127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set key1 value1 QUEUED 127.0.0.1:6379(TX)> set key2 value2 QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) OK 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set a1 v1 QUEUED 127.0.0.1:6379(TX)> set a2 v2 QUEUED 127.0.0.1:6379(TX)> discard OK
情况一:组队的时候有一个命令出错,执行时整个的所有队列都会被取消
127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set b1 v1 QUEUED 127.0.0.1:6379(TX)> set b2 v2 QUEUED 127.0.0.1:6379(TX)> set b3 (error) ERR wrong number of arguments for 'set' command 127.0.0.1:6379(TX)> exec (error) EXECABORT Transaction discarded because of previous errors.
情况二:组队的时候没有失败,执行的时候有一个命令失败,其他的命令都会执行,不会回滚。
127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set c1 v1 QUEUED 127.0.0.1:6379(TX)> incr c1 QUEUED 127.0.0.1:6379(TX)> set c2 v2 QUEUED 127.0.0.1:6379(TX)> exec 1) OK 2) (error) ERR value is not an integer or out of range 3) OK
事务的冲突问题
悲观锁
乐观锁
每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
抢票就是典型的乐观锁场景。
Redis事务三特性
单独的隔离操作
- 事务中的所有命令都会序列化,按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
没有隔离级别的概念
- 队列中的命令没有提交之前不会实际被执行,因为事务提交前任何指令都不会被实际执行
不保证原子性
- 事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
12. Redis持久化之RDB
Redis提供了2个不同形式的持久化方式
- RDB(Redis DataBase)
- AOF(Append Of File)
RDB:在指定的时间间隔内将内存中的数据集快照写入磁盘中,Snapshot快照,它恢复时是将快照文件直接读到内存里。
备份是如何执行的?
- Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能,如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。
- RDB的缺点是最后一次持久化的数据可能丢失。
读时共享,写时复制。
13. Redis持久化之AOF
AOF是什么?
- Append Only File,以日志的形式来记录每个写操作(增量保存),将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件将写指令从前到后执行一次以完成数据的恢复工作。
- AOF默认不开启,RDB默认开启
- appendonly no改为yes开启
- 如果同时开启AOF和RDB,系统默认取AOF的数据。
redis-check-aof --fix appendonly.aof
可以修复aof文件
同步频率设置:
- appendfsync always:始终同步,每次redis的写入都会立刻记入日志;性能较差但数据完整性比较好
- annendfsync everysec:每秒同步,每秒写入日志一次,如果宕机,本秒的数据可能丢失。
- appendfsync np:redis不主动进行同步,把同步时机交给操作系统
rewrite压缩
什么时候重写:
Redis会记录上次重写时的AOF大小,默认配置是当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
重写虽然可以节约大量磁盘空间,减少恢复时间。但是每次重写还是有一定的负担的,因此设定Redis要满足一定条件才会进行重写。
auto-aof-rewrite-percentage
:设置重写的基准值,文件达到100%时开始重写(文件是原来重写后文件的2倍时触发)
auto-aof-rewrite-min-size
:设置重写的基准值,最小文件64MB。达到这个值开始重写
AOF持久化流程:
- 客户端的请求写命令会被append追加到AOF缓冲区中
- AOF缓冲区根据AOF持久化策略[always, everysec, no]将操作sync同步到磁盘的AOF文件中
- AOF文件大小超过重写策略或手动重写时,会对AOF文件rewrite重写,压缩AOF文件容器
- Redis服务重启时,会重新load加载AOF文件的写操作达到数据恢复的目的。
优点:
- 备份机制更稳健,丢失数据概率更低
- 可读的日志文本,通过操作AOF稳健,可以处理误操作
缺点:
- 比起RDB占用更多的磁盘空间。
- 恢复备份速度要慢。
- 每次读写都同步的话,有一定的性能压力。
- 存在个别Bug,造成恢复不能
哪个好?
官方推荐两个都启动。如果对数据不敏感,可以单独选RDB;不建议单独用AOF,因为可能会出现bug。如果只是做纯内存缓存,可以都不用。
14. Redis主从复制
一般是一主多从
是什么?
- 主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主
能干什么?
- 读写分离;容灾的快速恢复
怎么搭建一主多从?
【尚硅谷】Redis 6 入门到精通 超详细 教程_哔哩哔哩_bilibili
一主二仆
info replication
可以看信息
slaveof IP 端口
9可以设置从服务器
redis-server redis.conf
启动服务器端
redis-cli -p 端口
启动客户端
ps -ef | grep redis
:查看redis进程
主机能读能写,从机只能读
从服务器挂掉后,重启变成了主服务器
该重新设置为从服务器,能看到挂掉那段时间主服务器添加的数据
主服务器挂掉后,从服务器还是从服务器,也知道主服务器挂掉了
主服务器重新启动后,还是主服务器,数据不丢失
- 当从服务器连接上主服务器之后,从服务器向主服务器发送进行数据同步的消息。
- 主服务器接到从服务器发送过来的同步消息,把主服务器数据进行持久化,rdb文件,把rdb文件发送给从服务器,从服务器拿到rdb进行读取。
- 每次主服务器进行写操作之后,和从服务器进行数据同步。(主服务器主动做的)
全量复制/增量复制
薪火相传
反客为主
salveof no one
将从机变为主机
非自动
哨兵模式(sentinel)
反客为主的自动版
主要是为了监控主机宕机之后,从机可以立马变为主机,就和上面的反客为主一样,但不用手动设置。
能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
15 Redis集群
容量不够,redis如何进行扩容?
并发写操作,redis如何进行分摊?
什么是集群?
- redis集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。
- redis集群通过分区(partition)来提供一定程度的可用性(availability):即使集群中有一部分节点失效或者无法进行通讯,集群也可以继续处理命令请求。
创建redis集群:【尚硅谷】Redis 6 入门到精通 超详细 教程_哔哩哔哩_bilibili
16. 应用问题
1. 缓存穿透
2. 缓存击穿
key对应的数据存在,但在redis中过期,此时若有大量并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端DB压垮
也就是一个key过期,一直访问数据库
解决方案:
key可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑一个问题:缓存被“击穿”的问题。
(1)预先设置热门数据:在redis高峰访问之前,把一些热门数据提前存入到redis里面,加大这些热门数据key的时长
(2)实时调整:现场监控哪些数据热门,实时调整key的过期时长
(3)使用锁:先判断值是否为空再让他进来与否
总结如下:
- 设置热门的key,加大时长过期
- 实时监控调整
3. 缓存雪崩
解决方案:
(1)构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
(2)使用锁或队列:用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
(3)设置过期标志更新缓存:记录缓存数据是否过期(设置提前量),如果过期会触发通知另外的线程在后台去更新实际key的缓存。
(4)将缓存失效时间分散开:比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
总结如下:
- 设置多个级别的缓存架构,时间来得及缓冲
- 使用锁的机制
- 设置一个过期时间标志来通知
- 将过期时间分散,比如5分钟、5.01分钟等
4. 分布式锁
由于分布式系统多线程、多进程并且分布在不同机器上,这将使原单机部署情况下的并发控制锁策略失效,单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问
也就是在这个机器上了锁,另外一个机器也要可以识别到这个锁,也就是共享锁,都是同一把锁
解决方案如下:
- 基于数据库实现分布式锁
- 基于缓存(Redis等)
- 基于Zookeeper
设置锁:setnx
释放锁:del
设置key的过期时间自动释放:exprie
zdb@zdb-virtual-machine:~$ redis-server /etc/redis.conf zdb@zdb-virtual-machine:~$ /usr/local/bin/redis-cli 127.0.0.1:6379> setnx users 10 (integer) 1 127.0.0.1:6379> setnx users 20 (integer) 0 127.0.0.1:6379> del users (integer) 1 127.0.0.1:6379> setnx users 10 (integer) 1 127.0.0.1:6379> expire users 10 (integer) 1 127.0.0.1:6379> ttl users (integer) 4 127.0.0.1:6379> ttl users (integer) -2 //即上锁又设置过期时间 127.0.0.1:6379> set users 10 nx ex 12 OK 127.0.0.1:6379> ttl users (integer) 3 127.0.0.1:6379> ttl users (integer) -2
UUID防止误删
lua脚本保证原子性
17. Redis6.0新功能
1. ACL
权限控制
acl list
命令展现用户权限列表
acl cat
,查看添加权限指令类别
acl whoami
命令查看当前用户
acl set user
命令创建和编辑用户ACL
127.0.0.1:6379> acl list 1) "user default on nopass ~* &* +@all" 127.0.0.1:6379> acl cat 1) "keyspace" 2) "read" 3) "write" 4) "set" 5) "sortedset" 6) "list" 7) "hash" 8) "string" 9) "bitmap" 10) "hyperloglog" 11) "geo" 12) "stream" 13) "pubsub" 14) "admin" 15) "fast" 16) "slow" 17) "blocking" 18) "dangerous" 19) "connection" 20) "transaction" 21) "scripting" 127.0.0.1:6379> acl whoami "default"
2. IO多线程
单线程+IO多路复用
redis是单线程
多线程IO默认是不开启的,需要在配置文件中设置
io-threads-do-reads yes
io-therads 4