Redis(狂神说 )学习使用笔记

Redis(Remote Dictionary Server )学习使用笔记

NoSQl概述

Not only sql(非关系型数据库)

为什么要用nosql?

传统的关系型数据库虽然得到了广泛的应用,但是它依旧有自己无法克服的瓶颈问题,当遇到如下问题,sql就显得非常吃力:

每秒上万次的读写请求,硬盘IO此时也将变为性能瓶颈面对海量数据,势必涉及到分库分表,难以维护。大数据查询SQL效率极低,数据量到达一定程度时,查询时间会呈指数级别增长难以横向扩展,无法简单地通过增加硬件、服务节点来提高系统性能这个时候NoSql就显现出了它的威力。

很多的数据类型用户的个人信息,社交网络,地理位置。这些数据类型的存储不需要一个固定的格式!

NoSQL的优点:

1.海量数据下,读写性能优异

2.数据模型灵活

3.数据间无关系,易于扩展

1 键值数据库

相关产品:Redis、Riak、SimpleDB、Chordless、Scalaris、Memcached

应用:内容缓存

优点:扩展性好、灵活性好、大量写操作时性能高

缺点:无法存储结构化信息、条件查询效率较低

使用者:百度云(Redis)、GitHub(Riak)、BestBuy(Riak)、Twitter(Ridis和Memcached)

2 列族数据库

相关产品:BigTable、HBase、Cassandra、HadoopDB、GreenPlum、PNUTS

应用:分布式数据存储与管理

优点:查找速度快、可扩展性强、容易进行分布式扩展、复杂性低

使用者:Ebay(Cassandra)、Instagram(Cassandra)、NASA(Cassandra)、Facebook(HBase)

3 文档数据库

相关产品:MongoDB(MongoDB是一个介于关系型数据库和非关系型数据中中间的产品! MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的)

、CouchDB、ThruDB、CloudKit、Perservere、Jackrabbit

应用:存储、索引并管理面向文档的数据或者类似的半结构化数据

优点:性能好、灵活性高、复杂性低、数据结构灵活

缺点:缺乏统一的查询语言

使用者:百度云数据库(MongoDB)、SAP(MongoDB)

4 图形数据库

不是存图片的的,存拓扑关系等。

相关产品:Neo4J、OrientDB、InfoGrid、GraphDB

应用:大量复杂、互连接、低结构化的图结构场合,如社交网络、推荐系统等

优点:灵活性高、支持复杂的图形算法、可用于构建复杂的关系图谱

缺点:复杂性高、只能支持一定的数据规模

使用者:Adobe(Neo4J)、Cisco(Neo4J)、T-Mobile(Neo4J)

KV键值对

分类Examples举例典型应用场景数据模型优点缺点
键值(key-value)Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB内容缓存,主要用于处理大量数据的高访问负载,也用于一些日志系统等等。Key 指向 Value 的键值对,通常用hash table来实现查找速度快数据无结构化,通常只被当作字符串或者二进制数据
列存储数据库Cassandra, HBase, Riak分布式的文件系统以列簇式存储,将同一列数据存在一起查找速度快,可扩展性强,更容易进行分布式扩展功能相对局限
文档型数据库CouchDB, MongoDbWeb应用(与Key-Value类似,Value是结构化的,不同的是数据库能够了解Value的内容)Key-Value对应的键值对,Value为结构化数据数据结构要求不严格,表结构可变,不需要像关系型数据库一样需要预先定义表结构查询性能不高,而且缺乏统一的查询语法。
图形(Graph)数据库Neo4J, InfoGrid, Infinite Graph社交网络,推荐系统等。专注于构建关系图谱图结构利用图结构相关算法。比如最短路径寻址,N度关系查找等很多时候需要对整个图做计算才能得出需要的信息,而且这种结构不太好做分布式的集群方案。
传统的关系型数据库RDBMS与nosql

RDBMS:

  • sQL

  • -结构化组织

  • -数据和关系都存在单独的表中-操作操作,数据定义语言-

  • 严格的一致性

  • -基础的事务

    nosql:

    不仅仅是数据

    没有固定的查询语言

    键值对存储,列存储,文档存储,图形数据库(社交关系)最终一致性,

    CAP定理和BASE(异地多活)初级架构师!(狂神理念:只要学不死,就往死里学!)高性能,高可用,高可扩展

缓存发展

数据库查询缓存。

发展过程:

1单机mysql

2 优化数据结构和索引—>文件缓存(io)—>Memcached

3 分库分表+水平拆分+MySql集群 MyISAM(表锁)—> Innodb(行锁)

4(最近的年代)数据量越来越大。Mysql等关系型数据库不够用。

Redis-benchmark压力测试

Redis-benchmark压力测试工具:
在这里插入图片描述

Redis基础知识

redis能干吗?

简单来说 Redis 就是一个使用 C 语言开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度非常快,因此 Redis 被广泛应用于缓存方向。

另外,Redis 除了做缓存之外,Redis 也经常用来做分布式锁,甚至是消息队列。

Redis 提供了多种数据类型来支持不同的业务场景。Redis 还支持事务 、持久化、Lua 脚本、多种集群方案

1、内存存储、持久化,内存中是断电即失、所以说持久化很重要( rdb、aof )2、效率高,可以用于高速缓存
3、发布订阅系统
4、地图信息分析
5、计时器、计数器(浏览量!)

redis特性

1、多样的数据类型

2、持久化
3、集群

4、事务

默认16个数据库,使用0database

# select index 切换数据库
select 3
#flushdb 清空当前库 flushdball清除全部数据
flushdb
#exists key key是否存在
exists test-string
#move key 移除key
move test-string
#expipre key 10 过期时间10s ttl key 查看还有多久时间过期
expipre test-time 10
ttl  test-time
# type key查看key的类型

Redis为什么单线程还这么快?

1、误区1∶高性能的服务器一定是多线程的?

