Redis 学习
Redis
为什么要用Nosql
Memcached(缓存)+Mysql+垂直划分(读写分离)
网站80%的情况都是在读,每次都要去查询数据库的话就十分麻烦,所以说希望减轻数据的压力,我们可以使用缓存来保证效率
发展过程:优化数据结构和索引-->文件缓存(I/O)--》Memcached
分库分表+水平划分+Mysql集群
技术和业务在发展的同时,对人的要求也提高
最近的年代
技术爆炸
Mysql等关系型数据库就不够用了,数据量很多,变化很快
Mysql存储大的文件,博客,图片,数据库表很大,效率低
目前一个基本的互联网项目
为什么要用NoSql
用户的个人信息,社交网络,地理位置,用户自己产生的数据,用户日志等等爆发式增长
什么是Nosql
NoSql = Not Only SQL(不仅仅是SQL)
关系型数据库:
泛指非关系型数据库,随着web2.0互联网的诞生,传统的关系型数据库不满足超大规模,高并发的社区,暴露出来难以克服的问题,NoSql在当今大数据时代发展迅速,redis是发展最快的
很多的数据类型用户的个人信息,社交网络,地理位置,这些数据类型的存储不需要一个固定的格式,不需要多余的操作就可以横向发展的
Nosql特点
1.方便发展(数据之间没有关系,很好扩展)
2.大数据量高性能(redis一秒写8万次,读取11万,Nosql的缓存记录级,是一种细粒度的缓存,性能比较高)
3.数据类型是多样型的!(不需要事先设计数据库,随取随用,如果是数据量十分大的表,很多人就无法设计了)
4.传统RDMS和NoSQL
传统的RDMS
--结构化组织
--sql
--数据和关系都存在单独的表中
--操作,数据定义语言
--严格的一致性
--基础的事务
..
NoSql
--不仅仅是数据
--没有固定的查询语言
--键值对存储,列存储,文档存储,图形数据库(社交关系)
--最终一致性
--cap定理和base(异地多活)初级架构师
--高性能,高可用,高可扩
...
了解3v 3高
大数据时代的3V:主要是描述问题的
- 海量Volume
- 多样Variety
- 实时Velocity
互联网需求的3高:主要是对程序的基本要求
- 高并发
- 高可拓
- 高性能
真正在公司的实践:NoSQL+RDMS
技术没有高低之分,看你如何使用
Nosql的四大分类
KV键值对:
- 新浪:Redis
- 美团:Redis+Tair
- 阿里,百度:Redis+memecached
文档型数据库(bson格式和json一样):
-
MongoDB(一般需要掌握):
- MongoDB是一个基于分布式文件存储的数据库,C++编写,主要用来处理大量的文档
- MongoDB是一个介于关系型数据库和非关系型数据库中中间的产品,MongoDB是非关系型数据库中功能最丰富,最像关系型数据库的
-
ConthDB
列存储数据库
- HBase
- 分布式文件系统
图关系数据库
- 他不是存图形的,放的是关系,比如:朋友圈社交网络,广告推荐
- Neo4J
Redis入门
概述
Redis(Remote Dictionary Server) 远程字典服务
是一个开源的使用ANSIC语言编写,支持网络,可基于内存亦可持久化的日志型,key-value数据库,并提供多种语言的API
免费和开源,是当下最热门的Nosql技术之一,也被人们称之为结构化数据库
Redis能干吗
- 内存存储,持久化,内存中是断电即失,所以说持久化很重要(rdb)
- 效率高,可以用于高速缓存
- 发布订阅系统
- 地图信息分析
- 计时器,计数器(浏览量)
- ...
特性
- 多样的数据类型
- 持久化
- 集群
- 事务
- ...
Redis推荐都是在linux服务器上搭建的基于linux学习
基础知识
redis默认由16个数据库,默认的是第0个
可以使用select进行切换
selelct 3#切换数据库
dbsize #查看大小
set name ming #存储key-value
get name #get value
keys * #查看数据所有的key
flushdb #清空当前数据库
flushall #清空全部数据库
Redis默认端口号是6379
Redis是单线程的
明白 Redis是很快的,官方表示,Redis是基于内存操作,CPU不是Redis性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,既然可以使用单线程来实现,就使用单线程了
Redis 为什么单线程还这么块
Redis是C语言写的,官方提供的数据为100000+的QPS,这个不比Memcache差
- 误区,高性能的服务器一定是多线程的
- 误区,多线程(CPU上下文切换)一定比单线程效率高
CPU>内存>硬盘的速度要有所了解
核心:redis是将所有的而数据全部放在内存中的,所以说使用单线程去操作效率就是最高的,多线程(上下文切换:耗时的操作),对于内存系统来说,如果没有上下文切换效率就是最高的,多次读写都是在一个CPU上的,在内存情况下,这个就是最佳的方案
五大数据类型
Redis是一个开源(BSD许可),内存中的数据结构存储,用作数据库、缓存和消息代理。它支持数据结构,如字符串String、哈希Hash、列表List、集合Set、有序集合Zset、带范围查询的排序集、位图bimaps、超日志Hyperloglog、带有radius查询和流的地理空间索引Geospatial。Redis内置了复制、Lua脚本、LRU逐出、事务和不同级别的磁盘持久性,并通过Redis Sentinel和Redis Cluster自动分区来提供高可用性。
Redis-key
exists name #判断key是否存在 ,存在返回1 否则返回0
move name 1 #1表示当前数据库 移除key 成功返回1,否则返回0
expire name 10 #设置过期时间 10s
ttl name #查看剩余过期时间
type name #查看key类型
String (字符串)
append name "fhdf" #往key追加字符串
strlen name #获取key字符串长度
incr views #加一操作
decr views #减一操作
#步长 设置
incrby views 10 #加10
decrby views 10#减10
#字符串范围
getrange name 0 3 #截取 [0,3]闭区间 0 1 2 3 共四个字符
getrange name 0 -1 #全部
#替换
setrange key1 1 xx #从1开始替换 替换2个,其它不变 abcdf 替换成 axxdf
# setex(set with expire) 设置过期时间
# setnx(set if not exist) 不存在再设置,存在创建失败 在分布式锁中常常使用
setex key3 30 "hello" #设置key并设置过期时间
mset k1 v1 k2 v2 k3 v3 #批量设置
mget k1 k2 k3 #批量获取
msetnx k1 v1 k5 v5 #原子性操作,一个存在,则都不创建成功
#巧妙的设计
set user:id1 {name:zhang,age:3} #设置一个user:1对象值为json字符来保存一个对象
List
基本数据类型:列表,命令都是l开头
lpush list one #将一个或多个值插入列表头部
rpush list two #插入列表尾部
lrange list 0 -1
lrange 0 1
lpop list
rpop list
lren list
lindex list 1 #通过下标获取值
lrem list 1 one #移除指定值
ltrim list 1 2 #截取 [1,2] 截取指定长度 list改变了
rpoplpush list list1 #移除元素加入另一item个列表
lset list 0 item#对 0是下标 list 的值进行更新
linsert list before "wold" "vv" #插入值
linsert list after "wold" "new" #插入值
#用作消息队列 lpush lpop rpush rpop
set集合
命令都是s开头,值是不重复的无序的
sadd mylist "hello"
smembers myset #列举出所有值
sismember myset hello #查看是否包含元素
scard myset #获取set集合中的内容元素个数
srem myset hello #移除集合中指定元素
srandmember myset 2 #随机抽选出指定个数的元素
spop myset #随机删除key
smove myset myset2 “hello” #移除指定元素到另一个集合
sdiff set1 set2 #集合set1-set2
sinter set1 set2 #集合的交集
sunion set1 set2 #集合的并集
#应用 微博,所有关注的人放在同一集合,粉丝也放在同一集合,
# 共同关注 共同好友,二度好友
Hash(哈希)
Map集合,key-map~~时候这个值是一个map集合 命令h开头
本质和String类型没有太大区别,还是一个简单的key-value
hset myhash field1 kuangshen #插入某一个值 哈希表
hget myhash field1 #获取某一个值
hmset myhash field2 v field3 gg # 批量插入
hmget myhash #获取多个字段值
hdel myhash field1 #删除hash指定key字段对应的value值
hgetall myhash #获取所有key-value值
hlen myhash #获取hash 表的字段数量
hexists myhash field1 #判断hash指定字段是否存在
hkeys myhash #获取所有key值
hvals myhash #获取所有value值
hincrby myhash field1 1 #加一
hsetnx myhash field4 5 #不存在即创建
#hash变更的数据 user name age ,尤其是用户信息之类的,经常变动的信息
# hash适合对象 string适合字符串
Zset(有序集合)
在set的基础上,增加了一个值。命令行z开头
set k1 v1
zset k1 score v1
zadd myset 1 one
zadd myset 2 two 3 three
zrange myset 0 -1
#排序如何实现
zadd salary 500 zhang
zadd salary 600 he
zrangebyscore salary -inf +inf #显示范围内的value
zrangebyscore salary -inf +inf withscores #显示key与score
zrange salary 0 -1 #显示所有的元素
zrem salary he #移除元素
zcard salary #获取有序集合个数
zrevrange salary #从大到小进行排序
zcount myset 1 3 #指定区间的数量
案例:set排序,存储班级成绩,工资表
权重 普通消息,重要消息
排行榜,topk
Geospatial(地理位置)
朋友定位,附近的人,打车距离计算
可以查询一些测试数据
只有六个命令
#geoadd 添加地理位置
geoadd china:city 116.40 39.90 beijing #添加 经度 纬度 名称 可以添加多个
#规则:两极无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入
geopos china:city beijing #获取指定地区的经纬度
#两人之间的定位
geodist 命令-返回两个给定位置之间的距离
m ,km,mi英里,ft英尺
geodist china:city beijing chongqing km
georadius #以给定的经纬度为中心,找出结合某一半径内的元素
#案例 附近的人
georadius china:city 110 30 1000 km
georadius china:city 110 30 1000 km withdist #返回距离
georadius china:city 110 30 1000 km withcoord #返回经纬度
georadius china:city 110 30 1000 km withdist count 2#限制数量
georediusbymember china:city beijing 100 km #以元素为中心寻找
geohash #返回一个或多个元素的geohash表示 11个字符的字符串
GEO 底层的实现原理就是zset,可以使用zset命令来操作geo
zrange china:city 0 -1 #查看全部元素
zrem china:city beijing #删除
Hyperloglog
什么是基数
一组数中不重复的元素
Redis Hyperloglog 基数统计的算法
优点:占用的内存是固定的,2^64不同的元素的技术,只需要12KB内存,如果要从内存角度的话Hyperloglog首选
允许出错
pfadd mykey a b c d f #添加
pfcount mykey #统计
pfmerge mykey3 mykey mykey2 #合并mykey,mykey2 到mykey3
pfcount #统计基数的数目
Bitmap
位存储
统计用户信息
活跃,不活跃,登陆,不登录,打卡,365打卡
bitmap 位图,数据结构,都是操作二进制位来记录,就只有0和1两个状态
setbit setbit sign 0 1
setbit setbit sign 3 1
getbit sign 3
#统计操作
bitcount sign #统计1的数
基本事务操作
Redis事务本质:一组命令的集合!一个事务的所有命令都会被序列化,在事务执行过程中,会按照顺序执行
一次性/顺序性/排他性:执行一系列命令-------队列--set --set --set ----执行
Redis事务没有隔离级别的概念
所有的命令在事务中,并没有直接被执行,只有发起命令的时候才会执行
Redis单条命令是保证原子性的,但是事务不保证原子性
redis的事务:
- 开启事务(multi)
- 命令入队()
- 执行事务(exec)
正常执行事务
multi #开启事务
set k1 v1 #命令入队
set k2 v2 #命令入队
exec #执行事务
#需要重新开启事务
multi
set k1 v3
set k2 v3
discard #取消事务 命令都不执行
编译型异常(代码有问题,命令有错),都不执行
运行时异常(1/0) 如果事务队列中存在语法性,那么执行命令的时候,其他命令可以正常执行,错误命令抛出异常
乐观锁
悲观锁:无论做什么都加锁
乐观锁:认为不会上锁,更新数据判断是否有人修改
- 获取version
- 更新的时候比较version
监控:watch
set money 100
set out 0
watch money #监视money对象
multi #事物正常结束,数据期间没有发生变动,这个时候就正常执行成功
decrby money 20
incrby out 20
exec #执行之前,另外一个线程,修改了我们的值,这个时候就会导致事务执行失败
unwatch #修改失败,获取最新值就好
watch money
#再次开启事务操作
测试多线程修改值,监视失败,使用watch可以当作redis的乐观锁操作
Redis.conf详解
Redis持久化
Redis是内存数据库,如果不讲内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,服务器中的数据库状态也会消失,所以Redis提供了持久化的功能
RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘,Snapshot快照,他恢复时是将快照直接读到内存
Redis会单独创建fork一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件,整个过程中,主进程是不进行IO操作的,这就确保了极高的性能,
RDB方式比AOF方式更加高效,缺点是最后一次持久化后的数据可能丢失
默认的就是RDB,一般情况下不需要修改这个配置
rdb保存的文件是dump.rdb
#redis.conf
save 60 5 #60秒内修改5次触发
dbfilename dump.rdb
触发机制
-
save的规则满足的情况下,自动触发rdb规则
-
执行flushall命令,也会触发rbd规则
-
退出redis,也会产生rdb文件
备份就自动生成一个rdb文件
只需要将rdb文件放在redis启动目录就可以,redis启动的时候会自动检查dump.rdb,恢复其中的数据
查看需要存在的位置
config get dir #默认的配置够用
优点:
适合大规模的数据恢复
如果对数据的完整性要求不高
缺点
需要一定时间间隔来操作,如果redis意外宕机了,这个最后一次修改的数据就没有了
fork进程的时候,会占用一定的内容空间
AOF(append only file)
将我们的所有命令都记录下来,恢复的时候把这个文件全部执行一遍
以日志的形式来记录每个写操作,将redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,redis重启的时候根据日志的内容将写指令从前到后执行一次以完成数据的恢复工作
AOF保存的是appendonly.aof文件
append #配置
appendonly no #默认不开启,需要手动进行配置,改为yes
appendfilename appendonly.aof
appendfsync everysec
appendfsync always
appendfsync no
##rewrite
重写规则,默认大于64Mb
fork一个新的进程将文件重写
如果这个aof文件有错误,redis启动不了,需要修复aof文件
redis给我们提供了一个工具,redis-check-aof --fix appendonly.aof
优点:
- 每一次修改都同步,文件的完整性会更好
- 每秒同步一次,可能会丢失一秒的数据
- 从不同步,效率最高的
缺点:
- 相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
- aof运行效率也比rdb慢,所以我们redis默认的配置就是rdb持久化
扩展:
- 只做缓存,不需要持久化
- 同时开启两种,默认先载入aof文件恢复原始数据
- 性能:rdb文件用作后备用途,建议只在slave上持久化文件,15分钟一次
Redis发布订阅
发布订阅(pub/sub)是一种消息通信模式:发送者发送消息,订阅者接收消息
- 消息发布者
- 频道
- 消息订阅者
#订阅端
psubscribe #接收多个
subscribe channel #订阅频道 等待读取信息
1)message
2)channel
3)"dddf"
#发送端
publish channel "dddf"
使用场景:
- 实时消息系统
- 实时聊天
- 订阅关注系统
Redis主从复制
主从复制:是将一台redis服务器的数据复制到其他的redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower)
数据的复制是单向的,只能由主节点到从节点,Master以写为主,Slave以读为主
默认情况下,每台服务器都是主节点,且一个主节点可以有多个从节点,但一个从节点只能有一个主节点
主从复制的作用:
- 数据冗余:主从复制实现了数据的热备份,是持久化的一种数据冗余方式
- 故障恢复:当主节点出现问题,可以由从节点提供服务,实现快速的故障恢复,实际上是一种服务的冗余
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,从节点提供读服务(即写redis数据时应用连接主节点,读redis数据时应用连接从节点),分担服务器负载,尤其是在写少读多的场景下,通过多个从节点分担负载,可以大大提高redis服务器的并发量
- 高可用(集群)基石:主从复制还是哨兵和集群能够实施的基础,因此说主从复制是redis高可用的基础
一般配置一主二从
单机会宕机
原因:
- 结构上,单个redis服务器会发生单点故障,一台服务器需要处理所有的请求负载,压力较大
- 容量上,单个redis服务器内存容量有限
环境配置
只配置从库不用配置主库
info replication #查看当前库的信息
redis-server kconfig/redis80.conf #开启服务
复制三个配置文件,然后修改对应的信息
- 端口
- pid名字
- log文件名字
- dump.rdb名字
修改完毕之后,启动服务
一主二从
默认每台服务器都是主节点:一般只用配置从机
slaveof 127.0.0.1 6379
info replication
命令是暂时的,进配置文件修改时永久的
主机可以写,从机不能写,只能读
主机断开连接,从机依旧连接到主机,但是没有写操作,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息
如果是使用命令行来配置的主从,这个时候如果重启了,就会变回主机,只要变为从机,立马就会从主机中获取值
slave启动成功连接到master后会发送一个sync同步命令
Master接到命令后,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步
全量复制:slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接,一次完全同步(全量复制)将被自动执行,数据一定可以在从机中看到
层层连接
手动配置
命令方式
slaveof no one #主动把自己选为主节点,老大修复了,重新连接
哨兵模式(自动选举)
哨兵模式是一种特殊的模式,哨兵是一个独立的进车改,作为进程,他会独立运行,其原理是哨兵通过发送命令,等待redis服务器响应,从而监控运行的多个redis实例
防止一个哨兵出现问题,配置多个哨兵,多个哨兵之间还会进行监控
哨兵作用:
- 通过发送命令,让redis服务器返回监控其运行状态,包括主服务器和从服务器
- 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让他们切换主机
主观下线
投票
客观下线
如果主机回来了,只能归并到新的主机下,当作从机,这就是哨兵模式的规则
优点:
- 哨兵集群,基于主从复制,所有主从配置优点都有
- 主从可以切换,故障可以转移,系统的可用性更好
- 哨兵模式就是主从模式的升级,手动到自动
缺点:
- redis不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦
- 实现哨兵模式的配置其实是很麻烦,里面有很多选择
缓存穿透和雪崩
服务的高可用问题
缓存穿透(查不到)
查询数据,缓存没有命中,都去请求数据库,给数据库很大压力,出现缓存穿透
解决方案:
- 布隆过滤器
-
- 一种数据结构,对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层系统的欻性能压力
缓存空对象
出现的问题
- 空值缓存起来,需要更多空间
- 即使对空值设置了过期时间,还是会在缓存层和存储层的数据会有一段时间窗口的不一致
缓存击穿
微博服务器宕机()
指一个key非常热点,在不停的访问,当key失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,数据库压力瞬间过大
解决方案:
-
设置热点数据永不过期
-
加互斥锁
-
- 分布式锁,使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
缓存雪崩
指在某一个时间段,缓存集中过期失效
比如抢购的时候
停掉一些服务,保证主要的服务可用
解决方案:
- redis高可用(异地多活),增加redis服务器,搭建集群
- 限流降级,通过加锁或队列来控制数据库写缓存的线程数量
- 数据预热,在正式部署之前,把可能数据先预先访问一遍,加载到缓存,设置不同的过期时间