Redis概述&下载与安装&使用redis

一、Redis概述

1.1 互联网架构的演变历程

  • 第一阶段

  • [ 1] 数据访问量不大,简单的架构即可搞定!
    请添加图片描述

  • 第二阶段

  • [ 1] 数据访问量大,使用缓存技术来缓解数据库的压力。

  • [ 2] 不同的业务访问不同的数据库

请添加图片描述

  • 第三阶段

  • [ 1] 主从读写分离。

  • [ 2] 之前的缓存确实能够缓解数据库的压力,但是写和读都集中在一个数据库上,压力又来了。

  • [ 3] 一个数据库负责写,一个数据库负责读。分工合作。愉快!

  • [ 4] 让master(主数据库)来响应事务性(增删改)操作,让slave(从数据库)来响应非事务性
    (查询)操作,然后再采用主从复制来把master上的事务性操作同步到slave数据库中

  • [ 5] mysql的master/slave就是网站的标配!
    请添加图片描述

  • 第四阶段

  • [ 1] mysql的主从复制,读写分离的基础上,mysql的主库开始出现瓶颈

  • [ 2] 由于MyISAM使用表锁,所以并发性能特别差

  • [ 3] 分库分表开始流行,mysql也提出了表分区,虽然不稳定,但我们看到了希望

  • [ 4] 开始吧,mysql集群

请添加图片描述

1.2 Redis入门介绍

  • 互联网需求的3高

  • 高并发,高可扩,高性能

  • Redis 是一种运行速度很快,并发性能很强,并且运行在内存上的NoSql(not only sql)数据库

  • NoSQL数据库 和 传统数据库 相比的优势

  • NoSQL数据库无需事先为要存储的数据建立字段,随时可以存储自定义的数据格式

  • 而在关系数据库里,增删字段是一件非常麻烦的事情。如果是非常大数据量的表,增加字段
    简直就是一个噩梦

  • Redis的常用使用场景

  • 缓存,毫无疑问这是Redis当今最为人熟知的使用场景。在提升服务器性能方面非常有效;一
    些频繁被访问的数据,经常被访问的数据如果放在关系型数据库,每次查询的开销都会很
    大,而放在redis中,因为redis 是放在内存中的可以很高效的访问

  • 排行榜,在使用传统的关系型数据库(mysql oracle 等)来做这个事儿,非常的麻烦,而利
    用Redis的SortSet(有序集合)数据结构能够简单的搞定;

  • 计算器/限速器,利用Redis中原子性的自增操作,我们可以统计类似用户点赞数、用户访问
    数等,这类操作如果用MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场
    景是限制某个用户访问某个API的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压
    力;

  • 好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好
    友、共同爱好之类的功能;

  • 简单消息队列,除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,
    比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,完全
    可以用List来完成异步解耦;

  • Session共享,以jsp为例,默认Session是保存在服务器的文件中,如果是集群服务,同一个
    用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用Redis保存Session后,无论
    用户落在那台机器上都能够获取到对应的Session信息。

二、下载与安装

2.1 下载
redis:http://www.redis.net.cn/
请添加图片描述
图形工具:https://redisdesktop.com/download
请添加图片描述
2.2 Linux下安装

  1. 上传tar.gz包,并解压
tar -zxvf redis-5.0.4.tar.gz
  1. 安装gcc(必须有网络)
yum -y install gcc

忘记是否安装过,可以使用 gcc -v 命令查看gcc版本,如果没有安装过,会提示命令不存在

  1. 进入redis目录,进行编译
make
  1. 编译之后,开始安装
make install

三 、安装后的基础操作

  1. 后台运行方式

redis默认不会使用后台运行,如果你需要,修改配置文件daemonize=yes,当你后台服务启动的
时候,会写成一个进程文件运行。

vim /opt/redis-5.0.4/redis.conf
daemonize yes

以配置文件的方式启动

cd /usr/local/bin redis-server /opt/redis-5.0.4/redis.conf
  1. 关闭数据库

单实例关闭

redis-cli shutdown

多实例关闭