2、误区2∶多线程(CPU上下文会切换!)一定比单线程效率高!先去CPU>内存>硬盘的速度要有所了解!

核心: redis是将所有的数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,

多线程(CPU上下文会切换︰耗时的操作!!!),对于内存系统来说,如果没有上下文切换效率就是最高的!多次读写都是在一个CPU上的,

在内存情况下,这个就是最佳的方案!

Redis数据类型
5种基本数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

类型简介特性场景
String(字符串)二进制安全可以包含任何数据,比如jpg图片或者序列化的对象,一个键最大能存储512M
Hash(字典)键值对集合,即编程语言中的Map类型适合存储对象,并且可以像数据库中update一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去)存储、读取、修改用户属性
List(列表)链表(双向链表)增删快,提供了操作某一段元素的API1,最新消息排行等功能(比如朋友圈的时间线) 2,消息队列
Set(集合)哈希表实现,元素不重复1、添加、删除,查找的复杂度都是O(1) 2、为集合提供了求交集、并集、差集等操作1、共同好友 2、利用唯一性,统计访问网站的所有独立ip 3、好友推荐时,根据tag求交集,大于某个阈值就可以推荐
Sorted Set(有序集合)将Set中的元素增加一个权重参数score,元素按score有序排列数据插入集合时,已经进行天然排序1、排行榜 2、带权重的消息队列
string(字符串)

string(字符串)

#keys *查看所有key
#set
set test-string 'I am String'
#get
get test-string
#append 追加字符串
append test-string ",hello"
#incr 自增1 decr自减1
incr test-num
#incrby test 10 设置增长步长
incrby test-num 10
#getrange 0 3 获取0到3 getrange 0 -1等于getkey
getrange test-string 0 3
#setrange 1 xx替换
setrange test-string 1 xx
#setex(set with expire) 设置过期时间
#setnx (set if not expire) 如果不存在设置(分布式锁中常使用)
#mset k1 v1 k2 v2 k3 v3批量设置
mset k1 v1 k2 v2 k3 v3
#mget k1 k2 k3批量获取
mget k1 k2 k3
#msetnx 原子性操作/整体成功/整体失败
msetnx k1 v1 k4 v4
#设置对象
set user: 1 iname : zhangsan ,age : 33
mset user: 1:name zhangsan user: 1:age 2
#getset 先get再set 更新操作
#String类似的使用场景:value除了是我们的字符串还可以是我们的数字!
#·计数器
#统计多单位的数量uid:95256449:follow 0
#strlen key 获取长度
strlen test-string
#del key删除
hash(哈希)

hash(哈希)

类似 key-map 值为map集合

#HSET set值
hset test-hash key hash
#HGet
hget test-hash key
hget test-hash key2
#HMSET批量set值  hmget k1 k2
hmset test-hash k1 hash k2 value
#hgetall key获取全部的值
hgetall test-hash
#hdel key field1删除
hdel key1 f1
#hlen key1长度
hlen key1 
#hexists key filed判断是否存在
hexists key1 f1
#hkeys key1获取所有filed   hvals key1 获取所有value
hkeys key1
hvals key1
# hincrby key field自增 hdecrby自减
hincrby hashkey field 2
#hsetnx 如果不存在则可以设置

list(列表)

list(列表)

·他实际上是一个链表,before Node after , left , right都可以插入值

·如果key不存在,创建新的链表

·如果key存在,新增内容

·如果移除了所有值,空链表,也代表不存在!

·在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点~消息排队!消息队列( Lpush Rpop ) ,栈( Lpush Lpop )

#lpush 添加一个元素到列表的头部(左边)
lpush test-list "list[0]"
#rpush 添加一个元素到尾部(右边)
rpush test-list "list[1]"
#lrange 0 10取出
lrange test-list
#lpop 左移除 rpop 右移除
lpop test-list
rpop test-list
#lindex test-list index通过下标获取值
lindex test-list 0
#llen key 长度
llen test-list 
#lrem 移除指定个数的value
lrem test-list  1 one
#ltrim 通过下标截取指定长度
ltrim test-list 0 2
#rpoplpush test-list mylist移动最后一个到mylist
rpoplpush test-list mylist
#lset  test-list 0 item更新 如果不存在列表我们去更新就会报错
lset  test-list 0 item
#linsert mylist before/after "list[2]" "list{1}"在xxx之前/之后插入
linsert mylist before "list[2]" "list{1}"
set(集合)

set(集合)

#sadd添加元素
sadd test-set "set[0]"
#smembers 取出
smembers test-set
#sismember 判断某个值在集合中是否存在
sismember test-set  "set[0]"
#srem 移除
srem test-set "set[1]"
# scard 统计个数
scard srem
#srandmember key count随机抽取几个个元素
srandmember test-set 
srandmember test-set 2
#spop 随机删除
spop test-set
#smove k1 k2 value移动
smove test-set myset "set[1]"
#sdiff k1 k2 k1和k2的差集
sdiff test-set myset
#sinter k1 k2交集
sinter test-set myset
zset(sorted set:有序集合)

zset(sorted set:有序集合)

#zadd key score value添加元素
zadd test-zset 3 "value1"
zadd test-zset 1 "value2"
#zrange取出
zrange test-zset
#zrangebyscore 排序取出 -inf +inf从小到大
zrangebyscore test-zset 0 10
#zrevertangebyscore 从大到下
#zrem移除
#zcard个数
#zcount指定区间个数
3种特殊的数据类型
Bitmap 位存储

Bitmap 位存储

统计用户信息,活跃,不活跃! 登录 、 未登录! 打卡,365打卡! 两个状态的,都可以使用 Bitmaps!

Bitmap 位图,数据结构! 都是操作二进制位来进行记录,就只有0 和 1 两个状态! 365 天 = 365 bit 1字节 = 8bit 46 个字节左右!

Bitmap就是通过一个bit位来表示某个元素对应的值或者状态

其中的 key 就是对应元素本身,实际上底层也是通过对字符串的操作来实现

