参考b站狂神说的视频
https://www.bilibili.com/video/BV1S54y1R7SB?p=36&share_source=copy_web
非关系型数据库:nosql数据库
非关系型数据库的优点
1。方便扩展(数据之间没有关系,很好扩展)
2。大数据高性能(nosql的缓存记录数)
3。数据类型是多样型的 (不需要事先设计数据库 随去随用)
高性能 高可用 高可扩
NOSQL的四大分类
K V键值对. ————->缓存 日志
美团:redis tait
阿里 百度:redis memcache
文档型数据库 —————>分布式文件系统
MongoDB(必须掌握)
基于分布式文件存储的数据库 C++编写,主要用来处理大量的文档
mongoDB是一个介于关系型数据库和非关系型数据库的中间的产品。MongoDB是非关系型数据库中功能最丰富 最像非关系型数据库的
ConthDB
列存储数据库—-》web应用 HBase
图关系数据库——-〉社交网络 Neo4j
2.Redis是什么
1.redis远程字典服务 由c语言编写,支持网络的,基于内存的可持久化 日志型,key value 数据库。支持多种语言的API
免费和开源。 结构化数据库。
作用:
内存存储 持久化 (内存中是断电即失)(持久化 rdb aof)
效率高 可以用于高速缓存
发布订阅系统(简单消息队列)
地图信息分析
计时器 计数器
特性
1.多样的数据类型
2.持久化
3.集群
4,事务
测试性能
Redis-benchmark 压力测试工具
测试 100个并发 100000请求
Refis-benchmark -h localhost -p 6379 -c 100 -n 100000
Redis 推荐在linux上搭建
3.Redis基础知识
Redis默认有16个数据库 默认使用的第0个
可以使用select进行切换数据库
eg:select 3 keys * 查看所有的key flushall 清空所有的数据库 flushdb 清空当前数据库
Redis 是单线程的
Redis是基于内存操作,CPU并不是redis的瓶颈,根据机器的内存和网络带宽
为什么单线程还这么快?
CPU>内存>硬盘
Redis是将所有的数据都存在内存中,对于内存系统来说,没有上下文切换效率就是最高的。
它可以用作数据库 缓存 消息中间件。
支持多种类型的数据结构 字符串 散列 列表 集合 有序集合与范围查询
redis的五大数据类型
Redis-Key
判断是否存在 exists key值 移除指定的key值 Move key值 数据库 设置过期时间,单位是秒 expire name 10 查看key的剩余时间 ttl key值 查看key的类型 type key值
string
set key值 value 赋值 get key值 获取值 APPEND key值 “hello” 拼接字符串 如果key不存在就新建 STRLEN key值 字符串长度 incr key值 对应的值加1 decr key值 对应的值减1 INCRBY key值 步长 可以设置指定增量 eg:INCRBY views 10 对应的值加10 获取范围 eg:set key1 “hello,zhangsan” GETRANGE key1 0 3 “hell” GETRANGE key1 0 -1 //查看全部字符串 “hello,zhangsan”
替换
set key2 abcdefg SETRANGE key2 1 xx //替换指定位置开始的字符串 get key2 ->axxdefg setex(set with expire) 设置过期时间 eg:setex key3 30 “hello” setnx(set if not exists) 不存在设置 eg:setnx mikes “redis”
批量获取和批量设置
批量设置 mset eg: mset key1 value1 key2 value2 key3 value3 批量获取 mget eg: mget key1 key2 key3 msetnx key1 value1 key4 value4 //msetnx是个原子性操作,一起成功 一起失败 对象 设置一个user对象 user:{id}:{filed} mset user:1:name zhangsan user:1:age 18 mget user:1:name user:1:age 1)”zhangsan” 2)”18”
先get后set
getset 先get后set getset db redis ->(nil) get db —>redis Getset db mongdb —->redis get db—>mongdb
string类型的应用:
计数器
统计数量
统计多单位的数量
对象缓存
List
列表 堆 栈
所有的list命令都是l开头的
添加 LPUSH list one //每次将数据插入头部 LPUSH list two 获取 LRANGE list 0 -1 -》“two” -》“one” Rpush list right //每次将数据放到尾部 LRANGE list 0 -1 -》“two” -》“one” -》“Right
LPOP 移除尾部的值
RPOP 移除头部的值
eg: Lpop list 通过下标获取值 LRAANGE list 0 -1 -》two -》one Lindex list 0 —》two 返回列表的长度 Llen list —>2 移除指定值。list可以重复 Lpush list three Lpush list three Lrem list 1 three //从list中移除一个three List 截断 Rpush mylist “hello” Rpush mylist “hello1” Rpush mylist “hello2” Rpush mylist “hello3” Ltrim mylist 1 2 //通过下标截取指定的长度 Lrange mylist 0 -1 ->“hello1” ->“Hello2” rpoplpush 移动list中最后一个值 并将其移动到新的列表中 rRush mylist “hello” rRush mylist “hello1” rRush mylist “hello2” rpoplpush mylist myotherlist Lrange mylist 0 -1 ->“hello” ->“hello1” Lrange myotherlist 0 -1 ->“Hello2” lset 将列表中指定下标的值替换为另外一个值 更新操作 如果列表不存在 更新会报错 如果下标的值存在更新,不存在 报错 Linset 将某个具体的value 插入到list中某个元素的前面和后面 Rpush mylist “hello” Rpush mylist “world” Insert mylist before world other ->“hello” ->“other” ->“World
小结
实际上是一个链表。
如果key 不存在 创建新的链表
在两边插入或者改动值 效率最高,中间元素,相对来说效率低一些
应用:消息队列
Set (集合)
不能重复
所有的set命令都是s开头的
添加
sadd myset “hello” sadd myset “kuangshen” 获取所有元素 SMembers SMembers myset ->“hello” ->“kuangshen
判断是否包含某个元素 Sismeber myset hello 获取set集合的元素个数 scard myset 移除某一个元素 Srem myset hello
随机抽出一个元素 Srandmember myset Srandmember myset 2 随机抽取指定个数的元素
随机移除set集合中的元素
eg:SMembers myset ->Kuangshen2 ->Kuangshen ->Hello Spop myset ->kuanghen2 Spop myset —>kuangshen
Smove 将一个指定的key 移动到另一个set中
Sadd myset “hello” Sadd myset “world” Sadd myset2 “set2” Smove myset myset2 “hello
Sadd key1 a Sadd key1 b Sadd key1 c Sadd key2 c Sadd key2 d Sadd key2 e Sdiff key1 key2 //两个set集合的差集 -> “b” -> “a” key1中key2没有的 Sinter key1 key2//两个set集合的交集 ->“c” SUnion key1 key2//并集
Hash(哈希)
Map 集合 key -map
本质和string 没有什么太大区别
所有的hash命令都是h开头的
set myhash field1 kuangshen Hget myhash field1 ->“Kuangshen” //值会被覆盖 Hmset myhash field1 hello field2 world Hmget myhash field1 field2 ->“hello” ->”world” Hgetall myhash ->“hello” ->”world” hdel myhash field1 //删除hash指定key值 Hlen myhash //获取hash的长度 Exists myhash field1//判定hash中指定的key是否存在 Hkeys myhash//获取所有的key值 Hvals myhash//获取所有的value值 Hset myhash field3 5 Hincrby myhash field3 1//给指定key值增加指定步长 Hsetnx //如果存在不能设置 不存在就能设置 Hash 变更的数据 user name age 更适合对象的存储 Hset user:1 name “zhangsan” Hset user:1 age “18”
应用:集合之间的操作。交集。并集 差集
Zset 有序集合
在set的基础上,增加了一个值 ,
set k1 v1
Set k1 score1 v1
eg:Zadd myset 1 one Zadd myset 2 two 3 three Zrange myset 0 -1 ->”one” “two” “three"
排序 Zadd salary 2500 xiaohong Zadd salary 5000 zhangsan Zadd salary 500 kuangshen ZRANGEBYSCORE salary -inf +inf ->“kuangshen” -> “xiaohong” -> “zhangsan"
ZRANGEBYSCORE salary -inf +inf with scores ->”kuangshen” ->“500” ->“xiaohong” ->“2500” ->“zhangsan” ->“5000
排序(上限)
ZRANGEBYSCORE salary -inf 2500 ->“kuangshen” ->“xiaohong
Zrevrange salary 0 -1 (从大到小排序) -》“zhangsan” -》“Kuangshen” 移除元素 zrem Zrem salary xiaohong 查看元素个数 Zcard salary Zconut 获取指定区间的成员数量 Zadd myset 1 hello 2 world 3 kuangshen Zconut myset 1 3 -> 3 Zcont myset 1 2 获取指定区间的成员数量 -〉2
小结:set 排序 存储班级成绩表 工资表排序
普通消息 1,重要消息 2.带权重进行判断
应用:排行榜应用实现
三种特殊数据类型
1.Geospatial 地理位置
Redis 的 geo 可以推算地理位置的信息 两地之间的距离 方圆几里的人
Geoadd key 值(纬度 经度 名称)
规则:两极无法直接添加 一般会下载城市数据 直接java程序导入
Geoadd china:city 116.40 39.90 beijing Geoadd china:city 121.47 31.23 Shanghai Geoadd china:city 106.50 29.53 Chongqing 114.05 22.52 Shenzhen
有效的经度 从-180 到180
有效的纬度 从-85 到85
获取指定城市的经度和纬度
Geopos china:city beijing ->”116.3999..” ->“39.90000
Geodist 获取指定两个位置的距离(直线距离)
Geodist china:city beijing Shanghai km
我附近的人?(获得所有附近的人的地址 定位)
Georadius 以给定的经纬度为中心 找出某一半径内的元素
Georadius China:city 110 30 500 km ->”chongqin”
Georadius China:city 110 30 500 km withdist 直线距离
Georadius China:city 110 30 500 km withcoord 经纬度
获得指定数量的人
Georadius China:city 110 30 500 km count 1
北京周围的城市
Georadiusbymember china:city beijing 100 km
Geohash 返回一个或者多个位置元素的geohash表示
将返回11个字符串的geohash字符串(将二维的经纬度转换为一维的字符串)
Geo 底层的实现原理就是zset ,可以使用zset 命令来操作
2.Hyperloglog PF开头的命令
什么是基数?不重复的元素
A(1,3,5,7,8,7)
B(1,3,5,7,8)
基数(不重复的元素) 网页的浏览量(一个人访问一个网站多次,但是还是算作一个人)
占用的内存是固定的,2^64 不同的元素,只需要占用128kB内存
0.81%错误率
eg:PFADD mykey a b c d e f g h I j PFCOUNT mykey ->10 PFADD mykey2 i j z x c v b n m PFCOUNT Mykey2 ->9 PFMEGRE mykey3 mykey mykey2 PFCOUNT mykey3 ->15
如果允许有一定错误率,就可以用hyperloglog
3.Bitmaps
位存储 操作二进制来记录
统计用户信息 两个状态的 都可以使用Bitmaps
周一到周六的打卡
Setbit sign 0 1 Setbit sign 1 0 Setbit sign 2 0 Setbit sign 3 1 Setbit sign 4 1 Setbit sign 5 0 Setbit sign 6 0 Getbit sign 6 ->0 Getbit sign 3 ->1 bitcount sign //统计打卡的天数 ->3
Redis基本的事务操作
事务
一组命令的集合 一个事务中的所有命令都会被序列化 会按照顺序执行,
一次性 顺序性 排他性 执行一些列的命令
Redis事务没有隔离级别的概念 单条命令出错不会影响前面的任务 也不会影响后面的命令
Redis 单条命令
开启事务(multi)
命令入队(…)
执行事务(exec)
Multi //开启事务
Set k1 v1
Set k2 v2
Get k2
Set k3 v3
exec //执行事务
->ok
->ok
->”v2”
->ok
每个事务执行完就没有了
放弃事务 discard 在事务执行之前可以放弃事务
编译型异常(代码有问题,命令有错)事务中所有的命令都不会被执行
运行型异常(如果事务队列中存在语法性错误,)执行事务的时候,其他命令会正常执行,错误命令抛出异常。
乐观锁
悲观锁:无论做什么都会加锁
乐观锁:更新数据的时候去判断一下,在此期间,是否有人修改过这个数据,
Mysql 获取version,更新的时候比较version
Redis监视测试
Set money 100
Set out 0
Watch money 监视money对象
Muiti
Decrby money 20
Incrby out 20
Exec 执行命令
->80
->20
测试多线程修改值,使用watch可以当作redis的乐观锁操作
Watch money Multi Decrby money 10 Incrby out 10 Exec 执行之前,另外一个线程,修改了我们的值,这个时候,就会导致我们事务执行失败 ->(nil)
如何解决?释放锁,重新写事务
Unwatch
Watch money
Jedis
使用java操作Redis
Redis官方推荐的java连接开发工具,使用java操作redis中间件。
导入包
2.编码测试:
连接数据库
操作命令
断开连接
Jedis jedis = new Jedis("127.0.0.1",6379);
System.out.println(jedis.ping());
方法和之前的api完全相同
Jedis.close();//关闭连接
事务: public static void main(String[] args) { // Jedis jedis = new Jedis("127.0.0.1",6379); System.out.println(jedis.ping()); jedis.flushDB(); JSONObject jsonObject = new JSONObject(); jsonObject.put("hello","world"); jsonObject.put("name","zhangsan"); Transaction multi = jedis.multi();//开启事务 String result = jsonObject.toJSONString(); try { multi.set("user1",result); multi.set("user2",result); multi.exec();//执行事务 }catch (Exception e){ multi.discard();//销毁事务 e.printStackTrace(); }finally { System.out.println(jedis.get("user1")); System.out.println(jedis.get("user2")); jedis.close(); } }
springboot整合
Springboot2.x之后。原来使用的jedis 被替换成了letture
Jedis:采用的直连。 多线程操作的话,不安全,避免不安全就使用jedis pool连接池
Lettuce:采用netty,异步,实例可以在多个线程中进行共享,不存在线程不安全的情况,可以减少线程数量。
导入包
配置连接
测试
传输对象需要序列化
自定义的redisTemplate
Redis.conf详解
1.大小写不敏感
2.可以包含其他配置文件
Network
Bind 127.0.0.1 绑定ip
通用 general
Demonize yes #后台运行 默认是no
Loglevel notice #生产环境
Logfile 日志文件名
Database 16 #数据库的数量
Always-show-logo yes #是否显示logo
快照
持久化,在规定的时间内,执行了多少次操作,则会持久化到文件.rdb .aof
如果900秒至少有一个key 进行了修改,我们
Save 900 1
Save 300 10
Save 60 10000
Stop-writes-on-bgsave-error yes 持久化如果出错,是否继续工作
Rdbcompression yes 是否压缩rdb文件,会消耗一些cpu资源
Rdbchecksum yes 保存rdb文件的时候,进行错误的检查校验
Dir ./ rdb文件保存的目录
Requirepass 123456 #设置密码
Config set require pass 123456
Config get require pass
->noauth authentication required
Auth 123456
->ok
Append only 模式
Appendonly no 默认是不开启,默认是使用rdb方式持久化,大部分所有的情况下,rdb完全够用
Appendfsync everysec 每秒执行一次
Redis持久化
Redis是内存数据库,如果不将内存中的数据库保存到磁盘中,一旦服务器进程退出,数据库状态也会消失,所以redis提供了持久化功能
默认是rdb 缺点就是最后一次持久化的数据可能会丢失
保存的文件是dump.rdb
触发规则
1.save的规则满足的情况下,会自动触发rdb规则
2.Flushall命令,也会触发我们的rdb规则
3.退出redis,也会产生rdb文件
备份就会自动生成一个dump.rdb
如果恢复rdb文件的数据
只需要将rdb文件
放到我们的redis启动目录下,redis启动的时候会自动检查dump.rdb 恢复其中的数据
查看需要的位置
Config get dir
->”dir”
->”usr/local/bin” 如果在该目录下存在dump.rdb文件,启动就会自动回复其中的数据
优点:
适合大规模的数据恢复
如果你对数据的完整性要求不高
缺点:
需要一定时间间隔进程操作,redis意外宕机了,最后一次修改数据就没了
Fork进程的时候,会占用一定的内存空间
AOF
以日志的形式来记录每个写操作,将redis执行过的所有指令记录下来(读操作不记录),只许追加文件不许修改文件,redis启动之初就会读取该文件重新构建,
AOF 保存的是appendonly,aof
默认不开启
重启redis就可以生效了
如果aof文件有错误,redis无法启动
redis修复aof工具 redis-check-aof
Redis-check-aof —-fix appendonly.aof
文件正常 重启就可以恢复
优点:
1.每次修改都同步,文件的完整性会更好
2。每秒同步一次,可能会丢失一秒的数据
3.从不同步,效率最高的
缺点:
1.相对于数据文件来说,aof远远大于rdb,修复的速度也比rdb慢
2.aof运行效率也比rdb慢,所以redis默认配置是rdb
Rewite 重写
发布订阅
消息通信模式 发送者发送消息 订阅者接收消息
消息发送者
频道
消息订阅者
Subscribe 频道名 //订阅频道
Publish 频道名 消息//向某频道发布消息
Subscribe 频道名 …//订阅多个频道
Eg:
订阅端:subscribe bilibili
->”subscribe”
->”kuangshenshuo”
->”message”
->”bilibili”
->”hello,bilibili”
发送端:publish bilibili “hello,bilibili”
Subscribe就是把订阅的人加入到该频道的发布链表中
使用场景:
实时消息系统
实时聊天(聊天室)
订阅 关注系统都是可以的
主从复制 读写分离
Master -slave1
-slave2
-slave3
主机进行读,从机进行写。减缓服务器的压力。
数据的复制是单向的,只能由主节点到从节点。
主从复制的作用:
1。数据冗余
2。故障恢复
3。负载均衡 通过多个从节点分担负载
4,高可用
单台redis的最大使用内存不应该超过20G
默认情况下,每台redis都是主节点
info replication 查看当前库的信息
->role:master 角色
connected_slaves:0 没有从机
复制3个配置文件,修改对应的信息
1。端口
2。pid名字
3。log文件名字
4。dump.rdb名字
启动服务 redis-server kconfig/redis79.conf
连接 redis-cli -p 6379
一主二从
主从复制之复制原理
默认情况下,每台redis都是主节点
info replication //查看当前库的信息
->role:master //角色
在从机中配置
slaveof 127.0.0.1 6379//绑定对应的主机
配置了之后,从机的角色变了 主机中可以看到从机的配置信息
真实的主从配置 修改配置文件
replicaof 主机IP 端口号
主机可以写,从机不能写只能读
主机中的所有信息和数据,都会自动被从机保存
测试:主机断开连接 从机依旧连接到主机的,这个时候,主机如果回来了,从机依旧可以直接获取到主机写的信息
如果使用命令行 配置的主从,重启之后,又会变成主机,只要变成从机,立马就会从主机中获取值
从机启动成功连接到主机后,会发送一条sync同步命令
master接到命令后,启动后台的存盘进同时收集所有接收到的用于修改数据集命令,
在后台命令执行完毕之后,master将传送整个数据文件到slave.完成一次完全同步
全量复制 slave服务在接收到数据库文件数据后,将其存盘加载到内存中
增量复制 master继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要重新连接master ,一次全量复制将会被自动执行。
层层链路
Master -slave1 master -slave2
79 80 81
80为从节点,不能写 ,只能读
slaveof no one
如果主机断开了连接 使用slaveof no one 让自己成为主机
哨兵模式(自动选举老大的模式)sentinel
通过后台监控主机是否故障,如果故障了根据投票数自动将从库转为主库
原理:哨兵通过发送命令 等待redis服务器响应 从而监控运行的多个redis实例
多哨兵模式
主服务器不能用 并且达到一定数量,让各个哨兵爸自己监控的从服务器实现切换主机,为客观下线
配置哨兵配置文件 sentinel.conf
Sentinel montior 被监控的名称 host port 1
Sentinel montior myredis 127.0.0.1 6379 1
后面的数字 代表主机挂了 slave投票看让谁接替成为主机 投票最多的,就会成为主机
启动哨兵
Reidis-sentinel kconfig/sentinel.conf
如果主机节点断开了,就会从从机中随机选择一个服务器
哨兵日志
Failover 发现故障
Sdown 主机转移
如果主机此时回来。只能归并到新的主机下,当作从机
优点
1.哨兵集群,基于主从复值模式,所有的主从配置优点,它都有
2.主从可以切换,故障可以转移,系统的可用性好
3.哨兵模式就是主从模式的升级,手动到自动,更加健壮
缺点
Redis不好在线扩容,集群容量一旦到达上限,在线扩容十分麻烦
实现哨兵模式的配置其实是很麻烦的,里面有很多选择。
Redis缓存穿透与雪崩
缓存穿透
用户查询一个数据 redis内存数据库中没有,缓存没有命中,于是在持久层数据库查询
发现也没有,于是本次查询失败,当用户很多,缓存都没有命中(秒杀),于是都去请求了持久层数据库,这就会给持久层数据库造成很大压力,出现了缓存穿透。
布隆过滤器
对所有可能查询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,避免了对底层存储系统的压力
客户端 -》bloomfilter ->缓存层 —-〉存储层
缓存空对象
没命中的空对象缓存起来,同时设置一个过期时间,之后再访问这个数据从缓存中获取
问题:
如果空值能够被缓存起来,这就意味着缓存需要更多空间,因为这当中可能有许多空值的键
即使对空值设置了一个过期时间,还是会存在缓存层和存储层的数据会有一段时间的不一致,这对于需要保持一致性的业务会有影响。
缓存击穿
是指一个key非常热门,在不停的扛着高并发,这个key在失效的瞬间,持续的大并发就会穿透缓存,直接请求数据库。
设置热点数据永不过期
加分布式锁
使用分布式锁,保证每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限
缓存雪崩
在某一个时间段,缓存集中过期失效,redis宕机
停掉一些服务
redis高可用
多增几台redis
限流降级
这个解决方案的思想是,缓存失效时通过加锁或者队列控制数据库写缓存的线程数量
数据预热
数据预热的含义就正式部署之前,先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中,在即将发生大并发访问前手动触发并加载缓存不同的key,设置不同的缓存过期时间,让缓存失效的时间尽可能均匀