redis-cli shutdown
  1. 常用操作

检测6379端口是否在监听

netstat -lntp | grep 6379

端口为什么是6379?
6379在是手机按键上MERZ对应的号码,
而MERZ取自意大利歌女Alessia Merz的名字。
MERZ长期以来被antirez(redis作者)及其朋友当作愚蠢的代名词。

检测后台进程是否存在

ps -ef|grep redis
  1. 连接redis并测试
redis-cli 
ping

测试存储数据

# 保存数据 
set k1 china 
# 获取数据 
get k1
  1. 测试性能

先 ctrl+c,退出redis客户端

redis-benchmark 

执行命令后,命令不会自动停止,需要我们手动ctrl+c停止测试

[root@localhost bin]# redis-benchmark 
====== PING_INLINE ====== 
# 1.8秒处理了10万个请求,性能要看笔记本的配置高低 
100000 requests completed in 1.80 seconds 
50 parallel clients 
3 bytes payload 
keep alive: 1 
87.69% <= 1 milliseconds 
99.15% <= 2 milliseconds 
99.65% <= 3 milliseconds 
99.86% <= 4 milliseconds 
99.92% <= 5 milliseconds 
99.94% <= 6 milliseconds 
99.97% <= 7 milliseconds 
100.00% <= 7 milliseconds 
 # 每秒处理的请求数量
55524.71 requests per second
  1. 默认16个数据库
vim /opt/redis-5.0.4/redis.conf
127.0.0.1:6379> get k1 # 查询k1 "china" 
127.0.0.1:6379> select 16 # 切换16号数据库 
(error) ERR DB index is out of range # 数据库的下标超出了范围 127.0.0.1:6379> select 15 # 切换15号数据库 
OK
127.0.0.1:6379[15]> get k1 # 查询k1
(nil) 
127.0.0.1:6379[15]> select 0 # 切换0号数据库 
OK
127.0.0.1:6379> get k1 # 查询k1 
"china"
  1. 数据库键的数量
dbsize
# redis在linux支持命令补全(tab)
  1. 清空数据库
    清空当前库
flushdb

清空所有(16个)库,慎用!!

flushall
  1. 模糊查询(key)
    模糊查询keys命令,有三个通配符:

*:通配任意多个字符

#查询所有的键
keys *
#模糊查询k开头,后面随便多少个字符
keys k*
#模糊查询e为最后一位,前面随便多少个字符
keys *e
#双 * 模式,匹配任意多个字符:查询包含k的键
keys *k*

?:通配单个字符

#模糊查询k字头,并且匹配一个字符
keys k?
#你只记得第一个字母是k,他的长度是3
keys k??

[]:通配括号内的某一个字符

#记得其他字母,就第二个字母可能是a或e
keys r[ae]dis
  1. 键(key)

exists key:判断某个key是否存在

127.0.0.1:6379> exists k1 
(integer) 1 # 存在 
127.0.0.1:6379> exists y1 
(integer) 0 # 不存在

move key db:移动(剪切,粘贴)键到几号库

127.0.0.1:6379> move x1 8 # 将x1移动到8号库 
(integer) 1 # 移动成功 
127.0.0.1:6379> exists x1 
# 查看当前库中是否存在x1 
(integer) 0 # 不存在(因为已经移走了) 
127.0.0.1:6379> select 8 # 切换8号库 
OK
127.0.0.1:6379[8]> keys * # 查看当前库中的所有键 
1) "x1"

ttl key:查看键还有多久过期(-1永不过期,-2已过期)
time to live 还能活多久

127.0.0.1:6379[8]> ttl x1 
(integer) -1 # 永不过期

expire key 秒:为键设置过期时间(生命倒计时)

