- **本地方法栈:**存储本地方法,主要由native修饰
- **Java虚拟机栈:**存储变量的值或对象的引用
- **堆:**存储对象
其中堆内存分配分为eden区、s0(from)区、s1区(to)、tenure区,而eden区、s0及s1又被称之为年轻代,tenure被称之为老年代
3.1)eden区:存储新创建的对象
3.2)s0(from)区、s1(to)区也被称之为(survivor)幸存区:存储执行GC(垃圾回收)之后没有被回收的对象,会互相切换
3.3)tenure区:存储多次执行GC垃圾回收之后没有被回收的对象,或者新创建的较大的对象 - **方法区:**存储由static修饰的变量、方法、代码块以及常量
- 程序计数器
应用场景:
前端:下载文件
后端:基本上后端所有的服务都应用到了多线程,比如来自前端的服务请求,优化性能增加CPU核数,分布式计算,异步处理,给不同的用户发送信息
定时任务,商品详情的展示
3、活锁和死锁问题分析,什么是活锁,什么是死锁,出现原因是什么?如何避免?**死锁:**是指多线程因争夺资源而造成的一种互相等待对方资源的现象,若无外力,他们将一直阻塞。
死锁条件:
- 互斥:线程在某一时间内独占资源。
- 请求与保持:一个线程因请求资源而阻塞时,对已获得的资源保持不放。 放大锁范围 或一次申请所有资源
- 不可剥夺:一个线程已获得的资源,在未释放之前,不能被其他线程占有。 占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源 设定过期时间
- 循环等待:若干线程之间行成一种头尾相接的循环等待资源的关系。 顺序加锁 按顺序来请求资源
**活锁:**任务或者执行者并没有被阻塞,由于某些条件没有满足,导致一直重复尝试、失败、尝试、失败。 等待一个随机时间
day02 1、多线程下如何做到数据共享(可拓展数据共享的使用场景)-
如果每个线程执行的代码相同,可以:
1.1)继承Thread类可以使用静态变量。
1.2)实现Runnable接口,在接口里面定义成员变量
例如售票系统 -
如果每个线程执行的代码不同,那么就需要不同的Runnable对象,有两种方式实现这些Runnbale对象之间的数据共享
2.1) 将共享数据封装在另一个对象中,然后实现不同的 *** 作,然后将这个新对象传递给Runnable对象,每个线程对共享数据的 *** 作也就相应的分配到了那个对象身上去完成。这样就能够实现对该数据的各种 *** 作的互斥与通信
2.2) 将这些Runnbale对象作为某一个类中的内部类,共享数据作为外部类的成员变量,每个线程对共享数据的 *** 作也就分配给了外部类,以便实现共享数据的各种 *** 作的互斥通信。
- 互斥同步
使用synchronized(同步)关键字。
synchronized实现同步的基础就是:Java中的每一个对象都可以作为锁。
具体实现方式:
a.普通方法同步,锁是当前实例对象
b.静态方法同步,锁是当前类的class对象
c.代码块同步,锁是synchronized括号里匹配的对象 - 非阻塞同步
具体实现方式:CAS,使用JUC包下的整数原子类decompareAndSet()和getAndIncrement()方法
缺点:
a.容易出现ABA问题,使用版本号来解决
b.只能保证一个变量的原子 *** 作,使用AtomicReference类来保证对象之间的原子性。可以把多个变量放在一个对象里。 - 无同步方案
线程本地存储:将共享数据的可见范围限制在一个线程中。这样无需同步也能保证线程之间不出现数据争用问题。通常使用的就是ThreadLocal类
并发编程三要素(线程的安全性问题体现在)
原子性:全部成功,全部失败
可见性:另一个线程能够立刻看到一个线程对共享变量的修改
有序性:程序执行的顺序按照代码的先后顺序执行,避免指令重排
线程安全问题的原因:
线程切换带来的原子性问题
缓存导致的可见性问题
编译优化带来的有序性问题
基于hash表的Map接口的非同步实现,允许一个null键,多个null值。
put的使用
数据结构不同:ArrayList是动态数组结构,linkedList是链表数据结构
效率不同:ArrayList查询快,增删满(需要数组的复制),linkedList首尾查询快,节点多的话查询慢,增删快
自由性不同:ArrayList自由性较低,因额为他需要手动设置固定大小的容量。linkedList自由性较高,能够动态的随数据量的变化而变化。
主要控件开销不同:Arraylist的主要控件开销在于需要在list列表预留一定空间,而linkedList主要控件开销在于需要存储结点信息以及结点指针信息。
- ConcurrentHashMap是线程安全的,HashMap不是线程安全的
- ConcurrentHashMap采用的是分段锁机制,锁的粒子性小(对桶数组进行分段处理),保证性能的同时是线程安全的
- 将数据从磁盘或网络拷贝到 *** 作系统(OS)内核Buffer(内核缓存)
- 将 *** 作系统内核数据拷贝到用户进程(如JVM缓存)。
BIO:阻塞IO模型,线程发起IO调用后,线程被阻塞,等待 *** 作系统(OS)内核Buffer准备好数据,将数据从内核拷贝到用户空间。
NIO:IO复用模型,多个线程的IO注册到一个复用器 selector,然后用一个线程调用selector,selector会监听所有注册进来的IO。如果所有IO都没有数据准备好,selector被阻塞,当任一IO的内核缓冲区有数据时,selector调用就会返回,将数据从内核拷贝到用户空间。其中selector线程被阻塞,IO线程不阻塞。
channel:类似fileInputStream
buffer:缓冲区,buffer可切换读写
数据总是通道写到缓冲区,或通道从缓冲区读
AIO:异步IO模型,当一个线程发起IO调用后,线程直接返回(不阻塞)。内核准备好数据,并把数据从内核拷贝到用户空间后,通知线程结果。如果IO *** 作成功则线程直接获取到数据。
开源的、轻量级、松耦合的、低侵入性的、企业级一站式Java开发框架。
Spring核心: IOC控制反转IOC:控制反转,即把对象的控制权交给IOC容器管理,DI(依赖注入)是IOC的实现方式之一,依赖注入的三种方式:setter方法注入,构造方法注入,基于注解注入(字段注入)
AOP面向切面编程AOP:面向切面,在不改变原有功能的情况下拓展功能,拦截器就是AOP的实现思想之一,前置通知、后置通知、最终通知(返回后通知)、环绕通知、异常通知
静态代理:AspectJ
动态代理:Spring AOP(jdkproxy和cglib)
Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。 Spring 的 IoC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务。
IOC是存放Java对象的容器。IOC的实现分为三步:
1)Resource定位:Resource是Spring对于用户定义Bean文件的统一标准,
的统一封装。为了读取 Resource,Spring 还设计了 ResourceLoader
2)BeanDefinition的载入和注册:载入就是将Resource中的内容解析出来,转换成Spring内部定义的数据结构BeanDefinition。
3)**依赖注入:**从SpringIOC容器获取对象。
AOP是面向切面编程,采用横向抽取机制,是在不修改原有代码的条件下对于面向对象编程的补充和完善。
AOP作为面向切面编程,在项目后期,我们在添加新的功能时,不能直接修改之前的方法,这样无法保证之前的代码可以正常运行,增加了代码的耦合性,而且牵一发而动全身,代码的改动量会加大。也可以新建一个类,继承原有的类,在进行方法重写,这样虽然会降低代码的耦合性但这样或导致类爆炸。所以这时候采用AOP。来进行切面编程,在需要加入新功能的地方加入一个切面,当程序运行到切面时,有需求时会进入切面运行切面中的代码。不仅降低了代码的耦合性,还不会产生类爆炸的情况。提高了复用性和管理的便捷性。
切入点表达式
1.bean(“包名.类名”) 具体某个类 按类匹配 粗粒度
2.within(“包名.类名”) 可以使用通配符 几个类一起匹配 粗粒度
3.execution(返回值类型 包名.类名.方法名(参数列表)) 细粒度
4.注解方式 @annotation(注解的类型) 细粒度
AOP最常见的使用:把业务逻辑和系统服务分开(声明式事务管理,权限和日志记录)。
AOP原理:Spring 提供了两种方式来生成代理对象:JdkProxy 和 Cglib,具体使用哪种方式生成,由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类实现了接口,则用 JdkProxy 来实现,否则使用 Cglib。
说明:当我们在项目中定义了AOP切面以后,系统启动时,会对有@Aspect注解描述的类进行加载分析,基于切入点的描述为目标类型对象,创建代理对象,并在代理对象内部创建一个执行链,这个执行链中包含拦截器(封装了切入点信息),通知(Around,…),目标对象等,我们请求目标对象资源时,会直接按执行链的顺序对资源进行调用。
BeanFactory是个Factory,是IOC容器的接口,提供了一套标准的Bean的规范。
FactoryBean是对Bean拓展的接口,是创建Bean的一种形式,用户可以通过实现该接口定制实例化Bean的逻辑。融合了简单的工厂设计模式和装饰者模式
在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。
但对FactoryBean而言,这个Bean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂模式和修饰器模式类似。
N个Bean互相引用对方,最终形成闭环。
构造器注入循环依赖
使用三级缓存解决。
- bean的定义
- bean的实例化
实例化一个Bean,也就是我们常说的new。 - IOC依赖注入****bean的属性注入
按照Spring 上下文对实例化的Bean 进行配置,也就是IOC 注入。 - setBeanName实现
如果这个Bean 已经实现了BeanNameAware 接口,会调用它实现的setBeanName(String)
方法,此处传递的就是Spring 配置文件中Bean 的id 值 - BeanFactoryAware实现
如果这个Bean 已经实现了BeanFactoryAware 接口,会调用它实现的setBeanFactory,
setBeanFactory(BeanFactory)传递的是Spring 工厂自身(可以用这个方式来获取其它Bean,
只需在Spring 配置文件中配置一个普通的Bean 就可以)。 - ApplicationContextAware实现
如果这个Bean 已经实现了ApplicationContextAware 接口,会调用
setApplicationContext(ApplicationContext)方法,传入Spring 上下文(同样这个方式也
可以实现步骤4 的内容,但比4 更好,因为ApplicationContext 是BeanFactory 的子接
口,有更多的实现方法) - postProcessBeforeInitialization接口
如果这个Bean 实现了BeanPostProcessor 接口,将会调用
postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor 经常被用
作是Bean 内容的更改,并且由于这个是在Bean 初始化结束时调用那个的方法,也可以被应
用于内存或缓存技术。 - init-method
如果Bean 在Spring 配置文件中配置了init-method 属性会自动调用其配置的初始化方法。 - postProcessAfterInitialization
如果这个Bean 实现了BeanPostProcessor 接口,将会调用
postProcessAfterInitialization(Object obj, String s)方法。
注:以上工作完成以后就可以应用这个Bean 了,那这个Bean 是一个Singleton 的,所以一般情况下我们调用同一个id 的Bean 会是在内容地址相同的实例,当然在Spring 配置文件中也可以配置非Singleton。 - Destroy 过期自动清理阶段
当Bean 不再需要时,会经过清理阶段,如果Bean 实现了DisposableBean 这个接口,会调用那个其实现的destroy()方法;destroy-method 自配资清理
最后,如果这个Bean 的Spring 配置中配置了destroy-method 属性,会自动调用其配置的销毁方法。
- 用户点击某个请求路径,发起一个 HTTP request 请求,该请求会被提交到 DispatcherServlet(前端控制器);
- 由 DispatcherServlet 请求一个或多个 HandlerMapping(处理器映射器),并返回一个执行链(HandlerExecutionChain);
- DispatcherServlet 将执行链返回的 Handler 信息发送给 HandlerAdapter(处理器适配器);
- HandlerAdapter 根据 Handler 信息找到并执行相应的 Handler(常称为 Controller);
- Handler 执行完毕后会返回给 HandlerAdapter 一个 ModelAndView 对象(Spring MVC的底层对象,包括 Model 数据模型和 View 视图信息);
- HandlerAdapter 接收到 ModelAndView 对象后,将其返回给 DispatcherServlet ;
DispatcherServlet 接收到 ModelAndView 对象后,会请求 ViewResolver(视图解析器)对视图进行解析; - ViewResolver 根据 View 信息匹配到相应的视图结果,并返回给 DispatcherServlet;
- DispatcherServlet 接收到具体的 View 视图后,进行视图渲染,将 Model 中的模型数据填充到View 视图中的 request 域,生成最终的 View(视图);
- 视图负责将结果显示到浏览器(客户端)。
三次握手
第一次,客户端发送SYN包和客户端的序列号到服务器,
第二次,服务器返回SYN包和ack和服务器的序列号,ack是序列号加1,
第三次,客户端发送SYN包和之前的ack和服务器的序列号加1新生成的ack到服务器,
客户端和服务器都确认服务器和客户端的收发能力正常。
使用#{}而不使用${},前者的功能和PreparedStatement的预编译 sql语句中的?占位符相似。
3、Mybatis框架有几级缓存,默认开启几级?(理论性)二级缓存,默认开启一级。
一级缓存是同个sqlsession相同sql语句字段会进行缓存,重复查询不需要再从数据库查询。
二级缓存是不同sqlsession中相同sql语句相同字段的缓存会进行共享。
和大多数持久层框架一样,MyBatis 提供了一级缓存和二级缓存的支持。默认情况下,MyBatis 只开启一级缓存。
- 一级缓存只作用于SqlSession,SqlSession关闭,一级缓存清空,因此,一级缓存缓存的是SQL语句
- 二级缓存是全局缓存,作用域超出SqlSession范围之外,可以被所有SqlSession共享,实现了跨SqlSession,因此,二级缓存缓存的是结果集对象
第一部分:SpringApplication初始化模块,配置一些基本的环境变量,资源,监听器,构造器;
第二部分:实现了应用具体的启动方案,包括流程的监听模块,加载配置环境模块以及创建上下文环境模块
第三部分:自动化配置模块,这个模块是实现SpringBoot的自动配置
启动:
每个SpringBoot程序都有一个主入口,也就是main方法,main里面调用SpringApplication.run()启动整个spring-boot程序,该方法所在类有一个@SpringBootApplication注解,它是一个组合注解;
首先进入run方法
run方法中去创建了一个SpringApplication实例,
- 使用SpringFactoriesLoader在应用的meta-INFO/spring.facories中查找并加载所有可用的ApplicationContextInitializer。
- 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的ApplicationListener。
在该构造方法内,我们可以发现其调用了一个初始化的initialize方法
该方法中实现了如下几个关键步骤:
- 创建了应用的监听器SpringApplicationRunListeners并开始监听
- 加载SpringBoot配置环境(ConfigurableEnvironment),如果是通过web容器发布,会加载StandardEnvironment
- 配置环境(Environment)加入到监听器对象中(SpringApplicationRunListeners)
- 创建run方法的返回对象:ConfigurableApplicationContext(应用配置上下文)
- 回到run方法内,prepareContext方法将listeners、environment、applicationArguments、banner等重要组件与上下文对象关联
- 接下来的refreshContext(context)方法(初始化方法如下)将是实现spring-boot-starter-*(mybatis、redis等)自动化配置的关键,包括spring.factories的加载,bean的实例化等核心工作。
refresh方法
配置结束后,Springboot做了一些基本的收尾工作,返回了应用环境上下文。回顾整体流程,Springboot的启动,主要创建了配置环境(environment)、事件监听(listeners)、应用上下文(applicationContext),并基于以上条件,在容器中开始实例化我们需要的Bean,至此,通过SpringBoot启动的程序已经构造完成
@SpringBootApplication找到@EnableAutoConfiguration导入@AutoConfigurationimportSelector类
再调用selectImprots()
String(博客的字数统计strlen,审计日志追加到指定key append,分布式自增id incr,博客的点赞 *** 作incr decr)
Hash(发布博客写内存hmset,浏览博客hmget,判断博客存在hexists,删除hdel,存储用户信息hmset,简易购物车)
List(队列lpush/rpop,栈,阻塞式队列lpush/brpop,秒杀活动公平性FIFO,消息队列,动态更新商品的销量列表卖得好的靠前linsert,商家的粉丝列表)
Set(朋友圈的点赞sadd,srem,smembers,scard,网站投票统计,微博的关注)
ZSet。
雪崩:一般缓存都是定时任务去刷新,大量缓存同一时间失效,请求全部到达数据库,造成数据库崩溃
解决:
- 每个key的失效时间设置一个随机值
- 设置热点数据永不过期(有更新 *** 作就更新缓存就好了)
- 检查更新:缓存依然保持设置过期时间,每次 get 缓存的时候,都和数据的过期时间和当前时间进行一下对比,当间隔时间小于一个阈值的时候,主动更新缓存。
- 通过互斥锁或者队列,控制读数据库和写缓存的线程数量。
**击穿:**一些热点数据的缓存失效,一般热点数据的请求都是大并发,这些请求到达数据库,就会使数据库崩溃
解决:
使用互斥锁,或者设置热点数据永不过期
**穿透:**用户不断查询缓存和数据库都不存在的数据,比如id为-1或者特别大的id,就会越过缓存直接到数据库查询
解决:
- 接口层增加校验(用户鉴权校验、参数校验,不合法的参数直接拦截)
- 查询的数据为空的key的值也进行缓存,比如null,稍后重试等。过期时间可以设置的短一些。
- nginx层对单个ip每秒访问过大的做出限制或拉黑
- 使用布隆过滤器。一定不存在的数据就会被过滤拦截。
虽然没有遇到,但我们事先进行了预防措施
布隆过滤器:所有可能的数据事先存储到一个足够大的bitmap位图,是有序数组,存储0,1,0存在,1不存在,数据hash后存储,防止碰撞可以多次hash,(redis新的数据结构,实际上还是string)。
通用解决方法:本地缓存,sentinel限流降级熔断,数据库加同步锁。redis主从架构,redis集群。 3、数据库中有2000W条数据,而Redis当中只有20W条数据,如何保证Redis中的数据一定是热点数据?(可考虑设置缓存过期时间)redis里有6种淘汰策略(5.0八种)
全局的键空间选择性移除- allkeys-lru(常用):当内存不足以容纳新写入数据时,在全局键空间中,移除最近最少使用的key。
- allkeys-random:当内存不足以容纳新写入数据时,在全局键空间中,随机移除某个key。
- no-eviction:当内存不足以容纳新写入数据时,新写入 *** 作会报错。
- allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。
- volatile-lru(常用):当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key 优先移除。
- volatile-lfu:从已设置过期时间的数据集挑选使用频率最低的数据淘汰。
我们可以大致计算一下20W数据所占的内存,通过限定redis的内存大小,并将淘汰策略设置为volatile-lru(常用)或allkeys-lru(常用),redis就会根据淘汰策略,删除不常用的数据,留下常用的热点数据。
打开redis配置文件,设置maxmemory参数,maxmemory是bytes字节类型。
应用:简单缓存失效:缓存每命中一次,增加一定过期时间
应用:24h活跃用户,set存放2天用户,登录覆盖时间,设置周期任务,每天3点删除前一天3点前的数据
索引就是数据库额外维护了一张表,相当于目录,是一个排序的数据结构,通常使用B+树或hash表。
优点:
解决慢sql,提高查询效率。
缺点:
创建和维护索引要消耗时间和空间。增、删、改的时候因为要动态维护索引,会降低效率。索引最大不超过5个
普通索引:仅加速查询
唯一索引:加速查询 + 列值唯一(可以有null)
主键索引:加速查询 + 列值唯一(不可以有null)+ 表中只有一个
组合索引:多列值组成一个索引,专门用于组合搜索,其效率大于索引合并
全文索引:对文本的内容进行分词,进行搜索
- 为常作为查询条件的字段建立索引,where子句中的列,或者连接子句中指定的列
- 为经常需要排序、分组 *** 作的字段建立索引
- 更新频繁字段不适合创建索引
- 不能有效区分数据的列不适合做索引列(如性别,男女未知,最多也就三种,区分度实在太低)
- 对于定义为text、image和bit的数据类型的列不要建立索引
- 最左前缀原则,就是最左边的优先。指的是联合索引中,优先走最左边列的索引。对于多个字段的联合索引,如 index(a,b,c) 联合索引,则相当于创建了 a 单列索引,(a,b)联合索引,和(a,b,c)联合索引(但并不是建立了多个索引树)。mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。使用短索引,如果对长字符串列进行索引,应该指定一个前缀长度,这样能够节省大量索引空间
- 非空字段:应该指定列为NOT NULL,除非你想存储NULL。在mysql中,含有空值的列很难进行查询优化,因为它们使得索引、索引的统计信息以及比较运算更加复杂。你应该用0、一个特殊的值或者一个空串代替空值
- 不要过度索引。索引需要额外的磁盘空间,并降低写 *** 作的性能。在修改表内容的时候,索引会进行更新甚至重构,索引列越多,这个时间就会越长
商品id就是主键索引,商品名字经常查询也可以作为索引
3、MySql中事务的隔离级别?什么是事务?(理论内容)读未提交,不可重复读,可重复读,串行化
事务就是一种机制,是一组 *** 作,这组 *** 作要么都执行,要么都不执行。
遵循ACID规则,
原子性,要么都成功,要么都失败
一致性,执行前后处于一致性状态
隔离性,并发事务之间相互隔离
持久性,事务一旦提交,数据的改变是永久性的。
- Synchronized是关键字,Lock是接口,
- synchronized可以给方法、代码块加锁, lock只能给代码块加锁
- synchronized自动获取释放锁,线程异常自动释放锁。 lock自己加锁和释放锁(finally中),易造成死锁。
sleep是Thread的静态方法,sleep是休眠一定时间,时间到了之后自动变为就绪状态,不会释放锁
wait是Object的实例方法,需要notify或notifyAll唤醒,会释放锁
悲观锁:认为所有 *** 作都会修改数据,所以每次 *** 作都需要上锁。
乐观锁:认为所有 *** 作都不会修改数据,所以不上锁,只是更新的时候会判断一下数据有没有被更新过。可以用CAS算法(会出现ABA问题)和版本号机制实现。
死锁:多个线程因为互相争抢资源导致互相等待对方资源的情况,没有外力的话,会一直阻塞。
活锁:多线程互相谦让资源,导致双方都无法使用资源。
hashmap是不安全的,都是数组加链表,1.8以后加了红黑树。key允许一个null。HashMap的初始容量为16,两者的填充因子默认都是0.75
hashtable是安全的,都是数组加链表。key不允许null。Hashtable初始容量为11,两者的填充因子默认都是0.75
产生:多线程互相争抢资源导致互相等待的情况,如果没有外力将一直阻塞
避免:
放大锁的范围,一次性请求所有资源。
设置一定时间如果请求不到资源,释放已经占有的资源。
顺序加锁。
使用多线程:商品详情的展示,秒杀商品定时上架,
使用锁:秒杀库存使用分布式锁的信号量机制。
内存溢出:当申请内存时,没有足够的内存空间。
内存泄漏:内存申请后,无法释放已经申请的内存,短生命周期对象被长生命周期对象引用,导致前者无法被回收。内存泄漏发生频繁会导致内存溢出
持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
两种持久化机制 RDB(默认) 和 AOF 机制
按照一定的时间间隔将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb。通过配置文件中的save参数来定义快照的周期。
配置
表示 60 秒内如果至少有 1000 个 key 的值变化,则保存
save 60 1000
分为save(同步)和bgsave(异步)
工作流程- redis根据配置尝试去生成rdb快照文件
- redis主进程fork一个子进程出来
- 子进程尝试将内存中的数据dump到临时的rdb快照文件中
- 完成rdb快照文件的生成之后,覆盖旧的快照文件
- 只有一个文件 dump.rdb,方便持久化,容灾性好。
- 性能最大化,fork 子进程来完成写 *** 作,让主进程继续处理命令,保证了 redis 的高性能
- 数据集大时,比 AOF 的启动效率更高。
- 数据安全性低。RDB 是间隔一段时间进行持久化,如果持久化之间 redis 发生故障,会发生数据丢失。
将Redis执行的每条写命令记录到单独的aof日志文件中,当重启Redis服务时,会从持久化的日志文件中恢复数据。
当两种方式同时开启时,数据恢复时,Redis会优先选择AOF恢复。
- 所有的写入命令会追加到AOF缓冲中。
- AOF缓冲区根据对应的策略向硬盘做同步 *** 作。
1)appendfsync always (同步持久化,每次发生数据变更会被立即记录到磁盘,性能差但数据完整性比较好)
2)appendfsync everysec (异步 *** 作,每秒记录,如果一秒钟内宕机,有数据丢失)
3)appendfsync no (将缓存回写的策略交给 *** 作系统,linux 默认是30秒将缓冲区的数据回写硬盘的) - 随着AOF文件越来越大,需要定期对AOF文件进行重写,达到压缩的目的。
1)手动执行 bgrewriteaof 触发AOF重写
2)在redis.conf文件中配置重写的条件 - 当Redis服务器重启时,可以加载AOF文件进行数据恢复。
- 数据安全,可以配置每进行一次命令 *** 作就记录到 aof 文件中一次。
- 通过 append 模式写文件,即使中途服务器宕机,可以通过 redis-check-aof 工具解决数据一致性问题。
- AOF 文件比 RDB 文件大,且恢复速度慢。
- 数据集大时,比 rdb 启动效率低。
Java垃圾回收机制:GC 是垃圾收集的意思(Gabage Collection),在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行,这就是垃圾回收机制,垃圾回收机制有效的防止了内存泄露
垃圾回收器的基本原理:在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫描那些没有被引用的对象,并将它们添加到要回收的集合中,进行回收。
垃圾回收器可以马上回收内存吗?:程序员不能实时的对某个对象或所有对象调用垃圾回收器进行垃圾回收。但可以手动执行System.gc()主动通知虚拟机进行垃圾回收,但是Java语言规范并不能保证GC一定会执行(如果-XX:+DisableExplicitGC设置成true,则不会进行回收)。
4、说一下 JVM 有哪些垃圾回收算法?-
标记-清除算法:标记无用对象,然后进行清除回收。缺点:效率不高,无法清除垃圾碎片。
-
(标记-)复制算法:按照容量划分两个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的那块内存空间清理掉。缺点:内存使用率不高,只有原来的一半。
-
标记-(清除-)整理算法:标记无用对象,让所有存活的对象都向一端移动,然后清除掉端边界以外的内存。
-
分代收集算法:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代采用复制算法,老年代采用标记整理算法。
G1垃圾回收器:
将堆划分为若干个区域(Region)。一个 H 区装不下这个大对象时,G1 会寻找连续的 H 区来存储,为了能够找到连续的 H 区,有时候不得不启动一次 Full GC。
为每个对象创建一个引用计数器,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;
可达性分析算法虚拟机默认采用的是可达性分析算法。
当一个对象到GC Roots(起始对象,活跃引用)不可达时,在下一个垃圾回收周期中尝试回收该对象。定义一系列的 GC ROOT 为起点。从起点开始向下开始搜索,搜索走过的路径称为引用链。当一个对象到 GC ROOT没有任何引用链相连的话,则对象可以判定是可以被回收的。
- 当不能从GC Root寻找一条路径到达该对象时,将进行第一次标记。
- 第一次标记后检查对象是否重写了finalize() 和是否已经被调用了finalize()方法。若没有重写finalize()方法或已经被调用,则进行回收。
- 在已经重写finalize()方法且未调用的情况下,将对象加入一个F-Queue 的队列中,稍后进行第二次检查
- 在第二次标记之前,对象如果执行finalize()方法并完成自救,对象则不会被回收。否则完成第二次标记,进行回收。值得注意的是finalize()方法并不可靠。
- 栈中引用的对象;
- 静态变量、常量引用的对象;
- 本地方法栈 native 方法引用的对象。
singletonFactories : 单例对象工厂的cache 三级缓存
earlySingletonObjects :提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥】 二级缓存
singletonObjects:单例对象的cache,一级缓存
1.session的数据是临时存储.cookie的数据可以永久保存. (生命周期不同)
2.sesion是浏览器中的一个内存对象!而cookie是一个实际的本地文件. (形式不同).
3.session一般存储是一些涉密数据.cookie一般存储相对公开的数据(免密登录). (安全性)
数据表结构的优化,比如遵循三范式,但根据情况也可以反规范化
适当的使用索引
sql语句的优化,比如不要使用select *,不要使用like,多表联查的时候小表带动大表
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)