Redis2.2 版本之后新增了setbit, getbit, bitcount 等几个 bitmap 相关命令

虽然是新命令,但是本身都是对字符串的操作

SETBIT key offset value

其中 offset 必须是数字,value 只能是 0 或者 1

这个命令的返回值是修改前的值

比如调用

[复制代码](javascript:void(0)😉

setbit byte0 0 1;
setbit byte0 2 1;
setbit byte0 5 1;

setbit byte1 1 1;
setbit byte1 4 1;

[复制代码](javascript:void(0)😉

对应内存中的值就是这样

bytebit0bit1bit2bit3bit4bit5bit6bit7
byte010100100
byte101001000

测试

使用bitmap 来记录 周一到周日的打卡! 周一:1 周二:0 周三:0 周四:1 …

127.0.0.1:6379> setbit sign 0 1
(integer) 0
127.0.0.1:6379> setbit sign 1 1
(integer) 0
127.0.0.1:6379> setbit sign 2 0
(integer) 0
127.0.0.1:6379> setbit sign 3 1
(integer) 0
127.0.0.1:6379> setbit sign 4 0
(integer) 0
127.0.0.1:6379> setbit sign 5 0
(integer) 0
127.0.0.1:6379> setbit sign 6 1
(integer) 0

查看某一天是否有打卡!

127.0.0.1:6379> getbit sign 3
(integer) 1
127.0.0.1:6379> getbit sign 5
(integer) 0

统计操作,统计 打卡的天数!

127.0.0.1:6379> bitcount sign # 统计这周的打卡记录,就可以看到是否有全勤!
(integer) 3
Geospatial地理位置

Geospatial地理位置

Redis 地理位置(Geospatial)

geoadd

# geoadd 添加地理位置
# 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
# 有效的经度从-180度到180度。
# 有效的纬度从-85.05112878度到85.05112878度。
# 当坐标位置超出上述指定范围时,该命令将会返回一个错误。
# 127.0.0.1:6379> geoadd china:city 39.90 116.40 beijin
(error) ERR invalid longitude,latitude pair 39.900000,116.400000
# 参数 key 值() 经度,纬度,名称
127.0.0.1:6379> geoadd china:city 116.40 39.90 beijing
(integer) 1
127.0.0.1:6379> geoadd china:city 121.47 31.23 shanghai
(integer) 1
127.0.0.1:6379> geoadd china:city 106.50 29.53 chongqi 114.05 22.52 shengzhen
(integer) 2
127.0.0.1:6379> geoadd china:city 120.16 30.24 hangzhou 108.96 34.26 xian
(integer) 2

geopos

获得当前定位:一定是一个坐标值!

127.0.0.1:6379> GEOPOS china:city beijing # 获取指定的城市的经度和纬度!
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
127.0.0.1:6379> GEOPOS china:city beijing chongqi
1) 1) "116.39999896287918091"
2) "39.90000009167092543"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"

geodist

两人之间的距离! 单位:

  • m 表示单位为米。
  • km 表示单位为千米。
  • mi 表示单位为英里。
  • ft 表示单位为英尺。
127.0.0.1:6379> GEODIST china:city beijing shanghai km # 查看上海到北京的直线距离
"1067.3788"
127.0.0.1:6379> GEODIST china:city beijing chongqi km # 查看重庆到北京的直线距离
"1464.0708"

georadius 以给定的经纬度为中心, 找出某一半径内的元素

我附近的人? (获得所有附近的人的地址,定位!)通过半径来查询!

获得指定数量的人,200

所有数据应该都录入:china:city ,才会让结果更加精确!

127.0.0.1:6379> GEORADIUS china:city 110 30 1000 km # 以110,30 这个经纬度为中心,寻
找方圆1000km内的城市
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km
1) "chongqi"
2) "xian"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist # 显示到中间距离的位置
1) 1) "chongqi"
2) "341.9374"
2) 1) "xian"
2) "483.8340"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withcoord # 显示他人的定位信息
1) 1) "chongqi"
2) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) 1) "108.96000176668167114"
2) "34.25999964418929977"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 1 #筛选出指定的结果!
1) 1) "chongqi"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
127.0.0.1:6379> GEORADIUS china:city 110 30 500 km withdist withcoord count 2
1) 1) "chongqi"
2) "341.9374"
3) 1) "106.49999767541885376"
2) "29.52999957900659211"
2) 1) "xian"
2) "483.8340"
3) 1) "108.96000176668167114"
2) "34.25999964418929977"

GEORADIUSBYMEMBER 找出位于指定范围内的元素,中心点是由给定的位置元素决定

georadiusbymember

# 找出位于指定元素周围的其他元素!
127.0.0.1:6379> GEORADIUSBYMEMBER china:city beijing 1000 km
1) "beijing"
2) "xian"
127.0.0.1:6379> GEORADIUSBYMEMBER china:city shanghai 400 km
1) "hangzhou"
2) "shanghai"

GEOHASH 命令 - 返回一个或多个位置元素的 Geohash 表示

该命令将返回11个字符的Geohash字符串!

# 将二维的经纬度转换为一维的字符串,如果两个字符串越接近,那么则距离越近!
127.0.0.1:6379> geohash china:city beijing chongqi
1) "wx4fbxxfke0"
2) "wm5xzrybty0"

GEO 底层的实现原理其实就是 Zset!我们可以使用Zset命令来操作geo

127.0.0.1:6379> ZRANGE china:city 0 -1 # 查看地图中全部的元素
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
6) "beijing"
127.0.0.1:6379> zrem china:city beijing # 移除指定元素!
(integer) 1
127.0.0.1:6379> ZRANGE china:city 0 -1
1) "chongqi"
2) "xian"
3) "shengzhen"
4) "hangzhou"
5) "shanghai"
HyperLogLog 基数统计

HyperLogLog 基数统计