127.0.0.1:6379[8]> set k1 v1 # 保存k1 
OK
127.0.0.1:6379[8]> ttl k1 # 查看k1的过期时间 
(integer) -1 # 永不过期 
127.0.0.1:6379[8]> expire k1 10 # 设置k1的过期时间为10秒(10秒后自动销毁) 
(integer) 1 # 设置成功 
127.0.0.1:6379[8]> get k1 # 获取k1 
"v1" 
127.0.0.1:6379[8]> ttl k1 # 查看k1的过期时间 
(integer) 2 # 还有2秒过期 
127.0.0.1:6379[8]> get k1 
(nil) 
127.0.0.1:6379[8]> keys * # 从内存中销毁了 
(empty list or set)

type key:查看键的数据类型

127.0.0.1:6379[8]> type k1 
string # k1的数据类型是会string字符串

四、Redis数据类型与底层数据结构

Redis简介
Redis是一个Key-Value的存储系统,使用ANSI C语言编写。
key的类型是字符串。
value的数据类型有:
常用的:string字符串类型、list列表类型、set集合类型、sortedset(zset)有序集合类型、hash类型。
不常见的:bitmap位图类型、geo地理位置类型。
Redis5.0新增一种:stream类型
注意:Redis中命令是忽略大小写,(set SET),key是不忽略大小写的 (NAME name)
string字符串类型
Redis的String能表达3种值的类型:字符串、整数、浮点数 100.01 是个六位的串
常见操作命令如下表:

命令名称命令格式命令描述
setset key赋值
getget key取值
getsetgetset key value取值并赋值
setnxsetnx key value当value不存在时采用赋值set key value NX PX 3000 原子操作,px 设置毫秒数
appendappend key value向尾部追加值
strlenstrlen key获取字符串长度
incrincr key递增数字
incrbyincrby key increment增加指定的整数
decrdecr key递减数字
decrbydecrby key decrement减少指定的整数

应用场景:
1、key和命令是字符串
2、普通的赋值
3、incr用于乐观锁
incr:递增数字,可用于实现乐观锁 watch(事务) 4、setnx用于分布式锁
当value不存在时采用赋值,可用于实现分布式锁
list列表类型
list列表类型可以存储有序、可重复的元素
获取头部或尾部附近的记录是极快的
list的元素个数最多为2^32-1个(40亿)
常见操作命令如下表:

命令名称命令格式命令描述
lpushlpush key v1 v2 v3 …从左侧插入列表
lpoplpop key从列表左侧取出
rpushrpush key v1 v2 v3 …从右侧插入列表
rpoprpop key从列表右侧取出
lpushxlpushx keyvalue将值插入到列表头部
rpushxrpushx keyvalue将值插入到列表尾部
blpopblpop key timeout从列表左侧取出,当列表为空时阻塞,可以设置最大阻塞时间,单位为秒
brpopblpop key timeout从列表右侧取出,当列表为空时阻塞,可以设置最大阻塞时间,单位为秒
llenllen key获得列表中元素个数
lindexlindex key index获得列表中下标为index的元素 index从0开始
lrangelrange key start end返回列表中指定区间的元素,区间通过start和end指定
lremlrem key count value删除列表中与value相等的元素当count>0时, lrem会从列表左边开始删除;当count<0时,lrem会从列表后边开始删除;当count=0时, lrem删除所有值为value的元素
lsetlset key index value将列表index位置的元素设置成value的值
ltrimltrim key start end对列表进行修剪,只保留start到end区间
rpoplpushrpoplpush key1 key2从key1列表右侧弹出并插入到key2列表左侧
brpoplpushbrpoplpushkey1 key2从key1列表右侧弹出并插入到key2列表左侧,会阻塞
linsertlinsert keyBEFORE/AFTERpivot value将value插入到列表,且位于值pivot之前或之后

应用场景:
1、作为栈或队列使用
列表有序可以作为栈和队列使用
2、可用于各种列表,比如用户列表、商品列表、评论列表等。
set集合类型
Set:无序、唯一元素
集合中最大的成员数为 2^32 - 1
常见操作命令如下表:

命令名称命令格式命令描述
saddsadd key mem1 mem2 …为集合添加新成员
sremsrem key mem1 mem2 …删除集合中指定成员
smemberssmembers key获得集合中所有元素
spopspop key返回集合中一个随机元素,并将该元素删除
srandmembersrandmember key返回集合中一个随机元素,不会删除该元素
scardscard key获得集合中元素的数量
sismembersismember key member判断元素是否在集合内
sintersinter key1 key2 key3求多集合的交集
sdiffsdiff key1 key2 key3求多集合的差集
sunionsunion key1 key2 key3求多集合的并集

