Redis基础总结

Redis基础总结

1、NoSQL出现的背景

单机MySQL→Memcached(缓存)+MySQL+垂直拆分→MySQL主从读写分离→分表分库+水平拆分+MySQL集群

MySQL存在的瓶颈

  • MySQL数据库也经常存储一些大文本字段,导致数据库表非常的大,在做数据库恢复的时候就导致非常慢,不容易快速数据库
  • 表结构更改困难

NoSQL–Not Only SQL

  • 泛指非关系型数据库
  • 数据存储不需要固定的模式,无需多余操作就可以横向扩展

Nosql特点

  • 易扩展:去关系型数据库的关系型特性
  • 大数据量高性能:读写性能高、数据库的结构简单,细粒度的Cache(记录级)
  • 多样灵活的数据模型:无需事先建立字段,随时可以存储自定义的数据格式

RDBMS与NoSQL比较

  • RDBMS
    • 高度组织化结构化数据
    • 结构化查询语言(SQL)
    • 数据和关系都存储在单独的表中
    • 数据操纵语言,数据定义语言
    • 严格的一致性
    • 基础事务
  • NoSQL
    • 代表不仅仅是SQL
    • 没有声明查询语言,没有预定义的模式
    • 键-值对存储,列存储,文档存储,图形数据库
    • 最终一致性,而非ACID属性
    • 非结构化和不可预知的数据
    • CAP定理
    • 高性能、高可用和可伸缩性

ACID和CAP

  • ACID:原子性、一致性、隔离性、持久性
  • CAP:强一致性、高可用性、分布式容忍性

2、Redis入门

2.1 Redis初了解

Redis:Remote Dictionary Server 远程字典服务器

是完全开源免费的,用C语言编写的,遵守BSD协议,是一个高性能的(key/value)分布式内存数据库,基于内存运行并支持持久化的NoSQL数据库,是当前最热门的NoSql数据库之一,也被人们称为数据结构服务器。

Redis特点

  • 支持数据持久化
  • 支持key-value类型数据,同时还提供list、set、zset、hash等数据结构
  • 支持数据备份,即master-slaver模式的数据备份

Redis杂项基础知识

  • 简单命令
# 开启redis服务
redis-server /myredis/reids.conf
redis-cli -p 6379
    
# 查看进程是否开启
ps -ef|grep redis
# 关闭进程、关闭redis
shutdown
exit
    
# 切换数据库,select dataNum[0-15]
select 1
# 查看数据库中所有的key
keys *
# 清除当前数据库
flushdb
# 清除所有数据库的内容
FLUSHALL
  • Redis是单进程:单进程模型来处理客户端的请求。对读写等事件的响应
    • Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是机器的内存和网络带宽,Redis是C语言编写的,官方提供的数据达到100000+的QPS(每秒内查询次数),这个数据不比Memcached差(单进程多线程的基于内存的Key-Value数据库)
  • 默认16个数据库,表从零开始,初始默认使用零号库

2.2 五大数据类型

Redis:key

keys *			# 查询数据库所有的key
set key value	# 设置值
get key			# 获得值
exists key		# 判断当前的key是否存在
expire key 10	# 设置key的过期时间,单位秒
ttl key 		# 查看当前key存活的时间
type key		# 查看当前key的类型

String:字符串

set key value	# 设置值
get key			# 获得值
exists key		# 判断当前的key是否存在
append key hello	# 追加字符串hello,如果当前key不存在,就相当于设置值
strlen key		# 获取字符串的长度
incr key		# 自增1
desr key		# 自减1
incrby key 10	# 设置步长,指定增量
decrby key 10
getrange key 0 3	# 截取字符串[0,3],[0,-1]获取全部字符串
setex			# 设置过期时间
setnx			# 判断不存在,设置值
mset			# 设置多个值
mget
msetnx			# 原子性操作,有一个失败就全部失败

List:列表

list: 栈、队列、阻塞队列

lpush list value	# 将一个值或多个值,插入到列表头部(左,后进先出)
lrange list 0 1 	# 获取区间具体的值,[0,-1]获取list全部值
rpush list value	# 将一个值或多个值,插入到列表尾部(右,先进先出)

lpop list		# 移除list的第一个元素
rpop list		# 移除list最后一个元素
lrem list 2 one	# 移除list指定个数的value,精准匹配(移除key中2个one)