优点:占用的内存是固定,2^64 不同的元素的基数,只需要12KB内存!如果要从内存角度来比较的 话 Hyperloglog 首选! 0.81% 错误率! 统计UV任务,可以忽略不计的!

什么是基数?

A {1,3,5,7,8,7}

B{1,3,5,7,8} 基数(不重复的元素) = 5,可以接受误差!

简介

Redis 2.8.9 版本就更新了 Hyperloglog 数据结构! Redis Hyperloglog 基数统计的算法!

优点:占用的内存是固定,2^64 不同的元素的基数,只需要12KB内存!

如果要从内存角度来比较的 话 Hyperloglog 首选! 0.81% 错误率! 统计UV任务,可以忽略不计的!

测试使用

网页的 UV (一个人访问一个网站多次,但是还是算作一个人!)

传统的方式, set 保存用户的id,然后就可以统计 set 中的元素数量作为标准判断 !

这个方式如果保存大量的用户id,就会比较麻烦!

我们的目的是为了计数,而不是保存用户id;

#PFadd 
127.0.0.1:6379> PFadd mykey a b c d e f g h i j # 创建第一组元素 mykey
(integer) 1
#PFCOUNT 统计 mykey 元素的基数数量
127.0.0.1:6379> PFCOUNT mykey # 统计 mykey 元素的基数数量
(integer) 10
127.0.0.1:6379> PFadd mykey2 i j z x c v b n m # 创建第二组元素 mykey2
(integer) 1
127.0.0.1:6379> PFCOUNT mykey2
(integer) 9
127.0.0.1:6379> PFMERGE mykey3 mykey mykey2 # 合并两组 mykey mykey2 => mykey3 并集
OK
127.0.0.1:6379> PFCOUNT mykey3 # 看并集的数量!
(integer) 15

如果允许容错,那么一定可以使用 Hyperloglog !

如果不允许容错,就使用 set 或者自己的数据类型即可!

Redis 和 Memcached 的区别和共同点

现在公司一般都是用 Redis 来实现缓存,而且 Redis 自身也越来越强大了!不过,了解 Redis 和 Memcached 的区别和共同点,有助于我们在做相应的技术选型的时候,能够做到有理有据!

共同点

  1. 都是基于内存的数据库,一般都用来当做缓存使用。
  2. 都有过期策略。
  3. 两者的性能都非常高。

区别

  1. Redis 支持更丰富的数据类型(支持更复杂的应用场景)。Redis 不仅仅支持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只支持最简单的 k/v 数据类型。
  2. Redis 支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而 Memecache 把数据全部存在内存之中。
  3. Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。
  4. Redis 在服务器内存使用完之后,可以将不用的数据放到磁盘上。但是,Memcached 在服务器内存使用完之后,就会直接报异常。
  5. Memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据;但是 Redis 目前是原生支持 cluster 模式的.
  6. Memcached 是多线程,非阻塞 IO 复用的网络模型;Redis 使用单线程的多路 IO 复用模型。 (Redis 6.0 引入了多线程 IO )
  7. Redis 支持发布订阅模型、Lua 脚本、事务等功能,而 Memcached 不支持。并且,Redis 支持更多的编程语言。
  8. Memcached过期数据的删除策略只用了惰性删除,而 Redis 同时使用了惰性删除与定期删除。

相信看了上面的对比之后,我们已经没有什么理由可以选择使用 Memcached 来作为自己项目的分布式缓存了。

可以看出,文件事件处理器(file event handler)主要是包含 4 个部分:

  • 多个 socket(客户端连接)
  • IO 多路复用程序(支持多个客户端连接的关键)
  • 文件事件分派器(将 socket 关联到相应的事件处理器)
  • 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
redis单线程

redis为什么不是用单线程?

大体上来说,Redis 6.0 之前主要还是单线程处理。

那,Redis6.0 之前 为什么不使用多线程?

我觉得主要原因有下面 3 个:

  1. 单线程编程容易并且更容易维护;
  2. Redis 的性能瓶颈不再 CPU ,主要在内存和网络;
  3. 多线程就会存在死锁、线程上下文切换等问题,甚至会影响性能。

Redis6.0 引入多线程主要是为了提高网络 IO 读写性能,因为这个算是 Redis 中的一个性能瓶颈(Redis 的瓶颈主要受限于内存和网络)。

虽然,Redis6.0 引入了多线程,但是 Redis 的多线程只是在网络数据的读写这类耗时操作上使用了, 执行命令仍然是单线程顺序执行。因此,你也不需要担心线程安全问题。

Redis6.0 的多线程默认是禁用的,只使用主线程。如需开启需要修改 redis 配置文件 redis.conf

io-threads-do-reads yesCopy to clipboardErrorCopied

开启多线程后,还需要设置线程数,否则是不生效的。同样需要修改 redis 配置文件 redis.conf :

io-threads 4 #官网建议4核的机器建议设置为2或3个线程,8核的建议设置为6个线程
过期时间与删除策略

为什么给数据设置过期时间?

内存是有限的,如果缓存中的所有数据都是一直保存的话,很容易Out of memory。

Redis 自带了给缓存数据设置过期时间的功能,比如:

127.0.0.1:6379> exp key  60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56

注意:Redis中除了字符串类型有自己独有设置过期时间的命令 setex 外,

**其他方法都需要依靠 expire 命令来设置过期时间 。另外, persist 命令可以移除一个键的过期时间: **

过期时间除了有助于缓解内存的消耗,还有什么其他用么?

很多时候,我们的业务场景就是需要某个数据只在某一时间段内存在,比如我们的短信验证码可能只在1分钟内有效,用户登录的 token 可能只在 1 天内有效。

如果使用传统的数据库来处理的话,一般都是自己判断过期,这样更麻烦并且性能要差很多

Redis是如何判断数据是否过期的呢

Redis 通过一个叫做过期字典(可以看作是hash表)来保存数据过期的时间。

过期字典的键指向Redis数据库中的某个key(键),过期字典的值是一个long long类型的整数,

这个整数保存了key所指向的数据库键的过期时间(毫秒精度的UNIX时间戳)。

在这里插入图片描述

过期字典是存储在redisDb这个结构里的:

typedef struct redisDb {
    ...

    dict *dict;     //数据库键空间,保存着数据库中所有键值对
    dict *expires   // 过期字典,保存着键的过期时间
    ...
} redisDb;

过期数据删除策略

常用的过期数据的删除策略就两个(重要!自己造缓存轮子的时候需要格外考虑的东西):

  1. 惰性删除 :只会在取出key的时候才对数据进行过期检查。这样对CPU最友好,但是可能会造成太多过期 key 没有被删除。

  2. 定期删除 : 每隔一段时间抽取一批 key 执行删除过期key操作。

    并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对CPU时间的影响。

定期删除对内存更加友好,惰性删除对CPU更加友好。两者各有千秋,所以Redis 采用的是 定期删除+惰性/懒汉式删除

但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致大量过期 key 堆积在内存里,然后就Out of memory了。

怎么解决这个问题呢?答案就是: Redis 内存淘汰机制。

redis内存淘汰机制

Redis 提供 6 种数据淘汰策略:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!

4.0 版本后增加以下两种:

  1. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
  2. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key
Rdis 持久化

Redis 支持持久化,而且支持两种不同的持久化操作。

Redis 的一种持久化方式叫快照(snapshotting,RDB),另一种方式是只追加文件(append-only file, AOF)

快照(snapshotting)持久化(RDB)

保存dump.rdb文件

Redis 可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。

Redis 创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本

(Redis 主从结构,主要用来提高 Redis 性能),还可以将快照留在原地以便重启服务器的时候使用。

快照持久化是 Redis 默认采用的持久化方式,在 Redis.conf 配置文件中默认有此下配置:

save 900 1           #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 300 10          #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。

save 60 10000        #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。Copy to clipboardErrorCopied

触发机制

key改变满足save

flushall

退出redis

恢复:

将dump.rdb放在usr/loval/bin

优点:适合大规模恢复数据

缺点:

redis宕机丢失最后一次修改数据。

持久化时子进程占用一定的内存

AOF(append-only file)持久化

将所有操作命令(写操作)记录下来。恢复时再次执行

数据文件:appendonly.aof

如果aof文件被破坏。redis-chenck-aof用来修护。

与快照持久化相比,AOF 持久化 的实时性更好,因此已成为主流的持久化方案。

默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启:

appendonly yesCopy to clipboardErrorCopied

开启 AOF 持久化后每执行一条会更改 Redis 中的数据的命令,Redis 就会将该命令写入硬盘中的 AOF 文件。

AOF 文件的保存位置和 RDB 文件的位置相同,都是通过 dir 参数设置的,默认的文件名是 appendonly.aof。

在 Redis 的配置文件中存在三种不同的 AOF 持久化方式,它们分别是:

appendfsync always    #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
appendfsync everysec  #每秒钟同步一次,显示地将多个写命令同步到硬盘
appendfsync no        #让操作系统决定何时进行同步Copy to clipboardErrorCopied

为了兼顾数据和写入性能,用户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步一次 AOF 文件,

Redis 性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。

当硬盘忙于执行写入操作的时候,Redis 还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。

优点:每一次修改都同步保证数据完整

    从不同步效率最高

缺点:同步速度比较慢

Redis 4.0 对于持久化机制的优化

Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。

如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。

这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。

当然缺点也是有的, AOF 里面的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。

补充内容:AOF 重写

AOF 重写可以产生一个新的 AOF 文件,这个新的 AOF 文件和原有的 AOF 文件所保存的数据库状态一样,但体积更小。

AOF 重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无须对现有 AOF 文件进行任何读入、分析或者写入

操作。

如果aof大于64m触发重写。无限追加会造成文件越来越大。

在执行 BGREWRITEAOF 命令时,Redis 服务器会维护一个 AOF 重写缓冲区,该缓冲区会在子进程创建新 AOF 文件期间,记录服务器执

行的所有写命令。当子进程完成创建新 AOF 文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新 AOF 文件的末尾,使得新旧

两个 AOF 文件所保存的数据库状态一致。最后,服务器用新的 AOF 文件替换旧的 AOF 文件,以此来完成 AOF 文件重写操作。

扩展:

性能建议:

●因为RDB文件只用作后备用途,建议只在Slave.上持久化RDB文件,而且只要15分钟备份一次就够了,只保留save 900 1这条

●如果Enable AOF ,好处是在最恶劣情况下也只会丢失不超过两秒数据,启动脚本较简单只load自己的AOF文件就可以了,代价

是带来了持续的I0 ,二是AOF rewrite的最后将rewrite过程中产生的新数据写到新文件造成的阻塞几乎是不可避免的。只要

硬盘许可,应该尽量减少AOF rewrite的频率, AOF重写的基础大小默认值64M太小了,可以设到5G以上,默认超过原大小

100%大小重写可以改到适当的数值。
●如果不Enable AOF , 仅靠Master-Slave Repllcation 实现高可用性也可以,能省掉一大笔I0 ,也减少了rewrite时带来的系统

波动。代价是如果Master/Slave同时倒掉,会丢失十几分钟的数据,启动脚本也要比较两个Master/Slave 中的RDB文件,载

入较新的那个,微博就是这种架构。

Redis 事务

单条命令保证原子性,redis事务不保证原子性,它是一组事务的集合。redis事务没有隔离性。

Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能。