应用场景:
适用于不能重复的且不需要顺序的数据结构
比如:关注的用户,还可以通过spop进行随机抽奖

sortedset有序集合类型
SortedSet(ZSet) 有序集合: 元素本身是无序不重复的
每个元素关联一个分数(score)
可按分数排序,分数可重复
常见操作命令如下表:

命令名称命令描述
zaddzadd key score1 member1 score2 member2 …为集合添加新成员
zremzrem key mem1 mem2 … .删除集合中指定成员
zcardsmembers key删除有序集合中指定成员
zcountzcount key min max返回集合中score值在[min,max]区间的元素数量
zincrbyzincrby key increment member在集合的member分值上加increment
zscorezscore key member获得集合中member的分值
zrankzrank key member获得集合中member的排名(按分值从小到大)
zrevrankzrevrank key member获得集合中member的排名(按分值从大到小)
zrangezrange key start end获得集合中指定区间成员,按分数递增排序
zrevrangezrevrange key start end获得集合中指定区间成员,按分数递减排序

应用场景:
由于可以按照分值排序,所以适用于各种排行榜。比如:点击排行榜、销量排行榜、关注排行榜等。
hash类型(散列表)
Redis hash 是一个 string 类型的 field 和 value 的映射表,它提供了字段和字段值的映射。
每个 hash 可以存储 2^32 - 1 键值对(40多亿)
在这里插入图片描述
常见操作命令如下表:

命令名称命令格式命令描述
hsethset key field value赋值,不区别新增或修改
hmsethmset key field1 value1 field2 value2批量赋值
hsetnxhsetnx key field value赋值,如果filed存在则不操作
hexistshexists key filed查看某个field是否存在
hgethget key field获取一个字段值
hmgethmget key field1 field2 …获取多个字段值
hgetallhgetall key
hdelhdel key field1 field2…删除指定字段
hincrbyhincrby key field increment指定字段自增increment
hlenhlen key获得字段数量

应用场景:
对象的存储 ,表数据的映射
bitmap位图类型
bitmap是进行位操作的
通过一个bit位来表示某个元素对应的值或者状态,其中的key就是对应元素本身。
bitmap本身会极大的节省储存空间。
常见操作命令如下表:

命令名称命令格式命令描述
setbitsetbit key offset value设置key在offset处的bit值(只能是0或者1)。
getbitgetbit key offset获得key在offset处的bit值
bitcountbitcount key获得key的bit位为1的个数
bitposbitpos key value返回第一个被设置为bit值的索引值
bitopbitop and[or/xor/not] destkey key [key …]对多个key 进行逻辑运算后存入destkey中

应用场景:
1、用户每月签到,用户id为key , 日期作为偏移量 1表示签到
2、统计活跃用户, 日期为key,用户id为偏移量 1表示活跃
3、查询用户在线状态, 日期为key,用户id为偏移量 1表示在线
geo地理位置类型
geo是Redis用来处理位置信息的。在Redis3.2中正式使用。主要是利用了Z阶曲线、Base32编码和
geohash算法
Z阶曲线
在x轴和y轴上将十进制数转化为二进制数,采用x轴和y轴对应的二进制数依次交叉后得到一个六位数编
码。把数字从小到大依次连起来的曲线称为Z阶曲线,Z阶曲线是把多维转换成一维的一种方法。
在这里插入图片描述
Base32编码
Base32这种数据编码机制,主要用来把二进制数据编码成可见的字符串,其编码规则是:任意给定一
个二进制数据,以5个位(bit)为一组进行切分(base64以6个位(bit)为一组),对切分而成的每个组进行编
码得到1个可见字符。Base32编码表字符集中的字符总数为32个(0-9、b-z去掉a、i、l、o),这也是
Base32名字的由来。
在这里插入图片描述
geohash算法
Gustavo在2008年2月上线了geohash.org网站。Geohash是一种地理位置信息编码方法。 经过
geohash映射后,地球上任意位置的经纬度坐标可以表示成一个较短的字符串。可以方便的存储在数据
库中,附在邮件上,以及方便的使用在其他服务中。以北京的坐标举例,[39.928167,116.389550]可以转换成 wx4g0s8q3jf9 。
Redis中经纬度使用52位的整数进行编码,放进zset中,zset的value元素是key,score是GeoHash的
52位整数值。在使用Redis进行Geo查询时,其内部对应的操作其实只是zset(skiplist)的操作。通过zset
的score进行排序就可以得到坐标附近的其它元素,通过将score还原成坐标值就可以得到元素的原始坐
标。
常见操作命令如下表:

命令名称命令格式命令描述
geoaddgeoadd key 经度 纬度 成员名称1 经度1 纬度1 成员名称2 经度2 纬度 2 …添加地理坐标
geohashgeohash key 成员名称1 成员名称2…返回标准的geohash串
geoposgeopos key 成员名称1 成员名称2…返回成员经纬度
geodistgeodist key 成员1 成员2 单位计算成员间距离
georadiusbymembergeoradiusbymember key 成员值单位 count 数asc[desc]根据成员查找附近的成员

应用场景:
1、记录地理位置
2、计算距离
3、查找"附近的人
举例:

127.0.0.1:6379> geoadd user:addr 116.31 40.05 zhangf 116.38 39.88 zhaoyun 116.47 40.00 diaochan #添加用户地址 zhangf、zhaoyun、diaochan的经纬度 
(integer) 3 
127.0.0.1:6379> geohash user:addr zhangf diaochan #获得zhangf和diaochan的geohash码 
1) "wx4eydyk5m0" 
2) "wx4gd3fbgs0" 
127.0.0.1:6379> geopos user:addr zhaoyun #获得zhaoyun的经纬度 
1) 1) "116.38000041246414185" 
   2) "39.88000114172373145" 127.0.0.1:6379> geodist user:addr zhangf diaochan #计算zhangf到diaochan的距离,单位是m
"14718.6972" 
127.0.0.1:6379> geodist user:addr zhangf diaochan km #计算zhangf到diaochan的距离, 单位是km 
"14.7187" 
127.0.0.1:6379> geodist user:addr zhangf zhaoyun km 
"19.8276" 
127.0.0.1:6379> georadiusbymember user:addr zhangf 20 km withcoord withdist 
count 3 asc 
# 获得距离zhangf20km以内的按由近到远的顺序排出前三名的成员名称、距离及经纬度 
#withcoord : 获得经纬度 withdist:获得距离 withhash:获得geohash码 
1) 1) "zhangf" 
   2) "0.0000" 
   3) 1) "116.31000012159347534" 
      2) "40.04999982043828055" 
2) 1) "diaochan"
   2) "14.7187" 
   3) 1) "116.46999925374984741" 
      2) "39.99999991084916218" 
3) 1) "zhaoyun"
   2) "19.8276" 
   3) 1) "116.38000041246414185" 
      2) "39.88000114172373145"

stream数据流类型
stream是Redis5.0后新增的数据结构,用于可持久化的消息队列。
几乎满足了消息队列具备的全部内容,包括:
消息ID的序列化生成
消息遍历
消息的阻塞和非阻塞读取
消息的分组消费
未完成消息的处理
消息队列监控
每个Stream都有唯一的名称,它就是Redis的key,首次使用 xadd 指令追加消息时自动创建。
常见操作命令如下表:

命令名称命令格式命令描述
xaddxadd key id <*> field1 value1…将指定消息数据追加到指定队列(key)中,*表示最新生成的id(当前时间+序列号)
xreadxread [COUNT count] [BLOCKmilliseconds] STREAMS key[key …] ID [ID …]从消息队列中读取,COUNT:读取条数,BLOCK:阻塞读(默认不阻塞)key:队列名称 id:消息id
xrangexrange key start end [COUNT]读取队列中给定ID范围的消息 COUNT:返回消息条数(消息id从小到大)
xrevrangexrevrange key start end [COUNT]读取队列中给定ID范围的消息 COUNT:返回消息条数(消息id从小到小)
xdelxdel key id删除队列的消息
xgroupxgroup create key groupname id创建一个新的消费组
xgroupxgroup destory key groupname删除指定消费组
xgroupxgroup delconsumer key groupname cname删除指定消费组中的某个消费者
xgroupxgroup setid key id修改指定消息的最大id
xreadgroupxreadgroup group groupname consumer COUNT streams key从队列中的消费组中创建消费者并消费数据(consumer不存在则创建)

五、Redis相关面试题

缓存雪崩、缓存穿透、缓存击穿
缓存穿透:key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会到数据源,从⽽可能压垮数据源。⽐如⽤⼀个不存在的⽤户id获取⽤户信息,不论缓存还是数据库都没有,若⿊客利⽤此漏洞进⾏攻击可能压垮数据库。不存在的key。

  • 缓存穿透解决方案:⼀个⼀定不存在缓存及查询不到的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存
    储层查不到数据则不写⼊缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存
    的意义。
    有很多种⽅法可以有效地解决缓存穿透问题,最常⻅的则是采⽤布隆过滤器,将所有可能存在的数据哈
    希到⼀个⾜够⼤的bitmap中,⼀个⼀定不存在的数据会被 这个bitmap拦截掉,从⽽避免了对底层存储
    系统的查询压⼒。另外也有⼀个更为简单粗暴的⽅法(我们采⽤的就是这种),如果⼀个查询返回的数
    据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进⾏缓存,但它的过期时间会很
    短,最⻓不超过五分钟。
// 伪代码
public object GetProductListNew() {
 int cacheTime = 30;
 String cacheKey = "product_list";
 String cacheValue = CacheHelper.Get(cacheKey);
 if (cacheValue != null) {
 return cacheValue;
 }
 cacheValue = CacheHelper.Get(cacheKey);
 if (cacheValue != null) {
 return cacheValue;
 } else {
 //数据库查询不到,为空
 cacheValue = GetProductListFromDB();
 if (cacheValue == null) {
 //如果发现为空,设置个默认值,也缓存起来
 cacheValue = string.Empty;
 }
 CacheHelper.Add(cacheKey, cacheValue, cacheTime);
 return cacheValue;
 }
}

缓存击穿:key对应的数据存在,但在redis中过期,此时若有⼤量并发请求过来,这些请求发现缓存过期⼀般都会从后端DB加载数据并回设到缓存,这个时候⼤并发的请求可能会瞬间把后端DB压垮。一个key或一些key 热点key。

  • 缓存击穿解决⽅案
    key可能会在某些时间点被超⾼并发地访问,是⼀种⾮常“热点”的数据。这个时候,需要考虑⼀个问题:
    缓存被“击穿”的问题。
    使⽤互斥锁(mutex key)
    业界⽐较常⽤的做法,是使⽤mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是⽴即去load db,⽽是先使⽤缓存⼯具的某些带成功操作返回值的操作(⽐如Redis的SETNX或者Memcache的ADD)去set⼀个mutex key,当操作返回成功时,再进⾏load db的操作并回设缓存;否则,就重试整个get缓存的⽅法。
    SETNX,是「SET if Not eXists」的缩写,也就是只有不存在的时候才设置,可以利⽤它来实现锁的效果。
// 伪代码
public String get(key) {
 String value = redis.get(key);
 if (value == null) { //代表缓存值过期
 //设置3min的超时,防⽌del操作失败的时候,下次缓存过期⼀直不能load db
 if (redis.setnx(key_mutex, 1, 3 * 60) == 1) { //代表设置成功
 value = db.get(key);
 redis.set(key, value, expire_secs);
 redis.del(key_mutex);
 } else { //这个时候代表同时候的其他线程已经load db并回设到缓存了,这时候重试获取缓
存值即可
 sleep(50);
 get(key); //重试
 }
 } else {
 return value; 
 }
 }