ltrim list 1 2	# 截取指定下标长度的list,并把截取部分赋值给list
rpoplpush list otherlist	# 移除list的最后一个元素,并将它移动到新的list中
exists list		# 判断这个列表是否存在

lset list 0 re	# 更新list中0下标的值为re
linsert list before[after] world other	# 将other插入到列表list的world前面或后面
  • list的底层是链表结构
  • 在两边插入或改动值效率最高,中间元素相对效率低一点
  • 消息队列(lpush、rpop),栈(lpush、lpop)

Set:集合

set值是无序,不重复

sadd set1 value ...		# 添加值到set1
smembers set 1			# 查看set1的所有值
sismember set1 hello 	# 判断hello是否在set1中
scard set1				# 获取set1集合的元素个数
srem set1 hello			# 移除set1集合中的hello元素
srandmember set1 [2]	# 随机抽出元素,可以指定抽出元素个数,默认为1
spop set1 				# 随机删除set集合中元素,可以指定精确删除的值
smove set1 setother xuzy # 将元素xuzy从set1移动到setother

sdiff set1 set2		# 差集
sinter set1 set2	# 交集
sunion set set2 	# 并集

Hash:哈希

Map集合,key-value

hset hash1 field1 value ...		# 设置一个hash,值为field1-value
hget hash1 field1 field2 ...	# 获取值
hgetall hash1					# 获取所有数据
hdel hash1 field1				# 删除hash1里的field1字段
hlen hash1 				# 获取hash1的字段数量
hexists hash1 field1	# 判断field1在hash1中是否存在
hkeys hash1 			# 获取hash1中所有field
hvals hash1				# 获取hash1中所有的value

hincrby hash1 field1 1	# 指定增量为1
hsetnx hash1 field hello 	# 如果不存在可以设置,如果存在着不能设置

Zset:有序集合

zadd zset1 score1 v1	# 添加值
zrange zset1 0 -1		# 获取所有值

zrangebyscore zset1 -inf +inf [withscores] 	# 显示所有值,并且根据score从小到大,[附带成绩],inf也可用实际的数值表示条件筛选

zrem zset1 xuzy		# 删除指定元素xuzy
zcard zset1			# 获取有序集合的个数

zcount zset1 1 3    # 获取指定score区间的成员数量

2.3 三种特殊数据类型

Geospatial:地理位置

# geoadd key 经度 纬度 城市
geoadd city 116.40 39.90 beijing
geopos city beijing

# 两地(人)距离 单位
geodist city beijing guangzhou km
# 以给定的经纬度为中心,找出某一个半径内的元素,[显示到中心点距离],[显示半径内的元素的定位信息]
georadius city 110 20 1000 km [withdist] [withcoord]
# 筛选结果的数量
georadius city 110 20 1000 km count 1
# 找出位于指定元素(指定)周围的其他元素
georadiusbymember china:city beijing 1000 km

# 将二维的经纬度转换为字符串,两个字符串越接近,距离越近
geohash city beijing guangzhou
  • 延伸
# geo底层实现原理是Zset,因此可以使用Zset的命令操作geo
# 查看全部元素
zrange city 0 -1
# 删除指定元素
zrem city beijing

Hyperloglog:基数

基数:不重复的元素,可以接受误差,如果不能容错,就使用set集合统计

Redis Hyperloglog 基数统计算法

pfadd key value1 value2 ...		# 创建第一组元素
pfcount key						# 统计key元素的基数数量
pfmerge key3 key key2			# 将key和key2合并到key3 并集

Bitmap:位存储

存储二进制记录,即只有0和1两种状态

# setbit key offset value 设置值
setbit sign 0 1
# getbit key offset	获取值
getbit sign 0
bitcount sign	# 统计值为1的个数

3、事务

**Redis事务:**一组命令的集合、所有命令序列化执行。

一次性、顺序性、排他性

注意:Redis单条命令具有原子性,但事务不保证原子性

  • 开启监控(watch)/【unwatch解锁】
  • 开启事务(multi)
  • 命令入队
  • 执行事务(exec)/取消事务(discard)

编译期异常,事务所有命令都不会被执行。

运行期异常,错误命令无法执行,其他命令可以正常执行。(这是事务没有原子性的原因)

乐观锁

  • 基于数据版本的记录机制实现
  • 使用watch监视键值对,如果事务提交exec时,watch监视的键值对发生变化,事务将被取消

4、java连接Redis数据库

4.1 Jedis

解决连接虚拟机的Redis时出现的拒绝访问和超时的问题

https://blog.csdn.net/DragonFreedom/article/details/79512686?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

  1. 导入依赖
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.3.0</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
</dependency>
  1. 测试
public static void main(String[] args) {
    Jedis jedis = new Jedis("192.168.204.128",6379);
    System.out.println(jedis.ping());
}

常用API接口方法,与上述2命令一致。

4.2 SpringBoot整合Redis

springboot操作数据:spring-data jpa jdbc mongodb redis

springboot2.x以后,底层由原来的jedis替换为lettuce

  • jedis:采用直连方式,多线程操作,不安全。如果想避免不安全,使用jedis pool连接池。
  • lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全的情况,可以减少线程数据。

手写一个RedisConfig替换自动配置redisTemplate组件

@Configuration
public class RedisConfig {
	// 自己定义了一个 RedisTemplate
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory
    factory) {
        // 我们为了自己开发方便,一般直接使用 <String, Object>
        RedisTemplate<String, Object> template = new RedisTemplate<String,
        Object>();
        template.setConnectionFactory(factory);
        // Json序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
        Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // String 的序列化
        StringRedisSerializer stringRedisSerializer = new
        StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

5、解读Redis.conf

启动redis的配置文件

redis-server /../redis.conf
redis-cli -p 6379

配置文件配置内容解读

# unit单位,大小写不敏感

# include 包含 引入其他配置文件
# include /path/to/local.conf

# 网络
bind 127.0.0.1 		# 绑定IP,只有绑定的IP才能访问
protect-mode yes	# 保护模式,默认yes
port 6379			# 端口设置

# general 通用
daemonize yes		# 以守护进程方式运行,默认是no
pidfile /var/run/redis_6379.pid		#以后台方式运行,需要指定一个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 ""		# 日志文件位置名
databases 16	# 数据库数量,默认16
always-show-logo yes	# 是否显示logo

# snapshot 快照
# RDB持久化配置区域
save 900 1	# 900s内,至少有1个key进行修改,则触发持久化操作
save 300 10
save 60 10000

stop-write-on-bgsave-error yes	# 持久化出错,是否还继续工作
rdbcompression yes		# 是否压缩rdb文件,需要消耗一点CPU资源
rdbchecksum yes			# 保存rdb文件的时候,进行错误检查校验
dir ./		# rdb文件保存目录

# replication 复制
# 主从复制设置

# security 安全
# 设置redis密码,默认没有密码

# clients 限制
maxclients 10000	# 设置能连接上redis的最大客户端数量
maxmemory <bytes>	# 配置最大内存容量
maxmemory-policy noeviction 	# 内存到达上限之后的处理策略
    1、volatile-lru:只对设置了过期时间的key进行LRU(默认值)
    2、allkeys-lru : 删除lru算法的key
    3、volatile-random:随机删除即将过期key
    4、allkeys-random:随机删除
    5、volatile-ttl : 删除即将过期的
    6、noeviction : 永不过期,返回错误
    
# append only file 模式
# AOF持久化配置
appendonly no	# 默认不开启AOF模式,大部分场景下RDB完全够用
appendfilename "appendonly.aof"		# 持久化的文件名字

# appendfsync always # 每次修改都会 sync。消耗性能
appendfsync everysec # 每秒执行一次 sync,可能会丢失这1s的数据!
# appendfsync no # 不执行 sync,这个时候操作系统自己同步数据,速度最快!

auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb		# 如果aof文件大于64mb,将自动fork一个新的进程来将重写

6、Redis持久化

Redis是内存数据库,如果不将内存中的数据持久化到磁盘中,一旦服务器进程退出,则服务器终端数据库状态也会消失,因此Redis提供了持久化功能。

Redis持久化有两种方法:RDB和AOF

6.1 RDB

Redis会单独创建(fork)一个子进程来进行持久化,首先将数据写入一个临时RDB文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程不进行任何IO操作,确保了极高的性能。如果需要进行大规模数据恢复,且对数据恢复的完整性不是很敏感,那么RDB方式比AOF方式要更加高效。

缺点:

  • 如果发生宕机,最后一次持久化的数据可能丢失
  • fork进程的时候,会占用一定的内容空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NDvaq9Mv-1592626541052)(C:\Users\徐梓泳\AppData\Roaming\Typora\typora-user-images\image-20200620001631392.png)]

6.2 AOP(Append Only File)

以日志形式记录每个写操作,将Redis执行的所有指令记录下来(读操作不记录),只许追加文件,但不可以改写文件,Redis启动前会读取该文件重构数据。

redis提供一个检查校验工具:redis-check-aof --fix

优点:

  • 每一次修改都同步,数据同步的完整性更好
  • 每秒同步一次,最多丢失1s数据
  • 从不同步的效率最高,由操作系统自己同步数据

缺点:

  • 数据文件大小:aof远大于rdb,修复的速度也比rdb慢
  • 运行效率:aof运行效率比rdb慢,redis默认的配置就是rdb持久化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-11ykI3Jx-1592626541054)(C:\Users\徐梓泳\AppData\Roaming\Typora\typora-user-images\image-20200620093542105.png)]

7、主从复制

主从复制(master-slave)主节点Redis服务器的数据,复制到从节点Redis服务器。

默认情况下,每台Redis服务器都是主节点。

主从复制的作用:

  • 数据冗余:实现数据的热备份,是持久化之外的一种数据冗余方式
  • 故障恢复:当主节点发生问题,可以由从节点提供服务,实现快速的故障恢复
  • 负载均衡:读写分离,提高Redis服务器的并发量
  • 高可用(集群):Redis高可用的基础

环境配置:只配置从库

info replication	# 查看当前库信息
slaveor ip port		# 设置作为主机(ip)的从机

复制原理

  • Slave启动成功连接master后会发送一个sync同步命令
  • master在第一次接到同步命令时,会进行一次全量复制,后面开始做增量复制
    • 全量复制:一次完全同步操作
    • 增量复制:master将新的修改命令依次传给slave,完成同步

哨兵模式

哨兵(Sentinel)模式:后台监控注解是否故障,如果故障将根据投票数自动将从库转换为主库,其他从库作为新选定的主库的从库。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7T2yjm02-1592626541057)(C:\Users\徐梓泳\AppData\Roaming\Typora\typora-user-images\image-20200620110044252.png)]

  1. 配置哨兵配置文件sentinel.conf

    # sentinel monitor 被监控的名称 host port 1
    sentinel monitor myredis 127.0.0.1 6379 1
    
  2. 启动哨兵

    redis-sentinel /../sentinel.conf
    

注意:主机宕机重连后,会成为新主机的从机

8、Redis缓存穿透和雪崩

缓存穿透

缓存穿透是指缓存和数据库都没有的数据,而用户不断发起请求。由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到的数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层查询,失去缓存的意义。

解决方案:

  • 接口层增加校验:用户鉴权校验、id基础校验
  • 在缓存取不到的数据,在数据库中也没有取到的,这时将key-value写入key-null、设置缓存存活时间(setnx)短点,可以防止用同一个id进行的暴力攻击,对一些需要保持一致性的业务有影响

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户太多,引起数据库压力瞬间增大。

解决方案:

  • 设置热点数据永不过期
  • 接口限流与垄断,降级。重要的接口做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口的某些服务不可用时,进行熔断,失败快速返回机制
  • 布隆过滤器:类似一个hash set,用于快速判断某个元素是否存在集合中
  • 加互斥锁:
    • 分布式锁:保证对应每个key同时只有一个线程去查询后端业务,其他线程没有获得分布式锁权限,因此只需要等待即可。

缓存雪崩

缓存雪崩是指缓存中数据大批量到过期时间,而查询量巨大,引起数据库压力过大甚至宕机。和缓存击穿不同,缓存击穿是指并发查同一条数据,缓存雪崩是指不同数据都过期了,很多数据在缓存查不到从而查数据库。

解决方案:

  • 缓存数据过期时间设置随机,防止同一时间大量数据过期
  • 如果缓存数据库是分布式部署,可以将热点数据均匀分布在不同的缓存数据库中
  • 设置热点数据永不过期

参考文献

1、【狂神说Java】Redis最新超详细版教程通俗易懂:https://www.bilibili.com/video/BV1S54y1R7SB

2、【面试】redis缓存穿透、缓存击穿、缓存雪崩区别和解决方案:https://blog.csdn.net/fcvtb/article/details/89478554

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值