#开启事务
> MULTI
OK
#事务入队
> INCR foo
QUEUED
> INCR bar
QUEUED
#执行事务
> EXEC
1) (integer) 1
2) (integer) 1Copy to clipboardErrorCopied
#discard 取消事务  事务队列中的命令都不会执行
discard
#编译时异常  命令又错误  事务队列中的命令都不会执行
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> error k1 # 这是一条语法错误命令
(error) ERR unknown command `error`, with args beginning with: `k1`, # 会报错但是不影响后续命令入队 
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> EXEC
(error) EXECABORT Transaction discarded because of previous errors. # 执行报错
127.0.0.1:6379> get k1 
(nil) # 其他命令并没有被执行
#运行时异常  如果事务队列中存在语法性,那么执行命令的时候,其他命令式可以正常执行的
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> INCR k1 # 这条命令逻辑错误(对字符串进行增量)
QUEUED
127.0.0.1:6379> get k2
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) (error) ERR value is not an integer or out of range # 运行时报错
4) "v2" # 其他命令正常执行

# 虽然中间有一条命令报错了,但是后面的指令依旧正常执行成功了。
# 所以说Redis单条指令保证原子性,但是Redis事务不能保证原子性。

使用 MULTI命令后可以输入多个命令。Redis不会立即执行这些命令,而是将它们放到队列,当调用了EXEC命令将执行所有命令。

但是,Redis 的事务和我们平时理解的关系型数据库的事务不同。我们知道事务具有四大特性: 1. 原子性2. 隔离性3. 持久性4. 一致性

  1. 原子性(Atomicity): 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
  2. 隔离性(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
  3. 持久性(Durability): 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
  4. 一致性(Consistency): 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;

Redis 是不支持 roll back 的,因而不满足原子性的(而且不满足持久性)。

Redis官网也解释了自己为啥不支持回滚。简单来说就是Redis开发者们觉得没必要支持回滚,这样更简单便捷并且性能更好。

Redis开发者觉得即使命令执行错误也应该在开发过程中就被发现而不是生产过程中。

你可以将Redis中的事务就理解为 :Redis事务提供了一种将多个命令请求打包的功能。

然后,再按顺序执行打包的所有命令,并且不会被中途打断,据有排他性。

redis事务没有隔离性。

监控

悲观锁:

  • 很悲观,认为什么时候都会出现问题,无论做什么都会加锁

乐观锁:

  • 很乐观,认为什么时候都不会出现问题,所以不会上锁!更新数据的时候去判断一下,在此期间是否有人修改过这个数据
  • 获取version
  • 更新的时候比较version

使用watch key监控指定数据,相当于乐观锁加锁

正常执行

127.0.0.1:6379> set money 100 # 设置余额:100
OK
127.0.0.1:6379> set use 0 # 支出使用:0
OK
127.0.0.1:6379> watch money # 监视money (上锁)
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> exec # 监视值没有被中途修改,事务正常执行
1) (integer) 80
2) (integer) 20

测试多线程修改值,使用watch可以当做redis的乐观锁操作(相当于getversion)

我们启动另外一个客户端模拟插队线程。

线程1:

127.0.0.1:6379> watch money # money上锁
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> DECRBY money 20
QUEUED
127.0.0.1:6379> INCRBY use 20
QUEUED
127.0.0.1:6379> 	# 此时事务并没有执行
123456789

模拟线程插队,线程2:

127.0.0.1:6379> INCRBY money 500 # 修改了线程一中监视的money
(integer) 600
12

回到线程1,执行事务:

127.0.0.1:6379> EXEC # 执行之前,另一个线程修改了我们的值,这个时候就会导致事务执行失败
(nil) # 没有结果,说明事务执行失败

127.0.0.1:6379> get money # 线程2 修改生效
"600"
127.0.0.1:6379> get use # 线程1事务执行失败,数值没有被修改
"0"
1234567

解锁获取最新值,然后再加锁进行事务。

unwatch进行解锁。

注意:每次提交执行exec后都会自动释放锁,不管是否成功

SpringBoot整合
  1. 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
1234

springboot 2.x后 ,原来使用的 Jedis 被 lettuce 替换。

jedis:采用的直连,多个线程操作的话,是不安全的。如果要避免不安全,使用jedis pool连接池!更像BIO模式

lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况!可以减少线程数据了,更像NIO模式

我们在学习SpringBoot自动配置的原理时,整合一个组件并进行配置一定会有一个自动配置类xxxAutoConfiguration,并且在spring.factories中也一定能找到这个类的完全限定名。Redis也不例外。

redis.conf详解

容量单位不区分大小写,G和GB有区别

# Note on units: when memory size is needed, it is possible to specify
# it in the usual form of 1k 5GB 4M and so forth:
#
# 1k => 1000 bytes
# 1kb => 1024 bytes
# 1m => 1000000 bytes
# 1mb => 1024*1024 bytes
# 1g => 1000000000 bytes
# 1gb => 1024*1024*1024 bytes
#
# units are case insensitive so 1GB 1Gb 1gB are all the same.

网络

################################## NETWORK #####################################

# By default, if no "bind" configuration directive is specified, Redis listens
# for connections from all the network interfaces available on the server.
# It is possible to listen to just one or multiple selected interfaces using
# the "bind" configuration directive, followed by one or more IP addresses.
#
# Examples:
#
# bind 192.168.1.100 10.0.0.1
# bind 127.0.0.1 ::1
#
# ~~~ WARNING ~~~ If the computer running Redis is directly exposed to the
# internet, binding to all the interfaces is dangerous and will expose the
# instance to everybody on the internet. So by default we uncomment the
# following bind directive, that will force Redis to listen only into
# the IPv4 loopback interface address (this means Redis will be able to
# accept connections only from clients running into the same computer it
# is running).
#
# IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACES
# JUST COMMENT THE FOLLOWING LINE.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bind 127.0.0.1 #ip绑定  注释掉或者改为 * , 0.0.0.0

# Protected mode is a layer of security protection, in order to avoid that
# Redis instances left open on the internet are accessed and exploited.
#
# When protected mode is on and if:
#
# 1) The server is not binding explicitly to a set of addresses using the
#    "bind" directive.
# 2) No password is configured.
#
# The server only accepts connections from clients connecting from the
# IPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domain
# sockets.
#
# By default protected mode is enabled. You should disable it only if
# you are sure you want clients from other hosts to connect to Redis
# even if no authentication is configured, nor a specific set of interfaces
# are explicitly listed using the "bind" directive.
protected-mode yes #保护模式,远程连接需要改为no

# Accept connections on the specified port, default is 6379 (IANA #815344).
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379  #端口

通用配置

 #以守护进程运行
daemonize no 
#如果以守护进程运行,需要制定pid文件
pidfile /var/run/redis_6379.pid
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
#日志级别
loglevel notice 
 # 日志文件
logfile ""
# Set the number of databases. The default database is DB 0, you can select
# a different one on a per-connection basis using SELECT <dbid> where
# dbid is a number between 0 and 'databases'-1
#默认数据库数量
databases 16

# By default Redis shows an ASCII art logo only when started to log to the
# standard output and if the standard output is a TTY. Basically this means
# that normally a logo is displayed only in interactive sessions.
#
# However it is possible to force the pre-4.0 behavior and always show a
# ASCII art logo in startup logs by setting the following option to yes.
# 是否总是显示logo
always-show-logo yes

持久化规则

# 持久化到 .rdb .aof文件中
# 如果900秒被 1个key发生改变
save 900 1
save 300 10
save 60 10000
#如果遇到错误继续
stop-writes-on-bgsave-error yes
#是否压缩rdb文件
rdbcompression yes
#校验 rdb文件
rdbchecksum yes
# The filename where to dump the DB
# rdb文件名
dbfilename dump.rdb

# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the 'dbfilename' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
#rdb文件保存目录
dir ./

安全

#密码
requirepass 123456
config set requirepass 123456
#登录
auth 123456

限制

#最大连接数
maxclients 10000
#最大内存
 maxmemory <bytes>
#内存淘汰机制
maxmemory-policy noeviction

APPEND ONLY MODE aof设置

#默认不开启 使用rdb
appendonly no
#持久化文件名
appendfilename "appendonly.aof"
#每次修改同步一次
# appendfsync always
#每秒同步一次
appendfsync everysec
# 操作系统自己同步
# appendfsync no
发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

消息发送者 频道 消息订阅者

在这里插入图片描述

命令

命令描述
PSUBSCRIBE pattern [pattern..]订阅一个或多个符合给定模式的频道。
PUNSUBSCRIBE pattern [pattern..]退订一个或多个符合给定模式的频道。
PUBSUB subcommand [argument[argument]]查看订阅与发布系统状态。
PUBLISH channel message向指定频道发布消息
SUBSCRIBE channel [channel..]订阅给定的一个或多个频道。
SUBSCRIBE channel [channel..]退订一个或多个频道
主从复制

主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master/Leader),后者称为从节点

(Slave/Follower), 数据的复制是单向的!只能由主节点复制到从节点(主节点以写为主、从节点以读为主)。

作用

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余的方式。
  2. 故障恢复:当主节点故障时,从节点可以暂时替代主节点提供服务,是一种服务冗余的方式
  3. 负载均衡:在主从复制的基础上,配合读写分离,由主节点进行写操作,从节点进行读操作,分担服务器的负载;尤其是在多读少写的场景下,通过多个从节点分担负载,提高并发量。
  4. 高可用基石:主从复制还是哨兵和集群能够实施的基础。

既然需要启动多个服务,就需要多个配置文件。每个配置文件对应修改以下信息:

  • 端口号
  • pid文件名
  • 日志文件名
  • rdb文件名

我们这里是使用命令搭建,是暂时的,==真实开发中应该在从机的配置文件中进行配置,==这样的话是永久的。

################################# REPLICATION #################################

# Master-Replica replication. Use replicaof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
#   +------------------+      +---------------+
#   |      Master      | ---> |    Replica    |
#   | (receive writes) |      |  (exact copy) |
#   +------------------+      +---------------+
#
# 1) Redis replication is asynchronous, but you can configure a master to
#    stop accepting writes if it appears to be not connected with at least
#    a given number of replicas.
# 2) Redis replicas are able to perform a partial resynchronization with the
#    master if the replication link is lost for a relatively small amount of
#    time. You may want to configure the replication backlog size (see the next
#    sections of this file) with a sensible value depending on your needs.
# 3) Replication is automatic and does not need user intervention. After a
#    network partition replicas automatically try to reconnect to masters
#    and resynchronize with them.
#
# replicaof <masterip> <masterport>
replicaof 127.0.0.1 6379
# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the replica to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the replica request.
#
# masterauth <master-password>
 masterauth 123456

使用规则

  1. 从机只能读,不能写,主机可读可写但是多用于写。

     127.0.0.1:6381> set name sakura # 从机6381写入失败
    (error) READONLY You can't write against a read only replica.
    
    127.0.0.1:6380> set name sakura # 从机6380写入失败
    (error) READONLY You can't write against a read only replica.
    
    127.0.0.1:6379> set name sakura
    OK
    127.0.0.1:6379> get name
    "sakura"
    12345678910
    
  2. 当主机断电宕机后,默认情况下从机的角色不会发生变化 ,集群中只是失去了写操作,当主机恢复以后,又会连接上从机恢复原状。

  3. 当从机断电宕机后,若不是使用配置文件配置的从机,再次启动后作为主机是无法获取之前主机的数据的,若此时重新配置称为从

    机,又可以获取到主机的所有数据。这里就要提到一个同步原理。

  4. 第二条中提到,默认情况下,主机故障后,不会出现新的主机,有两种方式可以产生新的主机:

    • 从机手动执行命令slaveof no one,这样执行以后从机会独立出来成为一个主机
    • 使用哨兵模式(自动选举)

一主二从配置

==默认情况下,每台Redis服务器都是主节点;==我们一般情况下只用配置从机就好了!

认老大!一主(79)二从(80,81)

使用SLAVEOF host port就可以为从机配置主机了。

如果没有老大了,这个时候能不能选择出来一个老大呢?手动!

如果主机断开了连接,我们可以使用SLAVEOF no one让自己变成主机!其他的节点就可以手动连接到最新的主节点(手动)!如果这个