缓存雪崩:当缓存服务器重启或者⼤量缓存集中在某⼀个时间段失效,这样在失效的时候,也会给后端系统(⽐如DB)带来很⼤压⼒。大量的key失效。

  • 缓存雪崩解决⽅案
    缓存失效时的雪崩效应对底层系统的冲击⾮常可怕!⼤多数系统设计者考虑⽤加锁或者队列的⽅式保证来保证不会有⼤量的线程对数据库⼀次性进⾏读写,从⽽避免失效时⼤量的并发请求落到底层存储系统上。还有⼀个简单⽅案就时讲缓存失效时间分散开,⽐如我们可以在原有的失效时间基础上增加⼀个随机值,⽐如1-5分钟随机,这样每⼀个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。
    加锁排队只是为了减轻数据库的压⼒,并没有提⾼系统吞吐量。假设在⾼并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致⽤户等待超时,这是个治标不治本的⽅法!
    注意:加锁排队的解决⽅式分布式环境的并发问题,有可能还要解决分布式锁的问题;线程还会被阻塞,⽤户体验很差!因此,在真正的⾼并发场景下很少使⽤
// 加锁排队伪代码
public object GetProductListNew() {
 int cacheTime = 30;
 String cacheKey = "product_list";
 String lockKey = cacheKey;
 String cacheValue = CacheHelper.get(cacheKey);
 if (cacheValue != null) {
 return cacheValue;
 } else {
 synchronized(lockKey) {
 cacheValue = CacheHelper.get(cacheKey);
 if (cacheValue != null) {
 return cacheValue;
 } else {
 //这⾥⼀般是sql查询数据
 cacheValue = GetProductListFromDB();
 CacheHelper.Add(cacheKey, cacheValue, cacheTime);
 }
 }
 return cacheValue;
 }
}

单线程的redis为什么这么快

  • redis在内存中操作,持久化只是数据的备份,正常情况下内存和硬盘不会频繁swap
  • 多机主从,集群数据扩展
  • maxmemory的设置+淘汰策略
  • 数据结构简单,有压缩处理,是专门设计的
  • 单线程没有锁,没有多线程的切换和调度,不会死锁,没有性能消耗
  • 使用I/O多路复用模型,非阻塞IO;
  • 构建了多种通信模式,进一步提升性能
  • 进行持久化的时候会以子进程的方式执行,主进程不阻塞

Redis 为什么是单线程的,优点

  • Redis采用单线程多进程集群方案
  • Redis是基于内存的操作,CPU不是Redis的瓶颈
  • 瓶颈最有可能是机器内存的大小或者网络带宽
  • 单线程的设计是最简单的但是对多核CPU利用率不够,所以Redis6采用多线程。

单线程优点:

  • 代码更清晰,处理逻辑更简单 不用去考虑各种锁的问题,
  • 不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
  • 不存在多进程或者多线程导致的切换而消耗CPU

有没有尝试进行多机redis 的部署?如何保证数据一致的?

  • redis多机部署方案:Redis主从+哨兵、codis集群、RedisCluster
  • 多机: 高可用、高扩展、高性能
  • 三者的区别,适用场景
  • 数据一致性指的是主从的数据一致性
  • Redis是AP模型,主从同步有时延。所以不能保证主从数据的时时一致性,只能保证数据最终一致性
  • 保证数据一致性方案:
    1、忽略
    如果业务能够允许短时间不同步就忽略,比如:搜索,消息,帖子,职位
    2、强制读主库,从库只做备份使用
    使用一个高可用主库提供数据库服务
    读和写都落到主库上
    采用缓存来提升系统读性能
    3、选择性读主库
    写主库时将哪个库,哪个表,哪个主键三个信息拼装一个key设置到cache里
    读时先在cache中查找:
    cache里有这个key,说明1s内刚发生过写请求,数据库主从同步可能还没有完成,此时就应该去主库查询cache里没有这个key,说明最近没有发生过写请求,此时就可以去从库查询
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值