hadoop实时day26–redis
一、今日内容大纲
1、nosql发展和介绍
2、redis介绍 安装
3、redis数据类型
4、redis操作
shell(课堂上教学)
jedis java api(自己练习)
5、redis持久化策略
RDB
AOF
6、redis集群部署模型
7、redis 缓存击穿 缓存雪崩 缓存穿透
二、Nosql数据库发展历史
1、Nosql介绍
https://www.runoob.com/mongodb/nosql.html
2、RDBMS vs NoSQL
#RDBMS
- 高度组织化结构化数据
- 结构化查询语言(SQL) (SQL)
- 数据和关系都存储在单独的表中。
- 数据操纵语言,数据定义语言
- 严格的一致性
- 基础事务
#NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
- 键值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理
- 高性能,高可用性和可伸缩性
3、常见的nosql数据库
类型 | 部分代表 | 特点 |
---|---|---|
列存储 | Hbase Cassandra Hypertable | 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。 |
文档存储 | MongoDB CouchDB | 文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。 |
key-value存储 | Tokyo Cabinet / TyrantBerkeley DB Memcache Redis | 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。 |
图存储 | Neo4J FlockDB | 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。 |
对象存储 | db4o Versant | 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。 |
xml数据库 | Berkeley DB XMLBaseX | 高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。 |
二、Redis基本介绍和使用场景
1、redis基本概念
redis是REmote DIctionary Server(Redis) 。
是开源免费的,用C语言编写的软件。
是一个高性能(key/value)分布式内存数据库,基于内存运行并支持数据持久化的nosql数据库。
是当前最热门Nosql数据库之一。
2、redis特点
redis和其他kv类型内存数据库相比:
1、redis支持数据持久化 重启的时候可以再次加重进行使用;
2、redis支持的数据类型丰富。支持string hash set list zset等数据结构存储。
3、redis支持数据备份。可分布式运行提高读写并发。
3、redis使用场景
#1、热点数据缓存 缓存服务器
redis速度快、支持数据类型丰富
#2、限时业务的使用
redis数据支持TTL 超时被自动删除。 优惠券活动信息、手机验证码等业务场景。
#3、取最新的N个数据、排行榜相关
配合list类型做数据最新的N个
配合zset有序集合实现排行榜功能
#4、定时器、计数器
redis支持原子性操作 incrby decrby递增递减需求。
比如:限制一个手机发多少短信 限制一个接口被调用多少次 验证码被发送几次。
银行输入密码错误5次锁定账号
三、Redis安装
-
redis中英文官网
https://redis.io/ http://www.redis.cn/ #推荐把redis的中文官方作为文档使用
-
安装包
redis-4.0.2.tar.gz
-
安装目录
mkdir -p /export/servers mkdir -p /export/software mkdir -p /export/logs mkdir -p /export/data
-
解压源码包
cd /export/servers tar zxvf redis-4.0.2.tar.gz mv redis-4.0.2 redis-src
-
编译安装
#yum在线安装依赖 yum -y install gcc gcc-c++ libstdc++-devel tcl #编译安装 cd /export/servers/redis-src/ make MALLOC=libc make PREFIX=/export/servers/redis install #安装的路径是由上述PREFIX 指定的 注意和源码位置区分开
-
配置文件
mkdir -p /export/servers/redis/conf cd /export/servers/redis/conf vi redis_6379.conf #不要忘了创建redis存储数据本地文件夹路径 mkdir -p /export/data/redis/6379/
#redis 配置中需要修改的地方 其他保持默认 bind node-1 daemonize yes logfile "/export/data/redis/6379/log.log" dir /export/data/redis/6379/
-
redis启动和关闭
#redis 启动 cd /export/servers/redis bin/redis-server conf/redis_6379.conf #查看redis是否启动成功 进程 ps -ef |grep redis #redis客户端 [root@node-1 redis]# bin/redis-cli -h node-1 node-1:6379> ping PONG #服务端和客户端关闭 客户端退出: 第一种:强制退出 Ctrl+c 第二种: quit 服务端退出: 第一种: ./redis-cli -h 192.168.72.141 shutdown 第二种: kill -9
-
测试redis读写性能
在把redis服务启动成功之后 执行下面命令 bin/redis-benchmark -h node-1 #可以通过日志看出不同类型数据的读写请求速度 103950.10 requests per second
四、Redis的数据类型
-
redis数据类型很强大
1、说redis的数据类型 准确来说讲的是redis中value的数据类型 redis是kv类型数据库 2、五大常见类型 string list set zset hash
-
类型数据操作–重点掌握实际中用的多的。
redis命令手册: http://www.redis.cn/commands.html 公益网站: https://www.runoob.com/redis/redis-keys.html
五、shell命令操作Redis
1、String类型
set k1 v1 #如果k1不存在 新增;如果k1已经存在,更新。
get k1 #获取k1所对应value
mset k1 v1 k2 v2 k3 v3...#一次设置多个kv对
mget k1 k2 k3....# 一次获取多个k所对应的值
node-1:6379> mget id name age
1) "10086"
2) "allenwoon"
3) "18"
#设置kv的时候 可以添加上TTL时间限制
SETEX key seconds value #给指定key设置超时时间 单位是s
PSETEX key milliseconds value#给指定key设置超时时间 单位是ms 毫秒
#递增递减操作
incr key #对value进行++操作
decr key #对value进行--操作
incrby key 数字 #对value进行增加 增加指定数字的大小
decrby key 数字 #对value进行减少 减少指定数字的大小
递增递减操作 如果value不是数字类型字符串 直接报错
node-1:6379> get name
"allenwoon"
node-1:6379> incr name
(error) ERR value is not an integer or out of range
node-1:6379>
#追加操作
APPEND key value #如果value是字符串 可以将内容追加到尾部
node-1:6379> set name allen
OK
node-1:6379> get name
"allen"
node-1:6379> APPEND name woon
(integer) 9
node-1:6379> get name
"allenwoon"
2、Hash类型
#hash类型可以理解为java中hashmap kv映射。
#但是redis本身已经有了key 为了避免重名 field value。
key value
k1 field1 v1
field2 v2
field3 v3
#hash类型特别适合存储对象数据。
node-1:6379> hset person name allen
(integer) 1
node-1:6379> hset person age 18
(integer) 1
node-1:6379> hset person city beijing
(integer) 1
node-1:6379> hset person hobby ball
(integer) 1
#根据key、field获取对应的值
HGET key field
#hkeys 根据指定key获取value中所有的field
node-1:6379> hkeys person
1) "name"
2) "age"
3) "city"
4) "hobby"
#获取所有的value的值
node-1:6379> HVALS person
1) "allen"
2) "18"
3) "beijing"
4) "ball"
#判断指定field是否存在
HEXISTS key field
node-1:6379> HEXISTS person country
(integer) 0 #表示不存在
node-1:6379> HEXISTS person city
(integer) 1 #表示存在
#HLEN key 获取哈希表中有多少个kv对
node-1:6379> HLEN person
(integer) 4
#HDEL key field1 field2 删除一个或者多个field
3、List类型
#list类型可以看成是java中 Linklist 队列。最大的特点FIFO,先进先出。
#是一个双向都可以操作的队列 Left Right
LPUSH key v1 v2 v3 #从左侧添加数据
RPUSH key v1 v2 v3 #从右侧添加数据
node-1:6379> LPUSH list1 a b d c e
(integer) 5
node-1:6379> LRANGE list1 0 -1
1) "e"
2) "c"
3) "d"
4) "b"
5) "a"
node-1:6379> RPOP list1
"a"
node-1:6379> RPOP list1
"b"
node-1:6379> RPOP list1
"d"
node-1:6379> RPOP list1
"c"
node-1:6379> RPOP list1
"e"
node-1:6379> RPOP list1
(nil)
--如果想使用list模拟队列的功能 可以从左侧添加数据 从右侧取出数据
LPOP #移除并且返回 key 对应的 list 的第一个元素。
redis> RPUSH mylist "one"
(integer) 1
redis> RPUSH mylist "two"
(integer) 2
redis> RPUSH mylist "three"
(integer) 3
redis> LPOP mylist
"one"
redis> LRANGE mylist 0 -1
1) "two"
2) "three"
redis>
RPOP #从右侧弹出一个元素
#查看list的数据 LRANGE key start stop查看list当中所有的数据示例
LRANGE list1 0 -1 # 下标从0开始 -1表示倒数第一个元素下标
#LINDEX key index 通过索引获取列表中的元素 从0开始
redis> LPUSH mylist "World"
(integer) 1
redis> LPUSH mylist "Hello"
(integer) 2
redis> LINDEX mylist 0
"Hello"
redis> LINDEX mylist -1
"World"
redis> LINDEX mylist 3
(nil)
redis>
#LSET key index value 通过索引设置列表元素的值
node-1:6379> LRANGE list2 0 -1
1) "a"
2) "b"
3) "ww"
4) "d"
5) "e"
#LINSERT key BEFORE|AFTER pivot value 在列表的元素前或者后插入元素
redis> RPUSH mylist "Hello"
(integer) 1
redis> RPUSH mylist "World"
(integer) 2
redis> LINSERT mylist BEFORE "World" "There"
(integer) 3
redis> LRANGE mylist 0 -1
1) "Hello"
2) "There"
3) "World"
redis>
#阻塞获取操作
BLPOP k1 timeout #从指定的列表取出左边第一个元素,如果该列表没有元素 客户端会阻塞 直到超时时间结束或者发现可用元素为止。
BRPOP k1 timeout
#发送一串字符串进行验证的时候,如果没有发送则一直被阻塞
4、set类型
#和java中set一样 特点:去重 无序
#redis中set集合是string类型的无序集合 集合成员是唯一的,
#使用业务场景:去重
SADD key member1 member2 #向集合添加一个或多个成员
SCARD key #返回集合存储的key的基数 (集合元素的数量)
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SCARD myset
(integer) 2
redis>
SMEMBERS key #显示set中所有元素
redis> SADD myset "Hello"
(integer) 1
redis> SADD myset "World"
(integer) 1
redis> SMEMBERS myset
1) "World"
2) "Hello"
redis>
SISMEMBER key member #判断 member 元素是否是集合key的成员
redis> SADD myset "one"
(integer) 1 #如果member元素是集合key的成员,则返回1
redis> SISMEMBER myset "one"
(integer) 1
redis> SISMEMBER myset "two"
(integer) 0 #如果member元素不是key的成员,或者集合key不存在,则返回0
redis>
SPOP key # 随机的删除一个元素 并返回这个元素
SADD myset "one"
SADD myset "two"
SADD myset "three"
SPOP myset
SMEMBERS myset
SADD myset "four"
SADD myset "five"
SPOP myset 3
SMEMBERS myset
SREM key member1 member2 #移除集合中一个或多个成员示例
5、zset类型(stored set)
#去重 有序(内部有自己的排序规则 根据double分数进行排序的)
stored set也是string类型的元素集合,且不允许重复。
不同于set的是,每一个元素都会关联一个double类型的分数。
redis正是通过分数来为集合中成员进行排序的 从小到大排序。
集合元素一定不重复唯一,但是分数可以重复。
#业务场景:排行榜 排序去重的业务
ZADD key score1 member1 [score2 member2] #向有序集合添加一个或多个成员,或者更新已存在成员的分数
node-1:6379> ZADD booktop 97 java
(integer) 1
node-1:6379> ZADD booktop 100 bigdta
(integer) 1
node-1:6379> ZADD booktop 95 python
(integer) 1
node-1:6379> ZADD booktop 60 php
(integer) 1
ZRANGE key start stop [WITHSCORES] #通过索引区间返回有序集合指定区间内的成员
#返回的元素可以认为是按得分从最低到最高排列。 如果得分相同,将按字典排序。
node-1:6379> ZRANGE booktop 0 -1 withscores
1) "php"
2) "60"
3) "python"
4) "95"
5) "java"
6) "97"
7) "bigdta"
8) "100"
ZREVRANGE key start stop [WITHSCORES] #通过索引区间返回有序集合指定区间内的成员
#其中成员的位置按score值递减(从大到小)来排列。具有相同score值的成员按字典序的反序排列。
node-1:6379> ZREVRANGE booktop 0 -1 withscores
1) "bigdta"
2) "100"
3) "java"
4) "97"
5) "python"
6) "95"
7) "php"
8) "60"
ZREVRANK key member
#返回有序集key中成员member的排名,其中有序集成员按score值从大到小排列。排名以0为底,也就是说,score值最大的成员排名为0。
node-1:6379> ZREVRANK booktop java
(integer) 1
ZRANK key member
#返回有序集key中成员member的排名。其中有序集成员按score值递增(从小到大)顺序排列。排名以0为底,也就是说,score值最小的成员排名为0。
node-1:6379> ZRANK booktop java
(integer) 2
#有序集合中元素分数的修改操作
ZINCRBY key increment member #有序集合中对指定成员的分数加上增量
node-1:6379> ZINCRBY booktop 2 python
"97"
#返回有序集key中,成员member的score值。
node-1:6379> ZSCORE booktop java
"101"
#移除元素
zrem key memvber .....
6、key通用操作命令
#redis中所有的索引都是从0开始的
#显示当前数据库所有的key
keys *
key 支持查找所有符合给定模式pattern(正则表达式)的 key 。
#以秒为单位返回指定的key剩余过期时间
ttl key
当key不存在的时候,返回-2;
当key存在但是没有设置ttl,返回-1
当key存在且设置了ttl,返回的是剩下的时间 以s为单位
#删除指定的key
DEL key
#给指定的key设置ttl时间 以秒为单位
expire key seconds
#移除key过期超时时间
PERSIST key
注意:要在ttl时间结束之前移除 否则到时间该key就被删除了
#查看指定key所对应的value数据类型
node-1:6379> TYPE list1
list
node-1:6379> TYPE set1
set
node-1:6379> TYPE name
string
node-1:6379> TYPE myset:__rand_int__
hash
#判断key是否存在
node-1:6379> EXISTS hhahaah
(integer) 0 #表示不存在
node-1:6379> EXISTS name
(integer) 1 #表示存在
#select 编号(0-15):切换数据库
redis内部默认情况下有16张数据库 标号从0开始。默认情况下使用0号库。可以使用selet 选择不同的数据库。 数据库之间彼此都是隔离的。
node-1:6379[1]> SELECT 4 #可以通过【数字】来判断当前操作的是哪一个数据库
OK
node-1:6379[4]> SELECT 0
OK
node-1:6379>
六、java操作Redis—jedis
-
使用jedis操作redis
redis官方针对java语言提供了多套api,这里推荐使用jedis。其最大的特点就是各种数据类型操作方法和shell命令行中是一样的 会极大减少学习的成本。 https://github.com/xetorthio/jedis
-
重点:jedis链接池 封装jedis链接池工具类
-
创建工程 依赖问题
<dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.9</version> </dependency> </dependencies>
-
jedis入门案例
* todo jedis入门案例 * 1、创建jedis对象 在对象中需要指定redis ip 端口 * 2、执行相关操作 * 3、是否资源 关闭连接 */ @Test public void JedisTestOne(){ //1、创建jedis对象 在对象中需要指定redis ip 端口 Jedis jedis = new Jedis("node-1", 6379); //2、执行相关操作 String pong = jedis.ping(); System.out.println(pong); //是否资源 关闭连接 jedis.close(); } //此种方式会频繁和redis进行连接的创建和关闭 不适合开发 开发中推荐使用jedis链接池
-
jedis链接池
/** * todo jedis 链接池使用 * 1、创建链接池对象 需要指定redis ip 端口 * 2、从链接池中获取链接 Jedis对象 * 3、执行相关操作 * 4、是否资源 把链接返回池子中 */ @Test public void JedisTestTwo(){ //1、创建链接池对象 需要指定redis ip 端口 //创建链接池配置对象 用于指定链接池相关属性 JedisPoolConfig config = new JedisPoolConfig(); JedisPool jedisPool = new JedisPool(config,"node-1", 6379); //2、从链接池中获取链接 Jedis对象 Jedis jedis = jedisPool.getResource(); //3、执行相关操作 String pong = jedis.ping(); System.out.println(pong); //4、是否资源 把链接返回池子中 jedis.close(); } //直接写链接池 同样不适合开发环境 通常要把这一段逻辑封装成一个工具类JedisUtil
-
jedisUtl工具类封装
//要求 1、工具类所创建的链接池全局唯一 单例 static 随着类的加载被加载创建 只创建一次 //2、能够在工具类中提供一个统一的方法 获取redis的链接 /** * @author: Allen Woon */ public class JedisUtils { //保证该成员变量无法被直接访问使用 私有的 private static JedisPool jedisPool; //保证链接池对象全局唯一 单例 随着类的加载被创建 且只创建一次 static { //创建链接池配置对象 用于指定链接池相关属性 JedisPoolConfig config = new JedisPoolConfig(); config.setMaxIdle(5);//闲时最大的数量 config.setMaxTotal(100);//最大的链接数量 config.setMinIdle(3);//闲时最小的数量 jedisPool = new JedisPool(config,"node-1", 6379); } //从链接池中获取链接的方法 public static Jedis getJedis(){ return jedisPool.getResource(); } }
七、Redis持久化策略
作为内存中Nosql数据库 redis支持把数据内存的数据持久化到磁盘中 避免断点丢失问题。
redis提供了两种持久化方案:RDB、AOF
1、RDB(redis database)
#在进程RDB持久化的时候 redis会fork出一个子进程专门用于持久化。
#首先将数据写入一个临时文件 持久化结束之后 再用这个临时文件去替换上次持久化的文件。整个过程主进程进行任何操作,确保了极高的性能。
fork的作用是复制一个跟原进程完全一样的进程,新进程中数据状态、环境变量等跟原进程一样。但是是一个全新的进程,并作为原进程的子进程运行。
#缺点:最后一次RDB容易产生数据丢失。如果要求是大规模数据恢复 并且要求精度不高。可以选用rdb
#默认RDB是开启的
save 900 1 : 在900秒之内,如果有一个数据进行修改,就会执行一下保存
save 300 10 : 在300秒之内, 如果有10个以上的数据被修改, 就会执行一下保存
save 60 10000 : 在60秒之内, 如果有10000个以上的数据被修改. 就会执行一下保存
dbfilename dump.rdb #持久化文件名称
dir /export/data/redis/6379/#保存的路径
#如果需要关闭rdb 把save参数注释掉 给save命令设置一个空字符串
save ""
#相关的其他参数
stop-writes-on-bgsave-error yes #如果持久化save失败 前端停止写操作
rdbcompression yes #是否开启压缩 LZF
rdbchecksum yes #持久化完毕是否进行数据校验
2、AOF(append only file)
#redis提供基于日志机制实现的持久化技术。会将用户的操作完整的记录下来保存追加到文件中。当服务器重启 的时候 根据日志记录重演操作 恢复数据。 还支持后端对AOF文件进行重新避免该文件体积过大。
#默认不开启aof
appendonly yes #是否开启 默认不开启
appendfilename "appendonly.aof" #文件的名称
appendfsync everysec # [always everysec no]
#优点:可以将用户所有操作记录下来 数据保存完整 不容易丢失
#缺点:持久化文件过大 不利于恢复
- 如果两种策略都开启 默认先AOF 精度高。
- 注意:如果把redis当成缓存数据库来使用。可以不开启持久化技术
八、Redis部署架构
1、单机架构
2、主从架构(master/slave)
-
解决:解决读写分离。
-
概念
俗称主从复制架构。主要职责就是为了实现读写分离。 主机数据更新之后根据配置、策略,自动同步到从机上。 master负责写,slave负责读。
-
如何配置 配从不配置主
#配从不配置主 在从机上添加一行参数 slaveof 主ip 端口
-
安装部署
#1、把安装包scp给其他两台机器 scp -r redis/ root@node-2:$PWD scp -r redis/ root@node-3:$PWD #2、在node-2 node-3创建一些文件夹 日志 数据 mkdir -p /export/logs mkdir -p /export/data/redis/6379/ #3、修改node-2 node-3上redis配置文件 bind 主机名 # 千万别忘了 slaveof 主ip 端口 #复制哪台机器 #4、启动3台redis #5、验证从机无法进行写操作 node-2:6379> set hobby1 ball (error) READONLY You can't write against a read only slave.
3、sentinel架构(哨兵模式)
-
背景
主从复制架构中 解决读写分离 但是master是单点故障。如何产生master高可用? 所谓的master挂掉之后 能够选举出来一个新的master。继续领导大家对外提供服务。
-
概念
哨兵模式是一种特殊的模式。 哨兵是一个独立的进程。会独立运行。通过发送命令检测redis进程健康问题。 哨兵系统通常由多个哨兵进程组成,可以去监视主机和各个从机。并在主机下线出现故障的时候,自动将从机中某个选举成为新的master。
-
哨兵作用
1、监视redis服务器健康状态。包括监视主机和从机。 2、master切换选举问题。然后通过发布订阅功能把消息通知给各个从机,修改配置文件,切换主机。
-
配置(3台机器都需要配置哨兵)
cd /export/servers/redis/conf vim sentinel.conf # 配置监听的主服务器,这里sentinel monitor代表监控,mymaster代表服务器的名称,可以自定义,node-1代表监控的主服务器,6379代表端口,2代表只有两个或两个以上的哨兵认为主服务器不可用的时候,才会进行failover操作。 #修改bind配置,每台机器修改为自己对应的主机名 bind node-1 #配置sentinel服务后台运行 daemonize yes #修改三台机器监控的主节点,现在主节点是node01服务器 sentinel monitor mymaster node-1 6379 2
-
启动哨兵进程 3台机器都需要启动
cd /export/servers/redis/bin/ ./redis-sentinel ../conf/sentinel.conf
-
jedis中如何添加哨兵信息
// 哨兵信息 Set<String> sentinels = new HashSet<>(Arrays.asList("node01:26379", "node02:26379","node03:26379")); // 创建连接池 JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels,jedisPoolConfig);
4、redis cluster集群模式
-
背景
当数据量过大的时候,大到一台服务器存储不下。不管是主从架构还是哨兵架构都无法解决数据存不下的问题。 这时候就需要对存储数据进行拆分,将数据部署存储在多台机器上。 cluster集群模式就应运而生。
-
概念
redis cluster集群模式是redis水平扩容,部署N台机器,将所有的数据分布存储在各个机器上。 cluster集群模式像是主从架构和sentinel结合体。cluster内部可以实现主从复制和master重新选举机制。
-
特点
多个redis之间互联 数据共享。 要求所有的节点必须是一主一从(也可以一主多从)。其中从不提供服务的,仅作为备用。 不支持同时处理多个key 比如:mset mget。因为redis需要把key分布式存储在不同机器上,并发量很高的情况下,同时操作key效率不高。 支持在线增加节点 删除节点。 客户端可以连接到任何一个节点上操作,内部自动跳转。
-
内部细节
-
存储数据的时候如何分配?
redis集群中内置了16384个哈希槽(坑位),均匀分布在3台机器上。 node-1:0-5000 node-2:5001-10000 node-3:10001-16383 当放置一个key的时候。首先对key通过一个算法CRC16算法计算一个值。 CRC16(key) % 16384 =余数 对应着哈希槽。
-
如何实现容错?
投票过程是整个集群中所有master实例参与。 如果半数master与某个节点通信失败 就任务那个节点出现故障了把他的备机切换成为主。
-
什么整个集群不可用
1、如果任意一个master挂掉 但是其没有slave 集群进行失败状态。 2、如果集群中超过半数master挂掉 整个集群进入失败状态。
-
-
cluster安装部署(伪分布式 单机使用不同的端口模拟集群模式)
redis集群搭建至少需要3主3从。需要6个redis实例。 这里在一台机器上进行伪分布式安装,使用不同的端口来启动redis. 跟着讲义 心细。 在使用客户端连接的时候 一定要加上-c参数 可以自动在集群多台机器间跳转。
九、Redis面试必问问题
Redis作为缓存服务器的使用流程
前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。
1、缓存穿透
描述:
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据。这时的用户很可能是攻击者,攻击会导致数据库压力过大。缓存不起作用,穿透缓存,直捣DB.
解决方案:
布隆过滤器
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击。
采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
2、缓存雪崩
描述:
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
解决方案:
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据均匀分布在不同搞得缓存数据库中。
设置热点数据永远不过期。
3、缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
(1)设置热点数据永远不过期。
雪压枝头低,虽低不着迷,一朝红日出,依旧与天齐