Redis(Remote Dictionary Server ),即远程字典服务
key-value存储系统
安装
1.下载安装包:https://redis.io/download/
2.解压redis的安装包
3.进入解压后的文件
4.基本环境安装yum install gcc-c++
make
5.默认安装路径:/usr/local/bin/
6.将redis配置文件复制到我们当前目录下
7.redis默认不是后台启动的,所以要修改后台文件daemonize no
改为daemonize yes
8.启动redis服务redis-server kconfig/redis.conf
9.测试:
连接默认端口:redis-cli -p 6379
(是手机按键MERZ对应的号码,意大利女歌手的名字,作者当时在追星)
1 | 127.0.0.1:6379> ping |
10.查看redis进程是否开启
1 | ps -ef|grep redis |
11.如何关闭服务shutdown
退出连接:exit
12.性能测试:redis-benchmark
https://www.runoob.com/redis/redis-benchmarks.html
基础知识:
redis默认有16个数据库
默认使用的是第0个数据库
可以用select进行数据库切换
如:select 3
打印数据库大小:DBSIZE
清空当前数据库:flushdb
清空全部数据库:FLASHALL
查看当前数据库所有的key:key *
获取对应key的内容:get [key]
判断某个key是否存在:EXISTS [key]
设置key为n秒过期:EXPIRE [key] n
,如:EXPIRE name 10
,然后用ttl name
查询还剩多久过期(以此来实现某些数据的限时缓存)
redis是单线程
redis是很快的,官方表示,redis是基于内存操作的,CPU不是redis的性能瓶颈,所以redis的瓶颈是根据及其的内存和网络带宽,既然不依赖cpu线程,就是用单线程了
字符串操作:
追加字符串:APPEND [key] "hello"
字符串长度:STRLEN [key]
后面的大部分字符串操作都和java差不错,要用的时候现查就行:https://www.runoob.com/redis/redis-strings.html
List类型操作:
在redis里实现栈、队列、阻塞队列
所有的list命令都是l开头的
插入数据LPUSH list one
LPUSH list two
查询数据LRANGE list 0 -1
(获取全部值)
1 | ```LRANGE list 1 1```(获取第二个值) |
geoadd 添加数据
geodist 返回两个给定位置之间的距离
geohash 返回geohash对位置进行的编码,用于内部调试,一般用不到
geopos 返回指定member的经纬度信息
georadius : 根据半径查找,需要给定中心点数据
georadiusbymember : 也是根据半径查找,但是中心点是已经存在的member
zrange 遍历member
zrem 移除member
1 |
|
pfadd 添加
pfcount 计数
pfmarge 合并 //合并到第一个key
1 |
|
setbit 中的offset是偏移量,可以看作下标,value只能是0或1
getbit
bitcount 统计key offset 为1的个数
bitpos 查看 key offset 为0或1的位置,并且可以设置range
```
事务操作:
(相当于先统一接收数据,然后统一执行,而不是接收一条执行一条)
Redis事务的本质是一组命令的集合,一次执行多个指令,事务中所有命令都被序列化,其他客户端提交的命令请求不会插入到事务执行命令序列中
顺序、排他、一次性
单条命令是原子性执行的,但事务不保证原子性,且没有回滚,事务中任意命令执行失败,其余命令仍会被执行
开启事务(multi)
命令入队(。。。)
执行事务(exec)
取消事务(discard)
监视、加锁(watch)
取消监视、解锁(unwatch)
锁
乐观锁应该适用于读多写少的情况,悲观锁应该适用于写多读少的情况
悲观锁
为了避免其他人同时修改,直接对数据进行加锁以防止并发,修改数据前锁定,修改的方式被称为悲观并发控制 Pessimistic Concurrency Control 悲观、并发、控制
独占性、排他性
在整个数据处理过程中,数据处于锁定状态
线程操作数据,对数据添加排他锁
假设最坏的情况,每次都默认其他线程更改数据
传统数据库使用的几种加锁机制,都是在操作之前上锁
行锁
表锁
读锁
写锁
在Java中同步用synchronized关键字实现
悲观锁住要分共享锁和排他锁
共享锁 shared locks
读锁、S锁,多个事务对同一个数据可以共享一把锁,都能访问数据,只能读不能修改
排他锁 exclusive locks
写锁、X锁、不能与其他锁并存,一个事务获取了数据行的排他锁,其他事物就不能再获得该行的其他锁,获取排他锁的事物可以对数据行读取和修改
悲观并发控制 先取锁再访问 的保守策略,开销大,还增加死锁的概率,降低并发性,其他事务必须等待
乐观锁
假设数据一般情况不会造成冲突,在数据提交更新时正式对数据的冲突与否进行检测,发现了冲突,发出错误信息,让用户决定如何处理,适用于读操作多的场景,可以提高程序的吞吐量
为了避免数据库的幻读,业务处理时间过长,乐观锁不会刻意使用数据库本身的锁机制,而是一句数据本身来保证数据的正确性
实现方法
CAS实现:java.util.concurrent.atomic 包下的原子变量
版本号控制,在数据表添加爱version字段,数据被修改时,version值+1,线程更新数据,同时读取version值,提交更新时,读取到的version值与当前数据库中的version值相等才更新,否则重试,直到更新成功
Redis缓存穿透、击穿、雪崩
都是服务的三高问题
高并发
高可用
高性能
面试高频,工作常用
redis缓存的使用极大的提升了应用程序的性能和效率,特别是数据查询方面,但同时,它也带来了一些问题,数据一致性问题,严格意义上来讲,问题无解,对一致性要求极高,不推荐使用缓存
布隆过滤器、缓存空对象
缓存穿透
用户查询一个数据,redis数据库中没有,也就是缓存没命中,于是向持久层数据库查询,发现也没有,于是查询失败,用户很多的时候,缓存都没有命中,都请求持久层数据库,给持久层数据库造成巨大压力,称为缓存穿透
在直达持久层的路径上加上过滤器、或者缓存中专门增加一个为空的请求
布隆过滤器
布隆过滤器是一种数据结构,对所有可能的查询参数以hash形式存储,在控制层进行校验,不符合则丢弃,从而避免了对底层存储系统查询压力
缓存空对象
当持久化层不命中后,将返回的空对象存储起来,同时设置一个过期时间,之后再访问这个数据就从缓存中获取,保护持久层数据源
需要面临的问题
存储空的key也需要空间
对空值设置了过期时间,还会存在缓存层和存储层的数据有一段时间窗口不一致,对于需要保持一致性的业务会有影响
缓存击穿
例子微博服务器热搜,巨大访问量访问同一个key
一个key非常热点,不停扛着大并发,集中对一个点进行访问,当个key失效的瞬间,持续大并发导致穿破缓存,直接请求数据库
某个key在过期的瞬间,大量的访问会同时访问数据库来查询最新的数据,并且回写缓存,导致数据库瞬间压力过大
解决方案
设置热点数据不过期
一直缓存也会浪费空间
加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程查询后端服务,其他线程没有获得分布式锁的权限,只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
缓存雪崩
在某一个时间段,缓存集中过期失效,redis宕机
产生雪崩的原因之一,设置缓存的存活时间较短,大并发访问时刚好都过期,直接访问了数据库,对数据库而言,会产生周期性压力波峰,暴增时数据库可能会宕机
双十一时会停掉一些服务,保证主要的一些服务可用,springcloud中说明过
解决方案:
增加集群中服务器数量
异地多活
限流降级
缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量,对某个key只允许一个线程查询数据和写缓存,其他线程等待
数据预热
正式部署之前,把可能的数据提前访问一遍,可能大量访问的数据就会加载到缓存中,加载不同的key,设置不同的过期时间,让缓存时间尽量均匀