springboot
springboot工程创建方式
1.maven搭建springboot工程
创建Maven工程
pom.xml文件中配置起步依赖
编写启动引导类
编写controller
接口测试
@configuration 配置类 @enableAutoConfiguration开启自动配置 @componentScan开启包扫描
2. spring initializr 快速构建
勾选需要的starters,生成默认文件夹
@spingbootApplication 组合注解(包括了以上3个注解)
spingboot原理分析
1.依赖管理 管理starters依赖的版本,并且starters是随用随去,避免版本冲突
2.starters 依赖关系的管理和封装。 例如starters-web, 封装了web开发所需要的各种jar包。
3. 自动配置 值:autoconfiguration的jar包中 // 生效:开启自动配置的注解 // 配置类是选择加载
springboot配置文件
springboot设计思想:约定大于配置 但是也可以根据自己的需要修改默认配置
方式1:application.propertites
方式2:application.yml
springboot集成
1.spring data JPA 数据库访问和操作
2.mybatis
3.jdbc
4.redis。。。。
springboot 数据访问
基于spring data 访问关系型或者非关系型数据库
JDBC
spring:
datasource:
redis(c语言编写)
大规模集合 多重数据种类 大数据应用
-数据持久化 使用了RDB和AOF机制
-kv list set
-高可用
-原子性 单线程
-性能高
-支持集群
缺点:
持久化过程:1定时快照 2aof
占用内存过高
为什么快?机器语言编写、基于内存
基本命令:
redis客户端
key命名规范:512M user:123:password
string类型:二进制安全 不需要频繁编码解码 set get
hash:hset key field value 一个javabean hget
应用场景
存储一个用户信息对象数据
查询一个对象:
1.转换对象,序列化的问题,修改值并发cas的问题
key value user:id 1 user:name wang
2.以上方法有很多重复数据
redis连接池
1.创建redis连接池
把连接池的基本信息放在静态代码块中,这样这部分信息只用加载一次
redis支持部分事务(对于1个事务操作,有些命令是运行时才知道有错,有错的部分不执行,其他部分执行)
批量操作在发送exec命令前被放入队列缓存
收到exec命令后进入事务执行,事务中任意命令执行失败,其余命令依然被执行
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令队列中。
事务执行步骤:
multi
业务逻辑
exec - 事务命令进行执行
discard -放弃事务执行
只有出现了报告错误(非语法错误),事务才会回滚
unwatch
监视1个或者多个key,如果事务执行之前这个key被其他命令所改动,那么事务将被打断。
【
乐观锁(适合多读):在数据更新的时候判断一下在此期间有没有人去更新这个数据。版本号。
悲观锁:表锁,行锁,读锁,写锁等,在操作之前就上锁。
CAS:
】
【
发布订阅模式。进程间的消息通信模式。
】
商品秒杀 转账
数据淘汰策略:
RDB:(默认)
与数据库的一致性
缓存穿透:查询一个一定不存在的数据,由于缓存不命中需要从数据库查询,查询不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。
解决方法:持久层查询不到就缓存空结果,查询时先判断缓存中是否存在
缓存雪崩:
实时同步:
对于强一致性要求高的,查询不到就从数据库查询,再保存到缓存中;更新缓存时,先更新数据库,再将缓存的设置过期。
异步队列:
对于并发程度较高的,可以采用异步队列的方式同步,用kafka处理消息和消费
分布式锁
setnx
死锁:超时时间
原子操作:怎么实现的?
还会出现死锁?高并发 永久失效 自己的锁被其他线程失效
clientId 进行验证
定时器作为分线程 开源框架:redisson
缓存的使用?
高性能+高并发
不良的后果?
与数据库不一致
缓存雪崩
缓存穿透
缓存并发竞争
redis 单线程还支持高并发?
线程模型:socket server、产生事件、IO多路复用程序、任务队列、文件事件分派器、
文件时间处理器:
1.非阻塞io多路复用 (只进行监听压入队列)
2.纯内存操作(连接应答处理器,命令请求处理器,命令回复处理器)
3.单线程反而避免了多线程的频繁上下文切换问题。
redis数据类型及应用场景?
string
hash类:缓存简单的对象
list:粉丝列表,文章评论列表
set: 共同好友 做交集
sorted set:排行榜
过期策略?内存淘汰策略?LRU算法?
定期删除+惰性删除
定期删除:默认每隔100ms随机抽取一些设置了过期时间的key,检查是否过期,如果过期了就删除。
但是定期删除可能导致很多过期key到了时间也并没有被删除,这个时候采用惰性删除,当用户去查询1个key时,redis检查这个key是否过期,如果过期了此时就会删除,不返回任何东西。
但是可能存在没有走惰性删除,此时导致大量过期key堆积在内存里面,导致内存块耗尽。
这时候采用内存淘汰机制:
最近最少使用
怎么保证高并发以及高可用?主从复制?哨兵原理?
读写分离 一主多从 master(写) slave(读)----可以水平扩容
【主从复制:写入复制的时候是异步的
主从复制的断点续传:
无磁盘化复制
过期key处理:master淘汰了1个key,会模拟1条del命令发送给slave.
配从不配主。
复制原理?先全量复制,再增量复制,重新连接master,也会全量复制一次。
---------------------------------------------------------------------------------
主机宕机后?从机还是从机;主机回来后,工作依旧进行。
从机死了?与master已经断开,需要重新连接。再重新配置。
-----------------------------------------------------------------------------------
去中心化复制。
上一个Slave是下一个slave的master,可以有效减轻master的写压力。
-------------------------------------------------------------------------------------
SLAVEOF no one 使当前数据库停止与其他数据库的同步,转成主数据库。
--------------------------------------------------------------------------------------
哨兵模式?
概念:能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
如果原主机回来了,会成为从机。
一个哨兵可以监控多个master。
主从复制缺点:复制延时
】
【
java 客户端操作--事务
】
数据同步的核心机制
1.都维护一个offset :知道彼此数据不一致的情况
2.backlog :
3.run_id :变化了,就触发全量复制
4.psync
高可用
持久化:RDB AOF
1.RDB 在指定时间将快照写入磁盘备份,恢复从磁盘写回内存 dump.rdb
单独fork1个子进程进行持久化,先将数据文件写入1个临时文件,当持久化过程结束后,再用临时文件替换上次持久化号好的文件。
整个过程,主进程不进行IO操作,确保了极高的性能。所以如果对数据完整性不敏感,使用RDB.
缺点是:最后1次持久化的数据可能丢失。
2.如何触发RDB快照?
/配置文件中默认的配置。(冷拷贝后重新使用)
/save命令
/flushall
3.AOF
以日志的形式记录每个写操作,只追加文件不改写,redis启动后会读取该文件重新构建数据。
RDB和AOF可以共存,先找AOF
配置:默认 异步操作 每秒记录,如果1秒内宕机,有数据丢失
AOF启动/恢复/修复:
Rewrite: 当AOF文件超过所设定的阈值时,Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集。
重写原理:fork1个新的进程将文件重写。
优点:灵活配置(每秒同步,修改同步,不同步)
缺点:相同数据集的数据而言aof文件要远大于rdb文件,恢复速度慢于rdb
分布式数据库的CAP原理
强一致性、可用性、分区容错性
分布式系统中,一定要满足P。
CA:传统数据库
AP:大多数网站架构的选择
CP:redis
BASE:基本可用、软状态、最终一致 让系统放松对某一时刻数据一致性的要求来换取系统整体伸缩性和性能上的改观。
分布式和集群的区别:
分布式:不同的多台服务器上面部署不同的服务模块,通过rpc调用
集群:不同的多台服务器上面部署相同的服务模块,统一调度,对外提供访问
redis-- kv数据库
与SQL对比:数据模型简单,没有解析字符串的过程,对于给定key,容易映射复杂值环境。
与其他k-v产品对比:支持数据持久化、多种数据结构、支持数据备份,集群高可用、所有操作都是原子性的、单线程
缺点:对内存占用过高
key的命名规范
key512M
数据类型
1.string :一个key,对应一个value
2.hash :适合存储对象。key field value (根据field获取相应的value) user(id,name,age) field相当于id,value相当于id的值
3.list: key [value1,value2....] 适合任务队列 例如网站实名认证检查、订单的下单流程(不同环节环环相扣)
4.set: string类型的无序集合 底层实现:intset(数组)、hashtable
5.zset: string类型的元素集合,每个元素关联一个double类型的分数。 适合:排行榜(把时间当作分数)、带权重的任务队列
redis事务
1.将一个事务中的所有命令序列化,然后按顺序执行
2.执行中不会被其他命令插入
只有出现报告错误,才会回滚,否则继续执行。
redis watch
监视key,如果事务执行之前这个key被其他命令改动,则事务被打断。
redis 数据淘汰
内存不足时,会根据数据淘汰算法淘汰部分key,保证写入成功,没有找到合适,返回OOM错误。
数据淘汰算法:最近最少算法
redis 持久化
1.RDB 默认 快照 方式写入到二进制文件中,一定间隔时间做一次快照
优点:保存数据快,还原数据快
缺点:不适合小机器
2.AOF :更好的持久化性。每次将写命令通过write函数追加到文件中
优点:更好的持久化性
缺点:持久化文件会越来越大
redis与数据库的一致性
1.实时同步
更新缓存时,先更新数据库,再将缓存的设置过期
--缓存穿透:查询一个一定不存在的数据时,由于缓存不命中需要从数据库查询,查不到数据则不写入缓存,这会导致这个不存在的数据每次请求都要到数据库去查询。
解决方法:在redis中增加这个查询的key,赋空值。
--缓存雪崩:缓存集中在一段时间内失效,发生大量的缓存穿透,所有查询落在了数据库上。
2. 异步队列
kafka
redis高并发 在工程项目中,一般是集群
1.高可用
2.高并发 响应时间 吞吐量 每秒查询率qps
垂直扩展:单机硬件性能 水平扩展
怎么保证redis高并发和高可用?
高并发:主从复制(多读少写 ,读写分离)
masterNode复制原理
1.通过RDB全量复制给slave,slave将其从磁盘加载到内存
2.master再将内存中缓存的最新数据发给slave
一般情况下,异步复制,slave在做复制的时候,不会block对自己的查询操作,会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧的数据集,加载新的数据集,此时暂停对外服务。
master持久化
如果采用了主从架构,就必须开启master的持久化。
redis为什么单线程还能保持高性能?
文件事件处理器是单线程的
1、客户端socket和redis的socket建立连接,触发ae_readable事件;
2、io多路复用程序监听到该事件,将任务放在队列里面;
3、任务依次交给文件事件分派器进行相应的处理。
原因:
1、非阻塞IO多路复用(只负责监听,转发,不负责处理)
2、纯内存操作
3、单线程反而避免了多线程的频繁上下文切换问题。
redis的并发竞争问题是什么?如何解决?redis事务的cas方案?
多个实例同时更改同一个key时产生并发问题。
解决:分布式锁(zk)。每次写之前,判断value的时间戳是否比缓存里的时间戳要新。
zk和redis分布式锁的区别? 分布式锁:在分布式环境下,多个操作也要以原子的方式执行
redis:redLock 如果redis中没有要设置的key,拿锁成功,否则失败;如果没有拿到锁,每隔1秒尝试拿锁。
释放锁就是删除key,一般用lua脚本删除。
线程不安全:共享变量 、变量的可见性和操作的原子性
基于数据库
数据库支持行级锁 // 实现简单 // 性能差,容易死锁(unlock之前程序停掉,其他线程拿不到锁)
基于redis
1.缓存有效期:数据不一定都是持久化的,当key过期时,会自动删除。
2. setnx: set if not exists. 设置 nx px
3. lua 脚本:支持redis操作序列的原子性。
加锁过程:通过setnx向特定的key写入1个随机值(避免锁误删),并同时设置失效时间(避免死锁),写值成功就加锁成功。
解锁过程:匹配随机值,删除redis上的特点key数据,要保证获取数据、判断一致和删除数据三个操作是原子的(通过lua脚本[因为redis是单线程的,所有这个时候只执行lua脚本,从而保证原子性])。
缺点:没有通知机制,可能出现死锁
threadlocal
基于zk(文件系统+监听机制)
zk数据节点
1.持久化节点: 客户端与zk断开连接后,该节点依旧存在。
2.临时节点:断开连接,节点自动删除。创建成功后,其他线程不能创建,会提示节点存在,抛出异常。
3.临时有序节点
独占锁:(悲观锁)
只有1个客户端能获得锁。
每个线程都有1个闭锁,减到0之前,都会阻塞,直到锁被释放:countDownLatch 计算器减1
500,节点删除,通知499个客户端重新获取锁。羊群效应,对性能有损耗。
时序锁:(乐观锁)
先排序,每次从最小节点开始获取锁,每个节点只监听比自己小1 的节点是否删除,而不用监听所有的节点。
为什么高可靠?
怎么解决死锁?
1.临时节点 会自动删除
多线程通信: