Redis
2021-12-23 14:51:53 0 举报
AI智能生成
redis学习笔记
作者其他创作
大纲/内容
介绍
Redis是一种运行速度很快,并发能力很强,并且运行再内存上的Nosql数据库
非关系型数据库(Nosql)
Redis,Memcahe,MongoDB..
Redis,Memcahe,MongoDB..
优势:
1.处理高并发,大批量数据的能力强。
2.支持分布式集群,负载均衡,性能高。
3.内存级数据库,查询速度快。
4.存储格式多,支持key-value形式,文档形式,图片形式。
5.没有多表连接查询机制和限制,扩展性高。
1.处理高并发,大批量数据的能力强。
2.支持分布式集群,负载均衡,性能高。
3.内存级数据库,查询速度快。
4.存储格式多,支持key-value形式,文档形式,图片形式。
5.没有多表连接查询机制和限制,扩展性高。
缺点:
1.不支持sql工业的标准。
2.没有join等复杂的连接操作。
3.事务处理能力弱。
4.没有完整性约束,对于复杂业务场景支持较差。
1.不支持sql工业的标准。
2.没有join等复杂的连接操作。
3.事务处理能力弱。
4.没有完整性约束,对于复杂业务场景支持较差。
关系型数据库:
mysql,sql,oracle..
mysql,sql,oracle..
优点:
1.通过事务处理保持数据的一致性。
2.数据更新的开销很小。
3.可以进行Join等复杂查询。
1.通过事务处理保持数据的一致性。
2.数据更新的开销很小。
3.可以进行Join等复杂查询。
缺点:
1.数据独写必须经过sql解析,大量数据,高并发下独写性能不足。
2.为保证数据一致性,需要加锁,影响并发操作。
3.无法适应非结构化存储。
4.扩展困难
1.数据独写必须经过sql解析,大量数据,高并发下独写性能不足。
2.为保证数据一致性,需要加锁,影响并发操作。
3.无法适应非结构化存储。
4.扩展困难
使用场景
缓存
一些频繁被访问的数据,经常被访问的数据如果放在关系型数据库,
幂次查询的开销会很大,而放在redis中,因为redis是放在内存中的
可以高效的访问。
幂次查询的开销会很大,而放在redis中,因为redis是放在内存中的
可以高效的访问。
排行榜
在使用传统的关系型关系数据库,来做这个功能,非常麻烦
而使用Redis的SortSet(有序集合)数据解构能够简单的搞定。
而使用Redis的SortSet(有序集合)数据解构能够简单的搞定。
计算器/限速器
利用Redis中原子性的自增操作,我们可以统计类似用户点赞数,
用户访问数等。这类操作如果使用Mysql,频繁独写会嗲来相当大的压力,
限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,
防止用户疯狂点击嗲来不必要的压力。
用户访问数等。这类操作如果使用Mysql,频繁独写会嗲来相当大的压力,
限速器比较典型的使用场景是限制某个用户访问某个API的频率,常用的有抢购时,
防止用户疯狂点击嗲来不必要的压力。
好友关系
利用集合的一些命令,比如求交集,并集,差集等。
可以方便搞定一些共同好友,共同爱好之类的功能。
可以方便搞定一些共同好友,共同爱好之类的功能。
简单消息队列
除了Redis自身的发布/订阅模式,我们也可以利用List来实现一个队列机制,
比如:到货通知,右键发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,
完全可以用List来完成异步解耦。
比如:到货通知,右键发送之类的需求,不需要高可靠,但是会带来非常大的DB压力,
完全可以用List来完成异步解耦。
Session共享
以jsp为例,默认Session是保护在服务器的文件中,如果是集群服务
同一个用户过来可能落在不同的机器上,这就会导致用户频繁登录;采用Redis保存Session后,
无论落在哪台机器上都能够获取到对应的Session信息。
同一个用户过来可能落在不同的机器上,这就会导致用户频繁登录;采用Redis保存Session后,
无论落在哪台机器上都能够获取到对应的Session信息。
Redis/Memcache/MongoDB对比
Redis和Memcache
1.Redis和Memcache都是内存数据库,不过memcache还可用户缓存其他东西,例如图片,视频等。
2.Memcache数据结构单一kv,Redis更丰富一些,Redis提供String,list,set,hashzset数据结构的存储,有效的减少网络IO次数。
3.虚拟内存-Redis当物理内存用完时,可以将一些很久没有用到的value交换到磁盘。
4.存储数据安全 -Memcache挂掉后,数据没了(没有持久化机制);redis可以定期把偶从你到磁盘(持久化)
5.灾难恢复-Memche挂掉后,数据不可恢复,Redis数据丢失后,可以通过RBD或AOF恢复。
Redis和MongoDB
1.Redis和MongoDB并不是竞争关系,更多的是一直哦弄个协作共存的关系。
2.MongoDB本质上还是硬盘数据库,在复杂查询时,仍然会有大量的资源消耗,
而且在处理负载逻辑时,仍然要不可避免地进行多次查询。
而且在处理负载逻辑时,仍然要不可避免地进行多次查询。
3.这时就需要redis或Memcache这样的内存数据库来作中间层进行缓存和加速。
4.比如在某些复杂的页面的场景中,整个也i按的内容如果都从MongoDB中查询,
可能要十几个查询语句,耗时很长。如果需求允许,则可以把整个页面的对象缓存至Redis中
,定期更新。这样MongoDB和Redis就能很好地协作起来
可能要十几个查询语句,耗时很长。如果需求允许,则可以把整个页面的对象缓存至Redis中
,定期更新。这样MongoDB和Redis就能很好地协作起来
数据库
分布式数据库
CAP原理
C(Consistency):强一致性
所有节点在同一时间的数据完全一致,这就是分布式的一致性。
A(Availability):高可用性
服务一直可用,而且要是正常的响应时间。
P(Partition tolerance):分区容错性
即分布式系统在遇到某节点或网络分区故障时,仍然能够对外提供满足一致性或可用性的服务。
CAP总结
分区是常态,不可避免,三者不可共存。
可用性和一致性是一对冤家
一致性高,可用性低。
一致性低,可用性高。
三大类原则
CA:单点集群,满足一致性,可用性的系统,通常在可拓展性上不太强。
CP:满足一致性,分区容错性的系统,通常性能不是特别高。
AP:满足可用性,分区容错性的系统,通常一致性要求低一些
传统的关系数据库
ACID原理
A:原子性
原子性是指事务是一个不可再分割的工作单位,事务中操作要么都发生,要么都不发生。
C:一致性
一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这就说数据库事务不能破坏
关系数据的完整性以及业务逻辑上的一致性。
关系数据的完整性以及业务逻辑上的一致性。
I:独立性
多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。
D:持久性
持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库中,并不会被回滚。
常用命令
系统
已配置文件方式启动:redis-server/opt/redis-5.0.4/redis.conf
进入redis:redis-cli
单实例关系:redis-cli shutdown
性能测试:redis-benchmark(ctrl+C停止)
String
add
保存key数据:set key china
往key中追加数据(abc):append key abc
返回字符长度
加减操作(必须是数字类型)
key自增1(++):incr key
key自减1(--):decr key
key自增3(+=3):incrby key 3
key自减(-=2):decrby key 3
添加数据的并添加生命周期
添加key键值v1时同时设置5秒的生命周期:setex key 5 v
nil:已过期
添加key键值v1时同时设置5秒的生命周期(判断是否存在,防止被覆盖):setnx key 5 v
0:添加失败,数据已存在
1:添加成功,数据不存在
update
移动键到几号库:move key db
delete
清空当前数据库:flushdb
清空所有数据库:fluashall
删除数据key:del key
select
获取key数据:get key
切换数据库:select 16 (切换16号数据库)
数据库数量:dbsize(默认16个数据库)
模糊查询
查询所有键: keys *
查询k开头:keys k*
查询e结尾:keys *e
查询包含k键:keys *k*
查询k开头,后匹配一个字符:keys k?
查询k开头,后匹配两个字符:keys k??
第二个字母可能是a或者e:keys r[ae]dis
判断看key是否存在:exists key
存在:(integer) 1
不存在:(integer) 0
查看key键过期时间:ttl key
永不过期:-1
已过期:-2
为key键设置过期时间(倒计时):expire key 秒
查看key键的数据类型:type key
查询key键值长度:strlen key
查询范围
查询key全部值:getrange key 0 -1
查询key的值,返回时下标0~下标3(包含0和3),共返回4个字符:getrange key 0 3
替换key的值,从下标1开始提供为xxx:setrange key 1 xxx
多功能操作
一次添加多条数据:mset k1 v1 k2 v2 k3 v3
一次获取多条数据:mget k1 k2
一次添加多条数据时,并判断:msetnx k3 v3 k4 v4
0:添加失败,已存在
1:添加成功
先get后set:getset key v1
List
add
从左往右(上-下):lpush list1 1 2 3 4
从右往左(下-上):rpush list1 1 2 3 4
插入元素(指定某个元素之前/之后)
插入某个元素之前
从左边进入,在list1中的2元素之前插入java:linsert list1 before 2 java
插入某元素之后
从左边进入,在list1中的2元素之后插入redis:linsert list1 after 2 redis
update
改变某个下标的值(将list1中下标为0的元素修改成x):lset list1 0 x
delete
移除左(上)边第一个元素:lpop list1
移除右(下)边第一个元素:rpop list 1
删除n个value(从list1中移除2个3):lrem list1 2 3
select
查询指定下标返回数据(0开始,-1结尾):lrange list1 0 -1
查询集合长度:llen list01
获取指定范围的值(获取list1中下标3~6的值):ltrim list1 3 6
多功能操作
从一个集合搞一个元素到另一个集合中(右出一个,左进一个):rpop lpush list01 list02
Set
add
添加元素(自动排除重复元素):sadd set1 1 2 2 3 5
返回插入元素数量
update
移动元素:将key1某个值赋值给key2:smove set1 set2 3
delete
删除集合中元素:srem key value
删除成功:1
随机移除:spop set1
select
查询集合:smembers set1
判断元素是否存在:sismember set1 2
存在:1
不存在:0
获取集合中元素的个数:scard set1
随机获取元素:srandmembers set1
随机获取n个元素:srandmember set1 3
数据集合类
交集:sinter
set1和set2共同存在的元素:sinter set1 set2
并集:sunion
将set1和set2中所有元素合并起来(去除重复的):sunion set1 set2
差集:sdiff
在set1中存在,在set2中不存在:sdiff set1 set2
在set2中存在,在set1中不存在:sdiff set2 set1
Hash
add
添加(添加user,值为id=1001):hset user id 1001
添加学生student,属性一堆:hmset student id 101 name tom age 22
添加的时候先判断是否存在:hset student age 18
添加失败:0 已存在
添加成功: 1 属性不存在可以添加
update
自增整数
自增整数2:hincrby student age 2
自增小数
自增小数5.5:hincrbyfloat user money 3.5
delete
删除属性(删除学生年龄属性):hdel student age
select
获取属性信息(获取学生姓名):hget student name
获取所有属性信息(获取学上全部信息)包括值:hgetall student
查询元素的属性个数(查询学生属性个数):hgetall student
判断元素是否存在某个属性(student是否存在name属性):hexists student name
获取属性的所有key不包括值(获取student所有的属性名):hkeys student
获取属性的所有值(获取student所有属性的值(内容)):hvals student
Zset
add
添加:zadd zset01 10 vip1 20 vip2 30 vip3 40 vip4 50 vip5
update
delete
删除(删除vip5):zrem zset01 vip5
select
查询数据:zrange zset01 0 -1
带着分数查询数据:zrange zset01 0 -1 withscores
模糊查询:zrangebyscore
20<=score<=40:zrangebyscore zset01 20 40
查询集合长度: zcard
范围内元素个数(分数在20~40之间,共有几个元素):zcount zset01 20 30
查询元素下标(vip3在集合中的下标(从上向下)):zrank zset01 vip3
通过值获得分数:zscore zset01 vip2
逆序找下标:zrevrank zset01 vip3
逆序查询:zrevrange
逆序范围查找:zrevrangebyscore
持久化
RDB
简介
1.在指定的时间间隔内,将内存中的数据集的快照写入磁盘
2.默认保存在/usr/local/bin中,文件名dump.rdb
2.默认保存在/usr/local/bin中,文件名dump.rdb
自动备份
每次关机时,redis会自动将数据备份到一个文件中:/usr/local/bin/dump.rdb
手动备份
每次操作完成,执行命令save就会立刻备份
RDB相关配置
stop-writes-on-bgsave-error:进水口和出水口,出水口发生故障与否
yes:当后台被封时候发生错误,前台停止写入
no:不管是否故障,继续写入
yes:当后台被封时候发生错误,前台停止写入
no:不管是否故障,继续写入
rdbcompression:对于存到到磁盘中的快照,是否启动LZF压缩算法。
一般都会启动。
yes:启动
no:不启动
一般都会启动。
yes:启动
no:不启动
rdbchecksum:在存储快照后,是否启动CRC64算法进行校验
注意:开启后,大约增加10%左右的CPU消耗
注意:开启后,大约增加10%左右的CPU消耗
dbfilename:快照备份文件名字
dir:快照备份问价能保存目录,默认为当前目录
优缺点
优点:适合大规模数据恢复,对数据完整性和一致性要求不高。
缺点:一定间隔备份一次,意外宕机,就是去最后一次快照的所有修改。
AOF
简介
以日志的形式记录每个写操作
将redis执行过的指令全部记录下来(读操作不记录)
只许追加文件,不可以改写文件
redis在启动指出会读取该文件从头到为执行一遍,这样来重构数据
开启AOF
修改redis.conf总配置文件:
appendonly yes
appendfilename appendonly.aof
appendonly yes
appendfilename appendonly.aof
注意点:1.文件中最后一句要删除,否则数据恢复不了
2.编辑这个文件,最后要:wq!强制执行
2.编辑这个文件,最后要:wq!强制执行
AOF相关配置
appendonly:开启aof模式
appendfilename:aof的文件名字,最好别改!
appendfsync:追写策略
always:每次数据变更,就会立刻记录到磁盘,性能较差,但数据完整性好
everysec:默认设置,异步操作,每秒记录,如果一秒当家,会有数据丢失
no:不追写
always:每次数据变更,就会立刻记录到磁盘,性能较差,但数据完整性好
everysec:默认设置,异步操作,每秒记录,如果一秒当家,会有数据丢失
no:不追写
no-appendfsync-on-rewrite:重写时是否运用Appendfsync追写策略;
用默认no即可,保证数据安全性。
用默认no即可,保证数据安全性。
AOF采用文件追加的方式,文件会越来越大,为了解决这个问题,增加了重写机制,redis会
自动记录上一次AOF文件的大小,当AOF文件大小达到预先设定的大小时,redis就会启动
AOF文件进行内容压缩,只保留可以恢复数据的最小指令集合
自动记录上一次AOF文件的大小,当AOF文件大小达到预先设定的大小时,redis就会启动
AOF文件进行内容压缩,只保留可以恢复数据的最小指令集合
auto-aof-rewrite-percentage:如果AOF文件大小已经超过原来的100%,也就是一倍,才重写压缩
auto-aof-rewrite-min-size:如果AOF文件已经超过64mb,才重写压缩
总结
RDB
只用作后备用途,建议15分钟备份一次就行
AOF
1.在最恶劣的情况下,也只丢失不超过2秒的数据,数据完整性比较高,但代价太大,会带来持续的IO
2.对硬盘的大小要求也高,默认64mb,太小了,企业级最少5G以上
复制原理
全量复制
Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份slave接收到数据
文件后,存盘,并加载到内存中;
文件后,存盘,并加载到内存中;
增量复制
Slave初始化后,开始正常工作时主服务器发生的写操作同步到从服务器的过程;
补充
Redis主从同步策略:主从刚刚连接的时候,进行全量同步;全同步结束后,进行增量同步。
当然,如果有需要,slave在任何时候都可以发起全量同步。
redis策略是,无论如何,首先会尝试进行增量同步,如不成功,要求从机进行全量同步。
事务
三特性
隔离性
所有命令都会按照顺序执行,事务在执行过程中,不会被其他客户端送来的命令打断。
没有隔离级别
队列中的命令没有提交之前都不会被实际执行,不存在"事务中查询要看到事务里的更新,事务外查询不能看"的问题
不保证原子性
如果一个命令失败,但是别的命令肯会执行成功,没有回滚
事务命令(三步走)
multi
可以理解成关系型事务中的begin
exec
可以理解成关系型事务中的commit
discard
可以理解成关系型事务中的rollback
watch
监控
unwatch
取消watch命令对所有key的操作
exec
一旦执行了exec命令,那么之前加的所有监控自动失效
发布订阅
publish(发布)
subscribe(订阅)
实现分布式锁
Redisson
Redisson就是用于在Java程序中操作Redis的库,它使得我们可以在程序中轻松地使用
Redis。
Redis。
Redisson在java.util中常用接口的基础上,为我们提供了一系列具有分布式特性的工具类。
分布式锁方案比较
zookeeper的特点就是高可靠性
redis特点就是高性能
redis集群的策略
主从复制(读写分离:主机写,从机读)
一主二仆
主机挂了,没有主机
血脉相传
继承关系,选出新主机
谋权篡位
手动选择主机
哨兵模式
简介
当主机宕机后,可以通过sentinel.conf配置文件,投票选出新主机
Sentinel是Redis的高可用性解决方案
由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及所有从服务
器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级
为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求
器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级
为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求
缺点
所有写的操作都是在master上完成的
然后再同步到slave上,所以两台机器之间会有延迟
当系统很繁忙的时候,延迟问题会加重
slave机器数量增加,问题也会加重
redis的连接池技术
JedisPool
相关问题
redis实际项目中如何使用缓存(使用场景)
查询(双层检测锁)
redisson秒杀
redis是单线程的吗 那他是怎么支持高并发请求的?
因为redis是单线程,所以命令也就具备原子性,使用setnx命令实现锁,
保存-v,如果k不存在,保存(当前线程加锁),执行完成后,删除k表示释放锁,
如果k已存在,阻塞线程执行,表示有锁。
保存-v,如果k不存在,保存(当前线程加锁),执行完成后,删除k表示释放锁,
如果k已存在,阻塞线程执行,表示有锁。
官方答案
因为redis是基于内存的操作,CPU不是Redis的评级,Redis的评级最有可能是机器内存的大小
或者带宽。既然单线程容易实现,而且CPU不会成为评级,那就顺理成章地采用单线程的方案了。
或者带宽。既然单线程容易实现,而且CPU不会成为评级,那就顺理成章地采用单线程的方案了。
Redis采用了单线程的模型,保证了每个操作的原子性,也避免了不必要的上下文切换和竞争条件
,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释
放锁操作,没有因为可能出现死锁而导致的性能消耗
,也不存在多进程或者多线程导致的切换而消耗CPU,不用去考虑各种锁的问题,不存在加锁释
放锁操作,没有因为可能出现死锁而导致的性能消耗
灵活多样的树结构。redis内部使用一个redisObjext对象来表示所有的key和value。
redisObject主要的信息包括数据类型、编码方式、虚拟内存等。它包含String,list,Set,Hash,ZSet五种数据类型。
针对不同的场景使用对应的数据类型,减少内存使用的同时,节省流量传输。
redisObject主要的信息包括数据类型、编码方式、虚拟内存等。它包含String,list,Set,Hash,ZSet五种数据类型。
针对不同的场景使用对应的数据类型,减少内存使用的同时,节省流量传输。
Redis是纯内存数据库,一般都是见到那的存取操作,线程占用的时间很多,时间的花费主要集中在IO上。
所以读取速度快。
所以读取速度快。
Redis使用的是非阻塞IO多路复用,使用了单线程轮询描述,将数据库的开、关、读、写都转换成了事件
,减少了线程切换时上下文的切换和竞争。
,减少了线程切换时上下文的切换和竞争。
redis缓存雪崩
概念
在高并发下,大量的缓存key在同一事件失效,导致大量的请求落到
数据库上。
例:活动系统里面同时进行着非常多的活动,但是在某个时间点,
所有的活动缓存全部过期。
数据库上。
例:活动系统里面同时进行着非常多的活动,但是在某个时间点,
所有的活动缓存全部过期。
解决方案
缓存数据的过期事件设置随机,防止同一事件大量数据过期现象发生。
如果缓存数据库是分布式部署,将热点数据君君分布在不同得缓存数据库中
设置热点数据永远不过期
redis缓存穿透
概念
是指查询一个一定不存在的数据,缓存没有命中,数据库也查询不到。这将导致每次查询都到数据库中查询,会增加数据库的压力。
解决方案
1. 在接口做校验
2. 存null值(缓存击穿加锁)
3. 布隆过滤器拦截: 将所有可能的查询key 先映射到布隆过滤器中,查询时先判断key是否存在布隆过滤器中,存在才继续向下执行,如果不存在,则直接返回。布隆过滤器将值进行多次哈希bit存储,布隆过滤器说某个元素在,可能会被误判。布隆过滤器说某个元素不在,那么一定不在。
2. 存null值(缓存击穿加锁)
3. 布隆过滤器拦截: 将所有可能的查询key 先映射到布隆过滤器中,查询时先判断key是否存在布隆过滤器中,存在才继续向下执行,如果不存在,则直接返回。布隆过滤器将值进行多次哈希bit存储,布隆过滤器说某个元素在,可能会被误判。布隆过滤器说某个元素不在,那么一定不在。
redis缓存击穿
概念
对一些设置了过期时间的key,如果这些key在某个时间点高并发的情况下,是一些非常热点的数据
如果这个key在大量请求时,刚好过期,那么查询的任务会落到数据库上,这称之为缓存击穿。
如果这个key在大量请求时,刚好过期,那么查询的任务会落到数据库上,这称之为缓存击穿。
解决方案
采用加锁的方法
0 条评论
下一页