时候老大修复了,那么久重新连接!

哨兵模式

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时

**间内服务不可用。*这不是一种推荐的方式,更多时候,我们优先考虑*哨兵模式

单机单个哨兵

在这里插入图片描述

哨兵的作用:

  • 通过发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。

多哨兵模式

在这里插入图片描述

哨兵的核心配置

sentinel monitor mymaster 127.0.0.1 6379
1
  • 数字1表示 :当一个哨兵主观认为主机断开,就可以客观认为主机故障,然后开始选举新的主机。

测试

redis-sentinel xxx/sentinel.conf
1

哨兵模式优缺点

优点:

  1. 哨兵集群,基于主从复制模式,所有主从复制的优点,它都有
  2. 主从可以切换,故障可以转移,系统的可用性更好
  3. 哨兵模式是主从模式的升级,手动到自动,更加健壮

缺点:

  1. Redis不好在线扩容,集群容量一旦达到上限,在线扩容就十分麻烦
  2. 实现哨兵模式的配置其实是很麻烦的,里面有很多配置项

哨兵模式的全部配置

完整的哨兵模式配置文件 sentinel.conf

# Example sentinel.conf
 
# 哨兵sentinel实例运行的端口 默认26379
port 26379
 
# 哨兵sentinel的工作目录
dir /tmp
 
# 哨兵sentinel监控的redis主节点的 ip port 
# master-name  可以自己命名的主节点名字 只能由字母A-z、数字0-9 、这三个字符".-_"组成。
# quorum 当这些quorum个数sentinel哨兵认为master主节点失联 那么这时 客观上认为主节点失联了
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor mymaster 127.0.0.1 6379 1
 
# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码
# sentinel auth-pass <master-name> <password>
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd
 
 
# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒
# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds mymaster 30000
 
# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,
这个数字越小,完成failover所需的时间就越长,
但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs <master-name> <numslaves>
sentinel parallel-syncs mymaster 1
 
 
 
# 故障转移的超时时间 failover-timeout 可以用在以下这些方面: 
#1. 同一个sentinel对同一个master两次failover之间的间隔时间。
#2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
#3.当想要取消一个正在进行的failover所需要的时间。  
#4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,但是就不按parallel-syncs所配置的规则来了
# 默认三分钟
# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout mymaster 180000
 
# SCRIPTS EXECUTION
 
#配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
#对于脚本的运行结果有以下规则:
#若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10
#若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
#如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
#一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
#通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,
#这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,
#一个是事件的类型,
#一个是事件的描述。
#如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。
#通知脚本
# sentinel notification-script <master-name> <script-path>
  sentinel notification-script mymaster /var/redis/notify.sh
 
# 客户端重新配置主节点参数脚本
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# 目前<state>总是“failover”,
# <role>是“leader”或者“observer”中的一个。 
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script <master-name> <script-path>
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
缓存穿透

什么是缓存穿透(查不到)?

缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。

举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。

解决办法:

1最基本的就是首先做好参数校验,一些不合法的参数请求直接抛出异常信息返回给客户端。比如查询的数据库 id 不能小于 0、传入的邮

箱格式不对的时候直接返回错误消息给客户端等等。

2 缓存无效key。如果缓存和数据库都查不到某个 key 的数据就写一个到 Redis 中去并设置过期时间,具体命令如下:

SET key value EX 10086 。这种方式可以解决请求的 key 变化不频繁的情况,如果黑客恶意攻击,每次构建不同的请求 key,会导致

Redis 中缓存大量无效的 key 。很明显,这种方案并不能从根本上解决此问题。如果非要用这种方式来解决穿透问题的话,尽量将无效的

key 的过期时间设置短一点比如 1 分钟。

3布隆过滤器

在这里插入图片描述

布隆过滤器是一个非常神奇的数据结构,通过它我们可以非常方便地判断一个给定数据是否存在于海量数据中。

我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“人”。

具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,先判断用户发来的请求的值是否存在于布隆过滤

器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。

缓存击穿(量太大,缓存过期)

概念

相较于缓存穿透,缓存击穿的目的性更强,一个存在的key,在缓存过期的一刻,同时有大量的请求,这些请求都会击穿到DB,造成瞬时DB请求量大、压力骤增。这就是缓存被击穿,只是针对其中某个key的缓存不可用而导致击穿,但是其他的key依然可以使用缓存响应。

比如热搜排行上,一个热点新闻被同时大量访问就可能导致缓存击穿。

解决方案

  1. 设置热点数据永不过期

    这样就不会出现热点数据过期的情况,但是当Redis内存空间满的时候也会清理部分数据,而且此种方案会占用空间,一旦热点数据多了起来,就会占用部分空间。

  2. 加互斥锁(分布式锁)

    在访问key之前,采用SETNX(set if not exists)来设置另一个短期key来锁住当前key的访问,访问结束再删除该短期key。保证同时刻只有一个线程访问。这样对锁的要求就十分高。

缓存雪崩

什么是缓存雪崩?

缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。

举个例子:系统的缓存模块出了问题比如宕机导致不可用。造成系统的所有访问,都要走数据库。

还有一种缓存雪崩的场景是:有一些被大量访问数据(热点缓存)在某一时刻大面积失效,导致对应的请求直接落到了数据库上。

这样的情况,有下面几种解决办法:

举个例子 :秒杀开始 12 个小时之前,我们统一存放了一批商品到 Redis 中,设置的缓存过期时间也是 12 个小时,那么秒杀开始的时

候,这些秒杀的商品的访问直接就失效了。导致的情况就是,相应的请求直接就落到了数据库上,就像雪崩一样可怕。

解决办法:

针对 Redis 服务不可用的情况:

  1. 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
  2. 限流,避免同时处理大量的请求。

针对热点缓存失效的情况:

  1. 设置不同的失效时间比如随机设置缓存的失效时间。
  2. 缓存永不失效

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值