赞
踩
spring的核心技术:IOC(控制反转),AOP面向切面编程
IoC(控制反转)
IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。
创建对象的控制权进行转移,以前创建对象的控制权是由自己把控的,而现在这种权力转移IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,原先的依赖关系就没了,它们都依赖IoC容器,通过IoC容器来建立它们之间的关系。
DI(依赖注入),可以说是IoC其中的一个内容,IOC容器创建好对象后再给调用者注入。他通过反射机制实现。有三种注入方式:
(1)接口注入(2)构造器注入(3)Setter方法注入。
AOP面向切面编程,在不修改源代码的情况下,对原有功能进行扩展,通过代理类对具体类进行操作,实现了高内聚开发。
什么是切面? 切面简单来说,把重复的代码形成它的类就是切面类。(工具类)
面向切面编程? 面向重复代码编程,重复代码只要写一次,自动调用自动运行。
Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理。JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。
Spring Aop中一共有四种增强: @before前置通知 @AfterReturning 后置通知 @AfterThrowing 异常通知 @After 最终通知 @Component 创建切面类对象 @Aspect指定当前类为切面类
定义切面类
@Component 创建切面类对象
@Aspect指定当前类为切面类
配置切入点表达式 pointcut expression=“execution(* com.imooc.aop.schema.advice.biz.Biz.(…))” 作用是对符合切入点表达式规则的类自动生产代理对象,使用环绕通知方法进行AOP编程,在某个方法调用前植入增强,前置通知,后置通知,异常通知,最终通知。
常见的切面类有:事务切面, 日志切面,权限切面。
我举个例子:
以前我们在开发一套后台管理系统时必然有些操作,先要验证用户是否已经登陆,如果登陆还可能要判断这个用户对应的角色是否有当前操作的权限。这时,就需要用到AOP切面处理 这种权限验证问题。
优势:
减少重复代码,提高开发效率
统一管理统一调用,方便维护。
根据业务需求,编写核心业务代码
把公用代码抽取出来,制作成通知,通知所在的类就是切面类
通过配置的方式,建立切入点(业务功能)和通知的关系
优点:
1 spring中避免了关键字new造成的耦合问题。
2 spring本身就是一个工厂,不需要再编写工厂类了。
3 spring不需要进行明确的引用关系的传递,直接通过配置完成
4 所有框架几乎都可以在spring中整合在一起使用。
5 spring编程=factory设计模式+proxy设计模式
IOC也是一种编程思想,用于实现模块之间的解耦,在Spring中它的作用是: 对象的创建,维护和销毁等生命周期的控制。IOC:把对象的创建、初始化、销毁都交给spring来管理,而不是由开发者控制,实现控制反转。
spring是一个大的工厂类,spring的特点就是基于配置,在其配置文件中通过元素来创建实例对象。
根据业务逻辑来看,对象经常不是独立的,一个对象的创建往往涉及另一个对象的创建,当然这个对象也要由IOC容器负责,负责的方式就是依赖注入DI,通过反射机制实现。有三种注入方式:(1)接口注入(2)构造器注入(3)Setter方法注入。
当然,它的缺点也是不少的:
1 spring基于大量的xml 配置文件,使得我们花了大量的时间放在配置上,拖慢了开发的进度,springboot 问世后,提倡代码优于配置解决了这个问题。
3 spring 的内容太庞大,随便打断点查看的时候会出现十几二十层代码,阅览性不强,在实际开发的过程中spring的角色更像是胶水一样,充当整合各种技术的角色,同时作为bean的容器。
2.spring****的核心技术
1 spring的核心技术有:IOC(控制反转),AOP面向切面编程
2 java 的高级特性:反射机制,代理
3 AOP:面向切面编程,系统中有很多各不相干的类的方法,在这众多方法中加入某种系统功能的代码,如加入日志,权限判断等,AOP可以实现横切关注点(如日志,安全,缓存和事务管理)与他们所影响的对象之间的解耦。
4 实现AOP 功能采用的是代理技术,调用代理类,代理类与目标类具有相同的方法声明。
5 AOP 在spring中主要表现在两个方面:提供声明式的事务管理、spring支持用户自定义切面。
6 AOP主要包括通知(Advice)切点(PointCut)连接点(JoinPoint)
Spring框架中使用到了大量的设计模式,下面列举了比较有代表性的:
代理模式—比如Spring的Aop,使用的是JDK动态代理和CGLIB动态代理,JDK动态代理是实现了接口的类生成代理,而CGLIB是针对类实现代理
单例模式—在spring配置文件中定义的bean默认为单例模式。
模板方法—用来解决代码重复的问题。比如. RedisTemplate, JmsTemplate
, JpaTemplate``。
工厂模式—BeanFactory用来创建对象的实例。
适配器–spring aop
装饰器–spring data hashmapper
观察者– spring 时间驱动模型
原理:
(1)用户发送请求至前端控制器DispatcherServlet;
(2)DispatcherServlet收到请求后,调用HandlerMapping处理器映射器请求获取Handler ;
(3)处理器映射器根据请求的url找到具体的处理器,生成处理器对象及处理器拦截器一并返回给DispatcherServlet;
(4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器;
(5)处理器适配器去执行Handler
(6)Handler执行完成返回ModelAndView;
(7)HandlerAdapter将Handler的执行结果ModelAndView返回给DispatcherServlet;
(8)DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析;
(9)ViewResolver解析后返回真正的视图View;
(10)DispatcherServlet对View进行渲染视图
(11)最后DispatcherServlet将渲染结果响应给用户。
流程:
\1. 用户发送访问请求
\2. 前端拦截器拦截请求
\3. 根据请求的地址,处理器适配器执行控制器方法
\4. 获取方法返回结果
\5. 调用视图解析器,渲染页面
\6. 前端控制器处理完毕后,返回服务器
\7. 跳转页面
\8. 响应用户请求
重要组件:
1、前端控制器DispatcherServlet(不需要程序员开发)
作用:接收请求,响应结果,相当于转发器,减少了其它组件之间的耦合度
2、处理器映射器HandlerMapping(不需要程序员开发)
作用:根据请求url查找Handler
3、处理器适配器(HandlerAdapter)(不需要程序员开发)
作用:按照特定的规则(HandlerAdapter要求的规则)去执行Handler
注意:编写Handler时按照HandlerAdapter的要求去做,这样适配器才可以执行Handler;
(Handler也就是Controller中的各个action方法,需要程序员开发)
4、视图解析器ViewResolver(不需要程序员开发)
作用:进行视图解析,根据逻辑视图名解析成真正的视图(view)
5、视图(View)(需要程序员开发)
View是一个接口,实现类支持不同的View类型(jsp,freemarker,excel,pdf等)
注解类的作用?
mybatis 注解@Insert @Update @Delete @Select @Param
spring 注解 @Component@Controller@Service @Repository:@Autowired @Value @Resource
springMVC 注解:@RequestMapping@RequestParam建立请求参数与方法形参的映射关系 @RequestBody转java对象@ResponseBody转json对象
Spring的特色是IoC容器,管理很多Bean,要求这些Bean都配置在applicationContext.xml文件中。
用了注解之后,就不需要在xml文件中配置了,Spring提供了几个辅助类会自动扫描和装配这些Bean。
所以,使用注解能大大减少xml文件的体积。
答案2:
Spring启动时会根据配置去扫描某些包里的类,得到类或方法上的注解,不同的注解会进行不同的操作,如@Component会把这个类的实例放入IoC容器内,@Autowired会把IoC容器内的对象赋值给某个变量
Spring 的事务,可以说是 Spring AOP 的一种实现。
spring的事务声明有两种方式: 一、编程式事务控制;二、声明式事务控制。
spring主要是通过“声明式事务”的方式对事务进行管理,即在配置文件中进行声明,通过AOP将事务切面切入程序,
使用环绕通知,手动控制通知执行时间点和顺序
这就是mybatis 为我们提供了的两种动态 sql 的语法,在 mapper接口 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析, 他们的执行结果都是一样的。
1 #{ }是一个参数占位符,也是一个 JDBC 预编译语句的参数标记符,数据库驱动在发送 sql 语句和参数给 数据库之前已经进行了预编译,DBMS 执行 sql 时,就不需要重新编译。能防止sql注入;另外,#{} 传入值时,sql解析出来的参数是带引号的
2 而$是拼接符,仅仅为一个纯碎的 string 替换,在SQL 解析时将会进行变量替换。,可能会引发sql注入。
相同点:
都是java中orm框架、屏蔽jdbc api的底层访问细节,使用我们不用与jdbc api打交道,就可以完成对数据库的持久化操作。jdbc api编程流程固定,还将sql语句与java代码混杂在了一起,经常需要拼凑sql语句,细节很繁琐。
ibatis的好处:屏蔽jdbc api的底层访问细节;将sql语句与java代码进行分离;提供了将结果集自动封装称为实体对象和对象的集合的功能.queryForList返回对象集合,用queryForObject返回单个对象;提供了自动将实体对象的属性传递给sql语句的参数。
Hibername的好处:Hibernate是一个全自动的orm映射工具,它可以自动生成sql语句,执行并返回java结果。
不同点:
1、hibernate要比ibatis功能强大很多。因为hibernate自动生成sql语句。
2、ibatis需要我们自己在xml配置文件中写sql语句,hibernate我们无法直接控制该语句,我们就无法去写特定的高效率的sql。对于一些不太复杂的sql查询,hibernate可以很好帮我们完成,但是,对于特别复杂的查询,hibernate就很难适应了,这时候用ibatis就是不错的选择,因为ibatis还是由我们自己写sql语句。
ibatis可以出来复杂语句,而hibernate不能。
3、ibatis要比hibernate简单的多。ibatis是面向sql的,不同考虑对象间一些复杂的映射关系。
系统角色:
dubbo框架的体系结构有5个核心组成部分,分别是
提供者provider,它的作用是为消费者提供数据。
注册中心registry,它的作用是用来注册和发现服务。
消费者consumer,它的作用是调用远程提供者提供的服务。
监控中心Monitor用来统计服务的调用次数以及调用时间,
Container服务运行容器,运行服务提供者。
调用关系:
0.服务容器负责启动,加载运行服务提供者。
1. 服务提供者在启动时,向注册中心注册自己提供的服务。
2. 服务消费者在启动时,向注册中心订阅自己所需的服务。
3. 注册中心返回 服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
4. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,
如果调用失败,再选另一台调用。
5. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
采用JDK动态代理技术或CGLib字节码生成(asm)技术。一般都采用JDK动态代理,因为代码易维护。当调用生产者类的服务时,其实调用的是代理类的方法,代理类中执行了通信的业务逻辑,并且获得最后的执行结果。
ZooKeeper是一个开放源码的分布式协调服务,它是集群的管理者,监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终,将简单易用的接口和性能高效、功能稳定的系统提供给用户。
分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、分布式协调/通知、集群管理、分布式锁和分布式队列等功能。
Zookeeper保证了如下分布式一致性特性:
· 顺序一致性
· 原子性
· 单一视图
· 可靠性
· 实时性(最终一致性)
Leader
· 事务请求的唯一调度和处理者,保证集群事务处理的顺序性
· 集群内部各服务的调度者
Follower
· 处理客户端的非事务请求,转发事务请求给Leader服务器
· 参与事务请求Proposal的投票
· 参与Leader选举投票
Observer
3.3.0版本以后引入的一个服务器角色,在不影响集群事务处理能力的基础上提升集群的非事务处理能力
· 处理客户端的非事务请求,转发事务请求给Leader服务器
· 不参与任何形式的投票
dubbo是把项目实现分离,分为消费端跟服务端,在消费者这边的配置文件中添上zookeeper地址,让它连上zookeeper,每个服务端都暴露一个端口给zookeeper,这样就实现服务端在zookeeper中注册,消费者发送请求给zookeeper,zookeeper接收请求,zookeeper再分发给这些服务端。
» Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。
当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数server的完成了和leader的状态同步以后,恢复模式就结束了。
状态同步保证了leader和server具有相同的系统状态
» 一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,
发现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的followers支持。
» 广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保证。所有的提议(proposal)都在被提出的时候加上了zxid。
实现中zxid是一个64为的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。
» 当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的server都恢复到一个正确的状态。
» 每个Server启动以后都询问其它的Server它要投票给谁。
» 对于其他server的询问,server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系统启动时每个server都会推荐自己)
» 收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信息设置成下一次要投票的Server。
» 计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则改server被选为leader。否则,继续这个过程,直到leader被选举出来
» leader就会开始等待server连接
» Follower连接leader,将最大的zxid发送给leader
» Leader根据follower的zxid确定同步点
» 完成同步后通知follower 已经成为uptodate状态
» Follower收到uptodate消息后,又可以重新接受client的请求进行服务了
Zookeeper实现分布式锁是通过节点和临时顺序节点来实现的:主要有以下步骤:
1. 在构造函数里面启动的时候建立一个节点,假如命名为:lock。节点类型为持久节点(PERSISTENT)【ZooKeeper里面的znode节点会自动同步的,而且是强一致性,创建一个节点后只有ZooKeeper集群同步完成后算成功】
2. 每当进程需要访问共享资源时,会在lock节点下面建立响应的顺序子节点,节点类型为临时顺序节点(EPHEMERAL_SEQUENTIAL)
3. 在建立子节点之后,判断刚刚建立的子节点顺序号是否为最小节点,如果是最小节点,则可以获得该锁对资源进行访问。(临时子节点建立会自动生成一个序号的)
4. 如果不是该节点,就获得该节点的上一顺序节点,并给该节点是否存在注册监听事件。同时在这里阻塞。等待监听事件的发生。获得控制权(实现watch接口,并且重写process方法,在process里面实现监听)
5. 当完成之后,关闭ZooKeeper连接,进而可以应发监听事件,释放该锁(客户端关闭ZooKeeper连接之后会删除当前的临时节点)
另外为了保证高并发访问量大的情况,避免过多的连接一下子查询到数据库造成数据库的崩溃呢,我们采用了redis来实现数据的缓存,这样呢就会查询数据的时候,先从redis缓存中查询数据是否存在,如果缓存中存在我们就直接从缓存中取,这样可以减少多数据库的访问,如果缓存中不存在数据再去数据库查询,并且将查询出来的数据添加到缓存中,因为redis的查询速度是相当快的(11000次/s),另外为了保证redis服务器的安全通常会在redis.conf中绑定具体的ip地址这样只有该地址才能访问redis服务器,并且设置密码,为了保证redis不会因为占用内存过大而导致系统宕机,通常在将redis当做缓存服务器使用时,设置存储数据的过期时间,并且通过设置maxmemory【最大内存】和maxmemory-policy【数据清除策略】为allkeys-lru来达到预期的效果。我在项目中通常使用redis来充当缓存服务器来缓存分类列表,品牌列表,热销商品,推荐商品以及该商品的关联商品等等。使用了jedis作为客户端,并考虑到性能问题使用了jedis连接池。考虑到redis服务器的高可用性,我们做了redis的主从复制,并且通过加入哨兵来使redis主服务器宕机时,从服务器自动转换为主服务器继续提供服务。
1:缓存穿透的问题:
一般出现这样的问题,是因为当我们查询一条肯定不存在的数据的时候,缓存中没有,就会透过缓存来查询数据库,数据库也不存在,这样就不会将值保存在缓存中,最后还是缓存和数据库中都没有,如果一直访问这条数据。我们就对数据库进行频繁的查询,给数据库带来压力;
解决方法:当查询的时候,如果缓存和数据库中都没有,我们就将这个数据以空的形式存放在缓存中,(或者是给一个false的标示)这样就不用去数据库就可以知道不存在,减少对数据库查询的次数,当我们这个值发生改变的时候,我们在重新进行赋值;
2:并发情况:当我们大量访问都是查询一个缓存中没有的数据时,这样就会都去数据库中进行查询,可能会造成数据库的宕机;
解决方法:在查询的时候,我给他添加了一个同步锁,只有第一条数据去数据库中查并且返回到redis中后才能查询,这是数据库中已近存在了值,这样也可以避免;
3:雪崩:大量数据的访问时间失效,这样用户就会访问到数据库,第一台数据库崩溃了,访问就会到第二台数据库进行查询,这样会导致第二台的也崩溃;
解决方法,就是设置失效时间时,不要一起失效,或者是设置在访问量少的时候,或者设置为永远不失效;
redis的持久化分为两种:
RDB(redis database)
将缓存放到一个文件中,默认一段时间去存储一次
会将内容先放到缓存文件,持久化结束之后,就用缓存文件代替上一次的持久化文件
优点:会调用子进程来保持持久化,不会有数据库I/O
缺点:如果持久化的时候数据库丢失了数据,因为是’覆盖的‘所以,就找不到数据了,故适用于不太重要的数据
简单来说: rdb文件小,易备份,易恢复,恢复快
AOF(append only file)
默认每秒去存储历史命令
保存的是数据的历史指令,恢复数据的时候是将命令从前到后在执行一遍
优点:遇突发情况的话能找到以前的记录,且数据丢失较少(1s)
缺点:每次都有IO操作,对服务器压力较大
总结:回复慢,数据完整性好,不易备份
redis的一大特点就是可以将数据进行持久化,在一定程度上确保了数据的安全性,但不是绝对的;
首先持久化分为rdb(快照持久化)和aof(精细持久化);
快照持久化:
是默认开启的;会自动保存数据,当启动时会在文件夹中生成dump.rdb文件;存放持久化后的数据;
当然我们也可以设置持久化的频率,在redis.conf文件中通过save进行设置,默认有三种情况,每秒超过一万数据或每5分钟有10条数据的时候再或者每15分钟有1条记录,都会执行快照持久化,当然也可以通过bgsave的方法来手动进行一个快照持久化;(也可以通过ip和端口号就给别人进行手动持久化);如果频繁的快照持久化,会降低性能和效率,但是这样也出现了一个问题,就是当一分钟内如果有一万条数据时,不会提交,但是在下一次提交之前,停电了,这样就会丢失掉这些数据;
当时想到的解决方法就是和(AOF)精细持久化进行一个结合,达到一个秒级的持久化;这个持久化需要我们手动进行开启,(注意,AOF开启之后,之前的数据都会丢失,所以要在一开始使用时就要配置好)开启的方法就是在配置redis.conf,将appendonly 改为yes;同时还可以更改文件名称;然后重新启动服务,这时精细化持久化就启动好了;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRx2vOYN-1589877835821)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image006.gif)]
在快照持久化的同时,我们进行精细持久化,
比如,我们每隔一个小时进行一次快照持久化,这中间我们添加精细持久化;当55分的时候宕机了,我们还是可以通过RDB和AOF来恢复我们的数据,尽可能减少了损失;等到一个小时以后我们进行了快照持久化,数据就会保存在rdb的文件中,我们就可以将aof的持久化文件重新开始;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjbDBeyy-1589877835823)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image008.gif)]
关于精细持久化也存在三个配置,
always只要发生改变就会发生持久化,这样是很浪费性能的,不推荐使用(我们的CPU不用干其他工作了,就在这里看着持久化)
everysec每秒进行一次快照持久化;推荐使用这种
no 是不确定,看服务器的使用情况(心情)来定(不安全,不推荐)
我们还可以通过把rdb文件和aof文件复制到另外一个redis服务器上,就可以共享数据;
我们还可以对redis服务器配置主从同步,来减轻redis服务器的压力,
修改从服务对应的主服务地址(修改时可以直接搜索slaveof关键字)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-krlhXUPt-1589877835823)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image010.gif)]
这样访问时会读写分离,查询的时候会走从服务器,而更新操作的时候走主服务器;
与memcached的区别 (问到再说)
1,redis并不是把所有的数据都存放在内存,还可以将一些长期不使用的值存放在磁盘当中。而memcached只能存放在内存当中的;
2,redis的数据类型比memcached的数据类型丰富,除了string外,还有set,list,sortd set,hash等;
3:redis是可以持久化数据的,在一定程度上避免了数据的丢失,而memcached的安全性较低,一旦遇到停电,宕机的情况数据就会丢失;
4设置过期时间,memchched是在set key的时候直接设定时间(memcache.set(key,value,new Date(50)) 设置为50秒过期),而redis设置过期时间是通过expire来设置的;
我们的项目采用的是maven来进行管理的,可以通过maven来进行包的依赖管理,保证jar包版本的一致性,所以只需要在pom.xml里添加对应的信息即可自动下载jar包,
1:在项目中我们是通过jedis来操作redis的,jedis是java提供的用来操作redis的API
2:我们需要导入一些相关的jar包,目的是为了一些工具类的使用;
3:配置redis.xml文件,
1)我们配置了连接池;设置了最大链接数,最大空闲数;然后用静态语句块初始化连接池信息,在获得jedis实例
2)配置Jedis ConnectionFactory,通过IP和端口号来链接相对应的redis数据库;
3)配置了模板,并且在模板中引入了redistemplate的相关jar包;
4:在web.xml文件中引入了 redis.xml文件;
5:我们封装了相对应的工具类;通过操作模板来调用其中的方法,进行增删改查;在service层中获得jedis的实例,通过jedis实例来调用Redis中set类型对应的方法,就可以实现在Redis缓存中放入和取出查询到的商品的信息了,需要注意的是redis里只能存储字节,所以相对应的实体类要实现serializable这个接口;
(1) Master最好不要做任何持久化工作,如RDB内存快照和AOF日志文件
(2) 如果数据比较重要,某个Slave开启AOF备份数据,策略设置为每秒同步一次
(3) 为了主从复制的速度和连接的稳定性,Master和Slave最好在同一个局域网内
(4) 尽量避免在压力很大的主库上增加从库
(5) 主从复制不要用图状结构,用单向链表结构更为稳定,即:Master <- Slave1 <- Slave2 <- Slave3…
这样的结构方便解决单点故障问题,实现Slave对Master的替换。如果Master挂了,可以立刻启用Slave1做Master,其他不变。
还有就是Redis的秒杀场景
使用的是Redis的事务功能,先通过watch()监控一个字段,且这个字段为0,之后对于秒杀数进行判断。当监控字段小于秒杀数时在IF条件内部内开启事务通过multi,当有人抢购成功的时候这个监控字段加1,且通过Exec执行事务,事务执行成功则抢购成功,否则抢购失败。抢购成功之后将相应的用户也存储到数据库中然后进行相应的订单处理。当监控字段大于秒杀数的时候也就是抢购完毕。无法进行抢购。
缓存的策略是每次数据在增删改之后就更新,主要是为了避免修改之后缓存更新不及时造成脏数据的出现
当时项目在部署完成后,遇到一个问题,用户登录输入验证码的时候,明明验证码输入的正确,但总是提醒说验证码不正确从而不能正常登录,经过分析后发现有可能第一次请求被发送到t1上,那么放在session中的验证码就被放到了t1上,当用户输入验证码点击登录时,新发送的请求有可能被发送到t2上面,这样在进行对比时就肯定会不一致从而提示验证码输入错误,后来我就考虑使用ip_hash这种负载均衡策略来代替默认的轮询策略,虽然解决了验证码错误问题,但是在后续的测试中发现如果用户在使用过程中突然一台服务器宕机了,那么因为session在这台服务器上存储着,所以就会提示用户重新登录,这样使用户的体验度非常不好,最后就通过将session信息保存到redis服务器中从而在多台web服务器中实现session共享,这样就解决了上面所说的那些问题。
我们的购物车,用的是redis 来实现的。 当加入购物车的时候 用户id作为redis 的key,产品集合作为redis的value。商品存的是 ,商品id 商品名称,和商品购买数量。
当加入商品到购物车的时候,首先判断当前用户id对应的的产品集合里面是否含有当前产品,有则数量加一。没有则新添加该商品。
答:会变化。我的购物车用户id做为key,value是产品集合,而且默认过期时间是7天,产品集合只存储产品id ,数量,名称。 当显示该商品的时候我需要用产品id 去其他redis 库里面获取价格。其他的redis库里面已经存储了所有产品id作为key,value是商品 价格数量。当老板变动数据库价格的时候我只需要更新,单独存储的这边redis产品。
在整个项目中实现商品搜索功能电商项目中,因为用户有时候不是多么清楚他所需要的东西的名称或者商店的名称,有时候仅仅是只知道他所需要的商品是干嘛用的,又或者是有多个要求,为了满足用户的诸多需求更准确的查询到用户所需要的商品,所以就用到了Solr搜索引擎
solr是一个基于Lucene的全文搜索服务器,相比Lucene更高效、使用更便捷,在进行模糊匹配的时候,他可以 用来替代数据库中的like ,从而在匹配准确性以及性能进行大幅度的提高。因为官方的分词器对中文支持不好所以使用了第三方的IK分词器来进行分词。在建立索引的时候我们通过在schema.xml配置IK分词器来完成中文分词。从而实现了高亮显示关键词,分页,排序,多字段,多条件的高性能搜索。
其实目前有很多优秀的中文分词组件 :像mmseg4j,IK Analyzer ,Paoding
通过查询资料发现用 IKAnalyzer比较好IK 比其他中文分词维护的勤快,和 Solr 集成也相对容易。
其中我们在配IK分词器的时候,当时遇到了三个问题:
分词器版本与solr服务版本的匹配:
当时用的solr是4.10.4版本的因为这个版本比较好用,稳定,5.以上的版本bug比较多,配IK分词器的2012FF_u1版本,开始时用的是u6,然后配完效果出不来,上网查了好多资料,又问了之前加的一个搜索引擎群,才知道是版本问题,u3,u5,u6的都不好用,只有u1的和solr4.10版本的合适,改完之后问题确实解决了
加入分词器的schema文件要把version改为1.5基本的CRUD操作都可以。
但是搜索却只能全字匹配或者只能单个字符匹配出结果。这是绝对不能容忍的。定位问题接近了一天,找有经验的同事给 排查也没排查出来问题。最后我自己一点一点比对multicore文件夹下的配置文件跟F:\solr\solr-4.6.0\example\solr \collection1这个文件夹下的配置文件的配置区别。当我把schema.xml的version属性从1.1升到跟collection1下的相同文件的1.5一致之后。重启服务器,问题解决了!、
分词器的词典编码格式为UTF-8无BOM格式
在从数据中取数据生成索引的时候,因为表中的数据量比较大,防止一次取出所导致内存溢出问题,我采用了分段批量提取的方式进行,
此外我们为了提高solr搜索的性能对其进行了主从配置。
\1. 我们solr使用的是solr4.7版本
\2. 通过修改schema.xml来添加要进行索引的字段以及增加ik分词器
\3. 通过solrj将数据库中的数据生成solr中的索引文件,注:solrj是java程序调用solr服务所用的jar包。
\4. 通过在solrconfig.xml中配置requestHandler name=“/Replication”来进行主从同步的配置,在从solr中通过masterUrl指明要从哪些主solr服务器中同步数据
我们为了提高用户体验度还使用了solr的spellCheck可以用于查询输入的自动完成功能auto-complete。他是基于动态代码方式建立内容,suggestion可通过读文件方式建立内容,并有点击率排序。使用facet在查询结果上根据分类添加了count信息, 然后用户根据count信息做进一步的查询, facet的主要好处就是可以任意对搜索条件进行组合, 避免无效搜索, 改善搜索体验.
分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
答案2:什么叫分布式事务:
分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。
本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。
消息队列的主要作用是为了解决高并发访问高峰,加快响应速度。一般情况下,不用消息队列,客户端来的请求会直接写入数据库,在访问高峰期,高并发的情况下,会对数据库访问造成压力,响应发生延迟,造成数据库访问的瓶颈。使用队列后,用户的请求发给队列后会立刻返回,之后再由消息队列的消费者进程监听对应的队列,从消息队列中获取数据,异步写入数据库。消息队列的服务处理速度远快于数据库,因此用户的响应延迟可得到有效改善。
消息队列说明
消息队列中间件是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题。
实现高性能,高可用,可伸缩和最终一致性架构。是大型分布式系统不可缺少的中间件。
目前在生产环境,使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等。
消息队列应用场景
消息队列在实际应用中常用的使用场景: 步处理,应用解耦,流量削锋和消息通讯四个场景。
异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。
则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
小结:
如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。
注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,
因此用户的响应时间可能是50毫秒。所以基于此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:
传统模式的缺点:
1) 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;
2) 订单系统与库存系统耦合;
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
1:订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功,请等待物流配送。
2:库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
3:假如:在下单时库存系统不能正常使用。也不影响正常下单,
因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。
2.3. 流量削锋
流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用容易挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
可以控制活动的人数.
可以缓解短时间内高流量压垮应用;
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;
秒杀业务根据消息队列中的请求信息,再做后续处理。
2.4. 消息通讯
消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
点对点通讯:
客户端A和客户端B使用同一队列,进行消息通讯。
聊天室通讯:
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。
以上实际是消息队列的两种消息模式,点对点或发布订阅模式。
JMS(Java Messaging Service)是Java平台上有关面向消息中间件(MOM)的技术规范,它便于消息系统中的Java应用程序进行消息交换,并且通过提供标准的产生、发送、接收消息的接口简化企业应用的开发,翻译为Java消息服务。
JMS是一个用于提供消息服务的技术规范,它制定了在整个消息服务提供过程中的所有数据结构和交互流程。而MQ则是消息队列服务,是面向消息中间件(MOM)的最终实现,是真正的服务提供者;MQ的实现可以基于JMS,也可以基于其他规范或标准。
简单的来说JMS类似接口开发规范,ActiveMQ是对它的具体实现,而ActiveMQ也可以基于其他的规范,比较灵活。
1 使用众多的拦截器对url拦截,以此来管理权限。但是这么多拦截器,笔者不可能对其一一来讲,主要讲里面核心流程的两个。
2 首先,权限管理离不开登陆验证的,所以登陆验证拦截器AuthenticationProcessingFilter要讲;还有就是对访问的资源管理吧,所以资源管理拦截器AbstractSecurityInterceptor要讲;但拦截器里面的实现需要一些组件来实现,所以就有了AuthenticationManager、accessDecisionManager等组件来支撑。
3 现在先大概过一遍整个流程,用户登陆,会被AuthenticationProcessingFilter拦截,调用AuthenticationManager的实现,而且AuthenticationManager会调用ProviderManager来获取用户验证信息(不同的Provider调用的服务不同,因为这些信息可以是在数据库上,可以是在LDAP服务器上,可以是xml配置文件上等),如果验证通过后会将用户的权限信息封装一个User放到spring的全局缓存SecurityContextHolder中,以备后面访问资源时使用。
4 访问资源(即授权管理),访问url时,会通过AbstractSecurityInterceptor拦截器拦截,其中会调用FilterInvocationSecurityMetadataSource的方法来获取被拦截url所需的全部权限,在调用授权管理器AccessDecisionManager,这个授权管理器会通过spring的全局缓存SecurityContextHolder获取用户的权限信息,还会获取被拦截的url和被拦截url所需的全部权限,然后根据所配的策略(有:一票决定,一票否定,少数服从多数等),如果权限足够,则返回,权限不够则报错并调用权限不足页面。
九种对象简介:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rbCqCtdE-1589877835825)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image014.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6kI2Zldu-1589877835827)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image016.jpg)]
答案2:
JavaWeb的四大作用域为:PageContext,ServletRequest,HttpSession,ServletContext;
PageContext****域:作用范围是整个JSP页面,是四大作用域中最小的一个;生命周期是当对JSP的请求时开始,当响应结束时销毁。
ServletRequest****域:作用范围是整个请求链(请求转发也存在);生命周期是在service方法调用前由服务器创建,传入service方法。整个请求结束,request生命结束。
HttpSession****域:作用范围是一次会话。生命周期是在第一次调用request.getSession()方法时,服务器会检查是否已经有对应的session,如果没有就在内存中创建一个session并返回。当一段时间内session没有被使用(默认为30分钟),则服务器会销毁该session。如果服务器非正常关闭(强行关闭),没有到期的session也会跟着销毁。如果调用session提供的invalidate() ,可以立即销毁session。
注意:服务器正常关闭,再启动,Session对象会进行钝化和活化操作。同时如果服务器钝化的时间在session 默认销毁时间之内,则活化后session还是存在的。否则Session不存在。 如果JavaBean 数据在session钝化时,没有实现Serializable 则当Session活化时,会消失。
ServletContext****域:作用范围是整个Web应用。当Web应用被加载进容器时创建代表整个web应用的ServletContext对象,当服务器关闭或Web应用被移除时,ServletContext对象跟着销毁。
作用域从小到大为:PageContext(jsp页面),ServletRequest(一次请求),HttpSession(一次会话),ServletContext(整个web应用)。
以前的模式是 所有的代码在同一个工程中 部署在同一个服务器中 同一个项目的不同模块不同功能互相抢占资源
微服务 将工程根据不同的业务规则拆分成微服务 微服务部署在不同的机器上 服务之间进行相互调用
Java微服务的框架有 dubbo(只能用来做微服务),spring cloud(提供了服务的发现,断路器等)
简单的说,Spring Data JPA是Spring基于ORM框架、JPA规范的基础上封装的一套JPA应用框架,该框架有如下几个优点:
1、提供统一的接口,可避免我们再次重复编写基础的DAO类;
Spring Data JPA提供了几个基础的接口类,分别为CrudRepository、PagingAndSortingRepository、 Repository,其中,CrudRepository提供了基础的增删查改方法,PagingAndSortingRepository则在CrudRepository的基础上提供了对数据查询的分页支持,Repository建立在PagingAndSortingRepository的基础上,提供了完善的接口方法。
2、遵循JPA规范,同时也提供了灵活的数据访问方式;
Spring Data JPA能够很好的兼容目前JPA2.0规范,在定义Entity类的时候,可以在类中定义标准的@NamedQuery查询,也可以在Spring Data JPA的Repository类的方法中定义@Query查询。
3、通过方法名即可自动生成HQL语句;
如果你使用过Spring Data JPA,那么一定会对其提供的方法名自动转换为HQL感到十分有意思,这也是Spring Data JPA让人觉得很方便的地方,下面给两个转换规则的例子:
Keyword | Sample | JPQL snippet
And | findByLastnameAndFirstname |… where x.lastname = ?1 and x.firstname = ?2
4、通过接口自动注入实现类,实现非常简单。
我们只需要定义一个类似DAO的接口类,并继承Spring Data JPA提供的Repository接口类,一切就大功告成了,不需要编写任何一行代码,你的DAO接口类就具备了增删查改、分页、记录数统计等功能。
Spring**:**
当我们使用Spring MVC时,我们需要配置组件扫描,调度程序servlet,视图解析器,Web jar(用于提供静态内容)等。Spring开发有非常头疼的三点: 以启动一个带ssm为例。
\1. 依赖太多了,而且要注意版本兼容。这个应用,要添加10-20个依赖,Spring相关的包10多个,然后是Hibernate包,Spring与Hibernate整合包,日志包,json包一堆,而且要注意版本兼容性。
\2. 配置太多了,要配置注解驱动,要配置数据库连接池,要配置Hibernate,要配置事务管理器,要配置Spring MVC的资源映射,要在web.xml中配置启动Spring和Spring MVC等
3.部署和运行麻烦。要部署到tomcat里面。不能直接用java命令运行。
太多重复和大家都一样的配置了。
Spring Boot**:**
Spring Boot的哲学就是约定大于配置。既然很多东西都是一样的,为什么还要去配置。
\1. 通过starter和依赖管理解决依赖问题。
\2. 通过自动配置,解决配置复杂问题。
\3. 通过内嵌web容器,由应用启动tomcat,而不是tomcat启动应用,来解决部署运行问题。
SpringBoot是Spring推出用于解决传统框架配置文件冗余,装配组件繁杂的基于Maven的解决方案,旨在快速搭建单个微服务
而SpringCloud专注于解决各个微服务之间的协调与配置,服务之间的通信,熔断,负载均衡等
技术维度并相同,并且SpringCloud是依赖于SpringBoot的,而SpringBoot并不是依赖与SpringCloud,甚至还可以和Dubbo进行优秀的整合开发
总结:
SpringBoot专注于快速方便的开发单个个体的微服务
SpringCloud是关注全局的微服务协调整理治理框架,整合并管理各个微服务,为各个微服务之间提供,配置管理,服务发现,断路器,路由,事件总线等集成服务
SpringBoot不依赖于SpringCloud,SpringCloud依赖于SpringBoot,属于依赖关系
SpringBoot专注于快速,方便的开发单个的微服务个体,SpringCloud关注全局的服务治理框架
SpringCloud和Dubbo都是现在主流的微服务架构
SpringCloud是Apache旗下的Spring体系下的微服务解决方案
Dubbo是阿里系的分布式服务治理框架
从技术维度上,其实SpringCloud远远的超过Dubbo,Dubbo本身只是实现了服务治理,而SpringCloud现在以及有21个子项目以后还会更多
所以其实很多人都会说Dubbo和SpringCloud是不公平的,但是由于RPC以及注册中心元数据等原因,在技术选型的时候我们只能二者选其一,所以我们常常为用他俩来对比
服务的调用方式Dubbo使用的是RPC远程调用,而SpringCloud使用的是 Rest API,其实更符合微服务官方的定义
服务的注册中心来看,Dubbo使用了第三方的ZooKeeper作为其底层的注册中心,实现服务的注册和发现,SpringCloud使用Spring Cloud Netflix Eureka实现注册中心,当然SpringCloud也可以使用ZooKeeper实现,但一般我们不会这样做,服务网关,Dubbo并没有本身的实现,只能通过其他第三方技术的整合,而SpringCloud有Zuul路由网关,作为路由服务器,进行消费者的请求分发,SpringCloud还支持断路器,与git完美集成分布式配置文件支持版本控制,事务总线实现配置文件的更新与服务自动装配等等一系列的微服务架构要素
目前国内的分布式系统选型主要还是Dubbo毕竟国产,而且国内工程师的技术熟练程度高,并且Dubbo在其他维度上的缺陷可以由其他第三方框架进行集成进行弥补
而SpringCloud目前是国外比较流行,当然我觉得国内的市场也会慢慢的偏向SpringCloud,就连刘军作为Dubbo重启的负责人也发表过观点,Dubbo的发展方向是积极适应SpringCloud生态,并不是起冲突
其实如果仔细阅读过微服务提出者马丁福勒的论文的话可以发现其定义的服务间通信机制就是Http Rest
RPC最主要的缺陷就是服务提供方和调用方式之间依赖太强,我们需要为每一个微服务进行接口的定义,并通过持续继承发布,需要严格的版本控制才不会出现服务提供和调用之间因为版本不同而产生的冲突
而REST是轻量级的接口,服务的提供和调用不存在代码之间的耦合,只是通过一个约定进行规范,但也有可能出现文档和接口不一致而导致的服务集成问题,但可以通过swagger工具整合,是代码和文档一体化解决,所以REST在分布式环境下比RPC更加灵活
这也是为什么当当网的DubboX在对Dubbo的增强中增加了对REST的支持的原因
SpringCloud社区活跃度远高于Dubbo,毕竟由于梁飞团队的原因导致Dubbo停止更新迭代五年,而中小型公司无法承担技术开发的成本导致Dubbo社区严重低落,而SpringCloud异军突起,迅速占领了微服务的市场,背靠Spring混的风生水起
Dubbo经过多年的积累文档相当成熟,对于微服务的架构体系各个公司也有稳定的现状
SpringBoot是Spring推出用于解决传统框架配置文件冗余,装配组件繁杂的基于Maven的解决方案,旨在快速搭建单个微服务
而SpringCloud专注于解决各个微服务之间的协调与配置,服务之间的通信,熔断,负载均衡等
技术维度并相同,并且SpringCloud是依赖于SpringBoot的,而SpringBoot并不是依赖与SpringCloud,甚至还可以和Dubbo进行优秀的整合开发
总结:
SpringBoot专注于快速方便的开发单个个体的微服务
SpringCloud是关注全局的微服务协调整理治理框架,整合并管理各个微服务,为各个微服务之间提供,配置管理,服务发现,断路器,路由,事件总线等集成服务
SpringBoot不依赖于SpringCloud,SpringCloud依赖于SpringBoot,属于依赖关系
SpringBoot专注于快速,方便的开发单个的微服务个体,SpringCloud关注全局的服务治理框架
1.ZooKeeper保证的是CP,Eureka保证的是AP
ZooKeeper在选举期间注册服务瘫痪,虽然服务最终会恢复,但是选举期间不可用的
Eureka各个节点是平等关系,只要有一台Eureka就可以保证服务可用,而查询到的数据并不是最新的
自我保护机制会导致
1 Eureka不再从注册列表移除因长时间没收到心跳而应该过期的服务
2 Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点(高可用)
3当网络稳定时,当前实例新的注册信息会被同步到其他节点中(最终一致性)
Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像ZooKeeper一样使得整个注册系统瘫痪
2.ZooKeeper有Leader和Follower角色,Eureka各个节点平等
3.ZooKeeper采用过半数存活原则,Eureka采用自我保护机制解决分区问题
4.Eureka本质上是一个工程,而ZooKeeper只是一个进程
远程过程调用(Remote Procedure Invocation): 也就是我们常说的服务的注册与发现,直接通过远程过程调用来访问别的service。
优点: 简单,常见,因为没有中间件代理,系统更简单
缺点:
只支持请求/响应的模式,不支持别的,比如通知、请求/异步响应、发布/订阅、发布/异步响应
降低了可用性,因为客户端和服务端在请求过程中必须都是可用的
二、消息
使用异步消息来做服务间通信。服务间通过消息管道来交换消息,从而通信。
优点:
把客户端和服务端解耦,更松耦合
提高可用性,因为消息中间件缓存了消息,直到消费者可以消费
支持很多通信机制比如通知、请求/异步响应、发布/订阅、发布/异步响应
缺点:
消息中间件有额外的复杂性
优点
1 每一个服务足够内聚,代码容易理解
2 开发效率提高,一个服务只做一件事
3 微服务能够被小团队单独开发
4 微服务是松耦合的,是有功能意义的服务
5 可以用不同的语言开发,面向接口编程
6 易于与第三方集成
7 微服务只是业务逻辑的代码,不会和HTML,CSS或者其他界面组合
7.1开发中,两种开发模式
前后端分离
全栈工程师
8 可以灵活搭配,连接公共库/连接独立库
缺点
1 分布式系统的负责性
2 多服务运维难度,随着服务的增加,运维的压力也在增大
3 系统部署依赖
4 服务间通信成本
5 数据一致性
6 系统集成测试
7 性能监控
服务器配置:
集群的环境,每个主机选择apahe 还是nginx,nignx的并发性好。nginx和apche区别 以及服务器的配置,例如缓存大小等
根据实际情况,可能对于图像比较多的情况,单独配置nginx服务器,作为图像服务器。在实习中使用的是七牛家的云存储单独作为图片存储,将有关车辆的上传图片全部放在上面。
数据库设计以及优化
(1)表的设计:
存储引擎:innodb还是 myisam? innodb支持事务外键,可以在崩溃时恢复(事务中redo日志实现),myisam不支持;存放数据的方式不同:myisam将表的结构frm 数据myd索引myi存在数据库目标中;innodb只在数据库目标文件中存在表的结构;索引采用B+树,myisam索引叶结点保存的是指针,指向数据,innodb存的就是数据;myisam占用空间小,在读的业务比较多的情况下采用myisam比较好。
字段的设置: 尽量使用短的字段,提高效率,建立索引也能减少资源浪费; 整型类型,比字符类型比较快;varchar 和 char不定长(节约空间)和定长(查询快)选用;索引字段:该字段进行不同的比较多,字段值不易过长。
合理选择数据的冗余:可以根据实际情况,不满足三范式:设置冗余字段,可以减少客户的处理,满足三范式,表之间的关系比较清晰,但是因为有外键什么的,多表关联可能性能降低,加大了用户的编程难度。
索引优化
(2)分库分表
针对大表,可以根据实际情况垂直分表或者水平分表。
垂直分表:对于大表中的某些字段经常使用,可以分表;
水平分表:例如月份,将不同的月份的数据存在不同的表中。
(3)MySQL集群的环境
读写分离:主要针对读操作比较多的情况下。
目的:给大型网站缓解查询压力
原理:服务器运行amobe服务,可以判断sql是写还是读操作。收到sql语句是写,服务器将写送到master mysql处理,利用mysql proxy机制然后同步到slave mysql;
当服务器是select时,服务器会根据负载均衡挑选出一个slave mysql,将select语句送到并处理。
缓存:
可以将一些不动的页面:页面静态化/部分页面静态化;
或者将一些数据存在memcache或者Redis中,不用去查表
数据一致性处理
当多个进程同时操作同一个数据,会产生资源争抢,数据一致性的问题。
高并发情况下,涉及到写操作时,不可能直接操作数据库,大并发的连接会导致mysql请求会阻塞,比如大量的insert update 请求到,会直接导致无数的行锁和表锁,甚至最后堆积很多,从来触发too many connections 错误。
web服务器 nginx和apache连接的进程有限,cpu上下文进程切换也会增加额外的开销,所以响应一定快。
这时可以采用
高并发下的数据安全,防超发,以抢票系统为例:
(1)消息队列:
将票数资源存在redis中,将请求存入消息队列(redis下的list阻塞的,可以实现消息队列,还可以实现优先消息队列点击打开链接)中,依次处理。缺点 :这样会处理比较慢,等待时间比较长。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gttE3wDg-1589877835828)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image018.jpg)]
:对于读操作是否也进入队列,这个问题根据具体的场景,像12306应该是不在队列中或者是优先排在最前面的,因为只是读,要求块。
(2)加锁
常见的锁:排它锁;乐观锁;悲观锁;
排他锁:在进行写时,禁止一切的读和写;
乐观锁:认为在写的时候,别人不在写,维护一个version号,等处理后对照version好,一致则对,否则回滚,操作不成功,
悲观锁:认为在写的时候,别人也在写。采用数据库提供的锁机制:在写操作的时(insert updata 等)myisam默认是锁表,innodb根据是否是主键,主键则行锁,否则表锁。读操作,innodb采用mvcc版本控制。
可以采用乐观锁+回滚:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3AD0I7OD-1589877835829)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image020.jpg)]
采用悲观锁:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qwOz4sEv-1589877835830)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image022.jpg)]
1**、HTML静态化**
用使用freemark实现Html静态化,效率最高、消耗最小的就是纯静态化的html页面,比如门户和信息网站 社区内的帖子、文章更新、网站首页商品分类这些更新频率很小,可以进行实时的静态化,这些信息其实大量被前台程序调用,这样避免了大量的数据库访问请求。
2**、图片服务器分离**
对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。
在应用服务器和图片服务器上,可以进行不同的配置优化,比如apache在配置ContentType的时候可以尽量少支持、尽可能少的LoadModule,保证更高的系统消耗和执行效率。
3**、数据库集群、库表散列**
大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。
在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。
上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。
我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。
sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。
4**、缓存**
缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。
架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的缓存模块,也可以使用外加的Squid模块进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。
网站程序开发方面的缓存,Linux上提供的Memory Cache是常用的缓存接口,可以在web开发中使用,比如用Java开发的时候就可以调用MemoryCache对一些数据进行缓存和通讯共享,一些大型社区使用了这样的架构。另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块,Java就更多了,.net不是很熟悉,相信也肯定有。
5**、镜像**
镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如linux上的rsync等工具。
6**、负载均衡**
一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。
对于大型网站来说,前面提到的每个方法可能都会被同时使用到,这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会。有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大。
7**、最新:CDN加速技术**
什么是CDN?
当某用户访问网站时,网站会利用全球负载均衡技术,将用户的访问指向到距离用户最近的正常工作的缓存服务器上,直接响应用户的请求。
当用户访问已经使用了CDN服务的网站时,其解析过程与传统解析方式的最大区别就在于网站的授权域名服务器不是以传统的轮询方式来响应本地DNS的解析请求,而是充分考虑用户发起请求的地点和当时网络的情况,来决定把用户的请求定向到离用户最近同时负载相对较轻的节点缓存服务器上。
通过用户定位算法和服务器健康检测算法综合后的数据,可以将用户的请求就近定向到分布在网络“边缘”的缓存服务器上,保证用户的访问能得到更及时可靠的响应。
由于大量的用户访问都由分布在网络边缘的CDN节点缓存服务器直接响应了,这就不仅提高了用户的访问质量,同时有效地降低了源服务器的负载压力。
1 值传递,就是传递基本数据类型,形参的修改是不会影响实参;
例如: 在main方法调用一个对象的方法,并把变量的值传递过去,
方法里面也初始化了这个变量的值,这个方法变量的值是不会因为传递过来的值而发生改变的,所以说
2 引用传递,就是传递引用类型,形参和实参都是指向同一个内存地址(就是同一个对象),所以对参数的修改肯定会影响到实际的对象;
例如我在main方法中, 对一个对象的属性赋值,如果其他方法也对这个对象属性赋值,对于这个参数的修改会同时影响实际的对象.
(3)String, Integer, Double等immutable的类型特殊处理,可以理解为传值,最后的操作不会修改实参对象。
map是一个键值对的存储接口,Map有两个具体的实现类是HashMap和HashTable,
hashmap是非线程安全的,hashtable是线程安全的,所以hashmap的效率高于hashtable;
hashmap允许空值空键,table不许空值空键;
关于map的底层,是通过数组加链表的形式来进行存放的,比如我们要存放一会key , value,我们会根据key的hashcode()值与数组的长度进行取余,通过余数放入到对应的数组位置上,然后通过key取值得时候,我们也是通过传过来的key的hashcode()与数组长度取余,然后就能很快的找到我们存放的位子。但是这样会存在一个问题,就是余数会有重复的情况,我们就加上一个链表。通过key取到链表之后,我们就将链表进行循环,然后相比较来查询出相对应的值;
HashMap底层就是一个数组结构,数组中的每一项又是一个链表。
当新建一个HashMap的时候,就会初始化一个数组。
Entry就是数组中的元素,每个 Entry 其实就是一个key-value对,
它持有一个指向下一个元素的引用,这就构成了链表。
HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。
HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,
当需要存储一个 Entry 对象时,会根据hash算法来决定其在数组中的存储位置,
再根据equals方法决定其在该数组位置上的链表中的存储位置;当需要取出一个Entry时,
也会根据hash算法找到其在数组中的存储位置,
再根据equals方法从该位置上的链表中取出该Entry。
默认是构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
也就是说,默认情况下,数组大小为16,那么当HashMap中元素个数超过16*0.75=12的时候,
就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,
而这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,
那么预设元素的个数能够有效的提高HashMap的性能。
在HashMap的底层存在着一个名字为table的Entry数组,在实例化HashMap的时候,会输入两个参数,一个是 int initCapacity(初始化数组大小,默认值是16),一个是float loadFactor(负载因子,默认值是0.75),
首先会根据initCapacity计算出一个大于或者等于initCapacity且为2的幂的值capacity,例如initCapacity为15,那么capacity就会为16,还会算出一个临界值threshold,也就是capacity * loadFactor
总结:当负载因子较大时,去给table数组扩容的可能性就会少,所以相对占用内存较少(空间上较少),但是每条entry链上的元素会相对较多,查询的时间也会增长(时间上较多)。反之就是,负载因子较少的时候,给table数组扩容的可能性就高,那么内存空间占用就多,但是entry链上的元素就会相对较少,查出的时间也会减少。所以才有了负载因子是时间和空间上的一种折中的说法。所以设置负载因子的时候要考虑自己追求的是时间还是空间上的少。
注意:设置initCapacity的时候,尽量设置为2的幂,这样会去掉计算比initCapactity大,且为2的幂的数的运算。
答案2:集合初始化时, 指定集合初始值大小。
说明: HashMap使用HashMap(int initialCapacity)初始化,
正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即loader factor)默认为0.75, 如果暂时无法确定初始值大小,请设置为16(即默认值)。
反例:HashMap需要放置1024个元素,由于没有设置容量初始大小,随着元素不断增加,容量7次被迫扩大,resize需要重建hash表,严重影响性能。
Servlet是单例多线程的。每个Servlet会根据请求来新建线程。也就是说你接到一个请求到Servlet,这个Servlet就会运行一个Thread。
Servlet的声明周期主要分为如下几步:
1)Web容器加载servlet并将其实例化后,servlet声明周期开始
2)容器运行期init方法进行servlet的初始化
3)请求到达是运行其service方法
4)service方法自动派遣运行与请求对应的doXXX方法(比如:doGet,doPost)等
5)当服务器决定将实例销毁的时候调用destroy方法
在web.xml中配置servlet的URL映射,就可以找到对应的业务处理代码。这样,通过配置文件中填写映射发方式,有助于servlet的更加灵活和可维护性。
Servlet体系结构是建立在Java多线程机制之上的,它的生命周期是由Web容器负责的。当客户端第一次请求某个Servlet时,Servlet容器将会根据web.xml配置文件实例化这个Servlet类。当有新的客户端请求该Servlet时,一般不会再实例化该Servlet类,也就是有多个线程在使用这个实例。Servlet容器会自动使用线程池等技术来支持系统的运行
图形表示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p5ylCPc8-1589877835831)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image024.jpg)]
(1)getParameter获取到的值只能是字符串,不可以是对象,而getAttribute获取到的值是Object类型的。
(2)通过form表单或者url来向另一个页面或者servlet传递参数的时候需要用getParameter获取值;getAttribute只能获取setAttribute的值
(3)setAttribute是应用服务器把这个对象放到该页面所对应的一块内存当中,当你的页面服务器重定向到另一个页面的时候,应用服务器会把这块内存拷贝到另一个页面对应的内存当中。通过getAttribute可以取得你存下的值,当然这种方法可以用来传对象。
(1)从地址栏区别来说
请求重定向的时候url地址栏是会发生变化的,可以跳转到站外的资源
请求转发的时候url地址栏是不会发生变化的,只能跳转站内的的资源。
(2)从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.因为它有request域存储数据
redirect:不能共享数据.
(3)从应用场景来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
(4)从效率来说
forward:高.
redirect:低.
Jsp 页面中的form 标签里的method 属性为get 时调用doGet(),为post时调用doPost()。
栈溢出,就是当递归调用的时候没有临界退出条件
批量导入大量数据或者dom4j解析大的xml文件的时候
会出堆溢出,通过分段批量提交以及sax代替dom4j
java虚拟机有一个堆,堆是运行时数据区域所有的类和数组的内存都是从堆里分配的
堆是在java虚拟机启动的时候创建的,两种内存:堆和栈,堆是java开发人员使用的
栈是jvm自己使用的,内存结构分为:堆(逻辑上连续的,物理上不是连续的,存的是
类实例,和数组),虚拟机栈(基本的数据类型,引用对象),本地方法栈(jvm栈是为
java方法的执行提供服务,而本地方法栈是为虚拟机为本地方法提供服务的),方法区(持久代)
(加载了类的基本信息,常量池的属于方法区的一部分,string就是存在常量池中),
计算器(就是来选取要执行下一条字节码的指令)
gc垃圾回收机制,动态分配内存的大小,依靠垃圾回收机制来完成对分配内存空间的回收,
从而避免了内存溢出的问题,降低了程序员工作的复杂度,采用了分代回收算法,jvm将内存分为
年轻代和年老代,年轻代就是对象刚建立不久,生命周期也很短,而年老代对象创建的比较久了
而且生命周期也很长,对于年轻代 频繁 采用复制算法 而年老代比较少采用tracing算法
jvm的内存大小和操作系统有关,一般来说是32位处理器内存为2个g,64为的处理器就不会有有限制了
操作系统会给一个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统 下为2G-3G)
java的监视和管理控制台:是我们在运行的时候监视各种jvm资源统计信息,使用与检测死锁,内存遗漏,可以连接到一个本地或远程的jvm可以用来监视线程的状态,内存的使用情况,垃圾收集等等等
jvm的调优:开启server模式,增堆的大小,以及持久代的大小,提高程序的运行效率,
设置初堆大小和最大堆的大小都为一样的值,避免了堆增长带来额外的压力,持久代也是一样
jvm中类的生命周期:加载、连接、初始化,使用,卸载
连接(校验,准备,解析)
在服务器启动的时候报内存溢出是因为方法区太小,也就相当于持久代的内存太小。
通过-XX:PermSize以及-XX:MaxPermSize来指定其大小
,可以解决这个问题。
引导类加载器:rt.Jar包
扩展类加载器:jre目录有一个ext下面的lib目录
系统类加载器:classPath的里面的东西
自定义类加载器:加载自定义类
JVM 优化主要是解决java的 GC (垃圾回收)问题。
JVM 的使用过程中各代有,年轻带主要存放,新创建对象。 年老代,年老代存放从年轻代存活的 对象。Perm(持久代)用 于存放静态文件,如今Java类、方法等。一般持久代可以设置大一点。
GC优化的目的有两个:
1、将转移到老年代的对象数量降低到最小;
2、减少full GC的执行时间;
为了达到上面的目的,一般地,你需要做的事情有:
1、减少使用全局变量和大对象;
2、调整新生代的大小到最合适;
3、设置老年代的大小为最合适;
4、选择合适的GC收集器;
【垃圾回收(GC收集器):串行收集器、并行收集器、并发收集器。
o 串行处理器:
–适用情况:数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。
–缺点:只能用于小型应用
o 并行处理器:
–适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。(例如 ERP 银行系统)
–缺点:应用响应时间可能较长
o 并发处理器:
–适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。(例如互联网网站)】
5、设置jvm堆大小 ,32bit 1.5-2G ,64bit 可以超过 2G ,新版的JDK 每个线程的堆大小在1M改变这个线程所占用的堆大小,可以生成更多的线程,一般项目里线程数不能超过5000个。
JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。
JVM是java的核心和基础,在java编译器和操作系统之间的虚拟处理器。它是一种利用软件方法实现的抽象的计算机基于下层的操作系统和硬件平台,可以在上面执行java的字节码程序。
Java语言写的源程序通过Java编译器,编译成与平台无关的‘字节码程序’(.class文件,也就是0,1二进制程序),然后在操作系统之上的Java解释器中解释执行。
java编译器只要面向JVM,生成JVM能理解的代码或字节码文件。Java源文件经编译成字节码程序,通过JVM将每一条指令翻译成不同平台机器码,通过特定平台运行。
JVM执行程序的过程 : I.加载.class文件 II.管理并分配内存 III.执行垃圾收集
JRE(java运行时环境)由JVM构造的java程序的运行环境
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bIs5khco-1589877835832)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image026.jpg)]
在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫面那些没有被任何引用的对象,并将它们添加到要回收的集合中,进行回收。
\1. 封装,隐藏内部实现,只暴露公共行为
\2. 继承,提高代码的重用性
\3. 多态,体现现实生活中相似对象的差异性
\4. 抽象,抽取现实世界中相似对象的共同点
1封装
封装给对象提供了隐藏内部特性和行为的能力。对象提供一些能被其他对象访问的方法来改
变它内部的数据。在 Java 当中,有 3 种修饰符: public, private 和 protected。每一种修饰符
给其他的位于同一个包或者不同包下面对象赋予了不同的访问权限。
下面列出了使用封装的一些好处:
1.1 通过隐藏对象的属性来保护对象内部的状态。
1.2 提高了代码的可用性和可维护性,因为对象的行为可以被单独的改变或者是扩展。
1.3 禁止对象之间的不良交互提高模块化
2继承
继承给对象提供了从基类获取字段和方法的能力。继承提供了代码的重用行,也可以在不修改类的情况下给现存的类添加新特性。
3多态
多态是编程语言给不同的底层数据类型做相同的接口展示的一种能力。一个多态类型上的操作可以应用到其他类型的值上面。
4抽象
抽象是把想法从具体的实例中分离出来的步骤,因此,要根据他们的功能而不是实现细节来创建类。 Java 支持创建只暴漏接口而不包含方法实现的抽象的类。这种抽象技术的主要目的是把类的行为和实现细节分离开。
final
修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取,不可修改。被声明为final的方法也同样只能使用,不能重载。
finally
在异常处理时提供 finally 块来执行任何清除操作。如果抛出一个异常,那么相匹配的 catch 子句就会执行,然后控制就会进入 finally 块(如果有的话)。
finalize
方法名。Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。它是在 Object 类中定义的,因此所有的类都继承了它。子类覆盖 finalize() 方法以整理系统资源或者执行其他清理工作。finalize() 方法是在垃圾收集器删除对象之前对这个对象调用的。
int 是基本数据类型,Integer是其包装类,注意是一个类。
为什么要提供包装类呢???
因为为了在各种类型间转化,通过各种方法的调用。否则 你无法直接通过变量转化。
比如,现在int要转为String
int a=0;
String result=Integer.toString(a);
在java中包装类,比较多的用途是用在于各种数据类型的转化中。
&和&&都可以用作逻辑与的运算符,&&为短路与,&不是短路与。
另外&可以做为整数的位运算符
例1:对于if(str != null&& !str.equals(“”))表达式,当str为null时,后面的表达式不会执行,所以不会出现NullPointerException如果将&&改为&,则会抛出NullPointerException异常。
例2:If(x33 &++y>0) y会增长,if(x33 && ++y>0)不会增长
备注:这道题先说两者的共同点,再说出&&和&的特殊之处,并列举一些经典的例子来表明自己理解透彻深入、实际经验丰富。
他们的区别主要存在在引用数据类型上
==为比较两侧的对象是否同一对象,是用内存地址来比较的
equals是方法,默认是用内存地址比较,重写后,主要是用来比较两侧的对象的值是否相同,和equals方法中的实现有关
==可以两侧都为null,但equals左侧的引用指向的对象不能空,不然有NullPointerException
除非需要比较两个引用指向的对象是同一对象,一般都使用equals方法进行比较。尤其是String之类的值对象,另外,常量尽量放在比较的左侧
答: 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数。
1 GET在浏览器回退时是无害的,而POST会再次提交请求。
2 GET产生的URL地址可以被Bookmark,而POST不可以。
3 GET请求会被浏览器主动cache,而POST不会,除非手动设置。
4 GET请求只能进行url编码,而POST支持多种编码方式。
5 GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
6 GET请求在URL中传送的参数是有长度限制的,而POST么有。
7 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
8 GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
9 GET参数通过URL传递,POST放在Request body中。
cookie 是 Web 服务器发送给浏览器的一块信息。浏览器会在本地文件中给每一个 Web 服务
器存储 cookie。以后浏览器在给特定的 Web 服务器发请求的时候,同时会发送所有为该服
务器存储的 cookie。下面列出了 session 和 cookie 的区别:
无论客户端浏览器做怎么样的设置,session都应该能正常工作。客户端可以选择禁用 cookie,
但是, session 仍然是能够工作的,因为客户端无法禁用服务端的 session。
MVC就是
M:Model 模型
V:View 视图
C:Controller 控制器
模型就是封装业务逻辑和数据的一个一个的模块,控制器就是调用这些模块的(java中通常是用Servlet来实现,框架的话很多是用Struts2来实现这一层),视图就主要是你看到的,比如JSP等.
当用户发出请求的时候,控制器根据请求来选择要处理的业务逻辑和要选择的数据,再返回去把结果输出到视图层,这里可能是进行重定向或转发等.
值类型(int,char,long,boolean等)都是用==判断相等性。对象引用的话,判断引用所指的对象是否是同一个。equals是Object的成员函数,有些类会覆盖(override)这个方法,用于判断对象的等价性。例如String类,两个引用所指向的String都是”abc”,但可能出现他们实际对应的对象并不是同一个(和jvm实现方式有关),因此用判断他们可能不相等,但用equals判断一定是相等的。
List,Set都是继承自Collection接口
List特点:元素有放入顺序,元素可重复
Set特点:元素无放入顺序,元素不可重复,重复元素会覆盖掉
(注意:元素虽然无放入顺序,但是元素在set中的位置是有该元素的HashCode决定的,其位置其实是固定的,加入Set 的Object必须定义equals()方法 ,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
List是对象集合,允许对象重复。
Map是键值对的集合,不允许key重复。
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因为地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续, ArrayList要移动数据,所以插入和删除操作效率比较低。
LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,所以在开辟内存空间的时候不需要等一个连续的地址,对于新增和删除操作add和remove,LinedList比较占优势。LinkedList 适用于要头尾操作或插入指定位置的场景
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对此访问的情况下选用ArrayList,当需要对数据进行多次增加删除修改时采用LinkedList。
List:有序,可重复,有索引。
|–ArrayList:底层是数组数据结构。是线程不同步的。查询元素的速度很快。但是增删元素的效率稍低。
|–LinkedList:底层是链表数据结构,是线程不同步的。查询元素的速度稍慢,但是增删速度很快。
|–Vector:底层也是数组数据结构。是线程同步的。被ArrayList替代了。查询速度,和增删的速度非常慢。效率低。
新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态
(1)生命周期的五种状态
新建(new Thread)
当创建Thread类的一个实例(对象)时,此线程进入新建状态(未被启动)。
例如:Thread t1=new Thread();
就绪(runnable)
线程已经被启动,正在等待被分配给CPU时间片,也就是说此时线程正在就绪队列中排队等候得到CPU资源。例如:t1.start();
运行(running)
线程获得CPU资源正在执行任务(run()方法),此时除非此线程自动放弃CPU资源或者有优先级更高的线程进入,线程将一直运行到结束。
死亡(dead)
当线程执行完毕或被其它线程杀死,线程就进入死亡状态,这时线程不可能再进入就绪状态等待执行。
自然终止:正常运行run()方法后终止
异常终止:调用stop()方法让一个线程终止运行
堵塞(blocked)
由于某种原因导致正在运行的线程让出CPU并暂停自己的执行,即进入堵塞状态。
正在睡眠:用sleep(long t) 方法可使线程进入睡眠方式。一个睡眠着的线程在指定的时间过去可进入就绪状态。
正在等待:调用wait()方法。(调用motify()方法回到就绪状态)
被另一个线程所阻塞:调用suspend()方法。(调用resume()方法恢复)
线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。程序员可以通过它进行多处理器编程,你可以使用多线程对 运算密集型任务提速。比如,如果一个线程完成一个任务要100毫秒,那么用十个线程完成改任务只需10毫秒。Java在语言层面对多线程提供了卓越的支 持,它也是一个很好的卖点。
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务。不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间。别把它和栈内存搞混,每个线程都拥有单独的栈内存用来存储本地数据。
在语言层面有两种方式。java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以你可以继承 java.lang.Thread 类或者直接调用Runnable接口来重写run()方法实现线程。
这个问题是上题的后续,大家都知道我们可以通过继承Thread类或者调用Runnable接口来实现线程,问题是,那个方法更好呢?什么情况下使 用它?这个问题很容易回答,如果你知道Java不支持类的多重继承,但允许你调用多个接口。所以如果你要继承其他类,当然是调用Runnable接口好 了。
这个问题经常被问到,但还是能从此区分出面试者对Java线程模型的理解程度。start()方法被用来启动新创建的线程,而且start()内部 调用了run()方法,这和直接调用run()方法的效果不一样。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启 动,start()方法才会启动新线程。
Runnable和Callable都代表那些要在不同的线程中执行的任务。Runnable从JDK1.0开始就有了,Callable是在 JDK1.5增加的。它们的主要区别是Callable的 call() 方法可以返回值和抛出异常,而Runnable的run()方法没有这些功能。Callable可以返回装载有计算结果的Future对象。
Java提供了很丰富的API但没有为停止线程提供API。JDK 1.0本来有一些像stop(), suspend() 和 resume()的控制方法但是由于潜在的死锁威胁因此在后续的JDK版本中他们被弃用了,之后Java API的设计者就没有提供一个兼容且线程安全的方法来停止一个线程。当run() 或者 call() 方法执行完的时候线程会自动结束,如果要手动结束一个线程,你可以用volatile 布尔变量来退出run()方法的循环或者是取消任务来中断线程。
这是我在一次面试中遇到的一个很刁钻的Java面试题, 简单的说,如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中 断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来 查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法 进行处理。
为什么把这个问题归类在多线程和并发面试题里?因为栈是一块和线程紧密相关的内存区域。每个线程都有自己的栈内存,用于存储本地变量,方法参数和栈 调用,一个线程中存储的变量对其它线程是不可见的。而堆是所有线程共享的一片公用内存区域。对象都在堆里创建,为了提升效率线程会从堆中弄一个缓存到自己 的栈,如果多个线程使用该变量就可能引发问题,这时volatile 变量就可以发挥作用了,它要求线程从主存中读取变量的值。
创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时 候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池(一个适合很多生存期短 的任务的程序的可扩展线程池)。
死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。这是一个严重的问题,因为死锁会让你的程序挂起无法完成任务,死锁的发生必须满足以下四个条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁最简单的方法就是阻止循环等待条件,将系统中所有的资源设置标志位、排序,规定所有的进程申请资源必须以一定的顺序(升序或降序)做操作来避免死锁。
这是上题的扩展,活锁和死锁类似,不同之处在于处于活锁的线程或进程的状态是不断改变的,活锁可以认为是一种特殊的饥饿。一个现实的活锁例子是两个 人在狭小的走廊碰到,两个人都试着避让对方好让彼此通过,但是因为避让的方向都一样导致最后谁都不能通过走廊。简单的说就是,活锁和死锁的主要区别是前者 进程的状态可以改变但是却不能继续执行。
我一直不知道我们竟然可以检测一个线程是否拥有锁,直到我参加了一次电话面试。在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。
对于不同的操作系统,有多种方法来获得Java进程的线程堆栈。当你获取线程堆栈时,JVM会把所有线程的状态存到日志文件或者输出到控制台。在 Windows你可以使用Ctrl + Break组合键来获取线程堆栈,Linux下用kill -3命令。你也可以用jstack这个工具来获取,它对线程id进行操作,你可以用jps这个工具找到id。
阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()方法就是一直等待客户端连接。这里的阻塞是 指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。
你可以很肯定的给出回答,Swing不是线程安全的,但是你应该解释这么回答的原因即便面试官没有问你为什么。当我们说swing不是线程安全的常 常提到它的组件,这些组件不能在多线程中进行修改,所有对GUI组件的更新都要在AWT线程中完成,而Swing提供了同步和异步两种回调方法来进行更新。
这个问题就像是如何强制进行Java垃圾回收,目前还没有觉得方法,虽然你可以使用System.gc()来进行垃圾回收,但是不保证能成功。在Java里面没有办法强制启动一个线程,它是被线程调度器控制着且Java没有公布相关的API。
Java程序中wait 和 sleep都会造成某种形式的暂停,它们可以满足不同的需要。wait()方法用于线程间通信,如果等待条件为真且其它线程被唤醒时它会释放锁,而 sleep()方法仅仅释放CPU资源或者让当前线程停止执行一段时间,但不会释放锁。
如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。
这个问题有值得一提的地方,就是线程安全也是有几个级别的:
(1)不可变
像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用
(2)绝对线程安全
不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet
(3)相对线程安全
相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。
(4)线程非安全
ArrayList、LinkedList、HashMap等都是线程非安全的类
ArrayList在添加一个元素的时候,它可能会有两步来完成:
\1. 在 Items[Size] 的位置存放此元素;
\2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。 那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
老生常谈的问题了,首先要说的是单例模式的线程安全意味着:某个类的实例在多线程环境下只会被创建一次出来。单例模式有很多种的写法,我总结一下:
(1)饿汉式单例模式的写法:线程安全
(2)懒汉式单例模式的写法:非线程安全
(3)双检锁单例模式的写法:线程安全
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的
避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。
8.1 Java****中有3种实现多线程的方式:
一是直接继承Thread类,二是实现Runnable接口。线程还有一种Java 5以后创建线程还有第三种方式:实现Callable接口,该接口中的call方法可以在线程执行结束时产生一个返回值
8.2 Thread**、Runnable、Callable接口实现多线程的区别**
观察以上三种实现方式和输出的结果可得
1.继承Thread方式,每次new Thread都是独立的,资源不共享,而Runnable资源共享;
2.实现Callable接口方式,只能运行一次FutureTask
Thread****类与Runnable接口实现多线程的区别
1.Thread类是Runnable接口的子类,使用runnable接口实现多线程可以避免单继承局限;
2.Runnable接口实现的多线程可以比Thread类实现的多线程更加清楚的描述数据共享的概念。
如何理解:
因为一个线程只能启动一次,通过Thread实现线程时,线程和线程所要执行的任务是捆绑在一起的。也就使得一个任务只能启动一个线程,不同的线程执行的任务是不相同的,所以没有必要,也不能让两个线程共享彼此任务中的资源。
通过Runnable方式实现的线程,实际是开辟一个线程,将任务传递进去,由此线程执行。可以实例化多个 Thread对象,将同一任务传递进去,也就是一个任务可以启动多个线程来执行它。这些线程执行的是同一个任务,所以他们的资源是共享。
Calleble****接口
1.最大的特点就是可以通过FutureTask获得线程核心处理方法的返回值(run方法是无返回值的
2.get方法会阻塞主线程来等待任务完成。FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果
FutureTask被多次调用,依然只会执行一次Runnable任务
方法重载是指同一个类中的多个方法具有相同的名字,但这些方法具有不同的参数列表,即参数的数量或参数类型不能完全相同
方法重写是存在子父类之间的,子类定义的方法与父类中的方法具有相同的方法名字,相同的参数表和相同的返回类型
override(重写)
\1. 方法名、参数、返回值相同。
\2. 子类方法不能缩小父类方法的访问权限。
\3. 子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
\4. 存在于父类和子类之间。
\5. 方法被定义为final不能被重写。
overload(重载)
\1. 参数类型、个数、顺序至少有一个不相同。
\2. 不能重载只有返回值不同的方法名。
\3. 存在于父类和子类、同类中。
另有时间可添加(略)
重载规则:必须具有不同的参数列表; 可以有不同的返回类型;可以有不同的访问修饰符;可以抛出不同的异常。
重写规则:参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载;返回类型必须一直与被重写的方法相同,否则不能称其为重写而是重载;访问修饰符的限制一定要大于等于被重写方法的访问修饰符;重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常,譬如父类方法声明了一个检查异常 IOException,在重写这个方法时就不能抛出 Exception,只能抛出 IOException 的子类异常,可以抛出非检查异常。
重载与重写是 Java 多态性的不同表现。
重写是父类与子类之间多态性的表现,在运行时起作用(动态多态性,譬如实现动态绑定)
而重载是一个类中多态性的表现,在编译时起作用(静态多态性,譬如实现静态绑定)。
重载和重写的区别
接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的,
另外,实现接口的一定要实现接口里定义的所有方法,而实现抽象类可以有选择地重写需要用到的方法,一般的应用里,最顶级的是接口,然后是抽象类实现接口,最后才到具体类实现。
还有,接口可以实现多重继承,而一个类只能继承一个超类,但可以通过继承多个接口实现多重继承,接口还有标识(里面没有任何方法,如Remote接口)和数据共享(里面的变量全是常量)的作用。
Java反射机制主要提供了以下功能:在运行时构造一个类的对象;判断一个类所具有的成员变量和方法;调用一个对象的方法;生成动态代理。反射最大的应用就是框架
Java****反射的主要功能:
确定一个对象的类
取出类的modifiers,数据成员,方法,构造器,和超类.
找出某个接口里定义的常量和方法说明.
创建一个类实例,这个实例在运行时刻才有名字(运行时间才生成的对象).
取得和设定对象数据成员的值,如果数据成员名是运行时刻确定的也能做到.
在运行时刻调用动态对象的方法.
创建数组,数组大小和类型在运行时刻才确定,也能更改数组成员的值.
反射的应用很多,很多框架都有用到
spring 的 ioc/di 也是反射….
javaBean和jsp之间调用也是反射….
struts的 FormBean 和页面之间…也是通过反射调用….
JDBC 的 classForName()也是反射……
hibernate的 find(Class clazz) 也是反射….
反射还有一个不得不说的问题,就是性能问题,大量使用反射系统性能大打折扣。怎么使用使你的系统达到最优就看你系统架构和综合使用问题啦,这里就不多说了。
登陆、权限拦截、日志处理,以及各种Java框架,如Spring,Hibernate,JUnit 提到注解就不能不说反射,Java自定义注解是通过运行时靠反射获取注解。实际开发中,例如我们要获取某个方法的调用日志,可以通过AOP(动态代理机制)给方法添加切面,通过反射来获取方法包含的注解,如果包含日志注解,就进行日志记录。
总体来说设计模式分为三大类:
创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
工厂模式适合:凡是出现了大量的产品需要创建,并且具有共同的接口时,可以通过工厂方法模式进行创建。
单例对象(Singleton)是一种常用的设计模式。在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在。这样的模式有几个好处:
1、某些类创建比较频繁,对于一些大型的对象,这是一笔很大的系统开销。
2、省去了new操作符,降低了系统内存的使用频率,减轻GC压力。
3、有些类如交易所的核心交易引擎,控制着交易流程,如果该类可以创建多个的话,系统完全乱了。
动态代理
一个是真正的你要访问的对象(目标类),一个是代理对象,真正对象与代理
对象实现同一个接口,先访问代理类再访问真正要访问的对象。
代理模式分为静态代理、动态代理。
静态代理是由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。
动态代理是在实现阶段不用关心代理类,而在运行阶段才指定哪一个对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LOMgAh4z-1589877835834)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image028.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qpWhk5ps-1589877835834)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image030.jpg)]
意图:
将一个类的接口转换成客户希望的另外一个接口。Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用性:
你想使用一个已经存在的类,而它的接口不符合你的需求。
你想创建一个可以复用的类,该类可以与其他不相关的类或不可预见的类(即那些接口可能不一定兼容的类)协同工作。
(仅适用于对象Adapter )你想使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它的父类接口。
请勾选private,protected,public和default的作用域(笔试)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BOnRG3q7-1589877835835)(file:///C:\Users\BERNARD\AppData\Local\Temp\msohtmlclip1\01\clip_image032.jpg)]
数组的元素在内存中是连续存放的,我们可以通过下标快速访问到某个元素,而如果要对数组进行增加或者删除元素,就要移动大量的元素,插入删除的效率低。并且数组的大小是固定的,不能动态扩展。可能浪费内存。
链表的元素在内存中是不连续的,由结点组成,每个结点都包含一个指针域(存储下一个结点的指针)一个数据域。访问链表元素需要从第一个结点开始,遍历找到该结点元素,查找效率低,但是增加和删除元素就很方便,只需要修改结点指针的指向,链表内存利用率很高,大小不固定,扩展很灵活。
hashmap不安全:主要是addEntry()函数,多个线程都可以得到当前哈希表位置的头结点,并修改头结点。这样别的线程就会覆盖之前线程的操作。
hashtable安全:是加了锁的,synchronized。
两个都是map容器,不同的是key的结构不同。
map的key是红黑树结构(二叉平衡排序树),哈希map的key是哈希表的结构。Hash_map检索出来的元素是无序的,map用迭代器遍历出来的元素是有序的。树结构的查找是O(nlog2n),哈希表结构的查找很快,是O(1)。但是树结构比较有利于插入和删除,所以删除和插入比较多的时候,用map比较好;数据量很大,查询频繁的时候用哈希map比较好。
应用层,表示层,会话层,传输层,网络层,数据链路层,物理层,他们都是协议模型,他们的应用场景,有HTTP,FTP,TCP,UDP等协议。
\1. Oracle是大型数据库而Mysql是中小型数据库,Oracle市场占有率达40%,Mysql只有20%左右,同时Mysql是开源的而Oracle价格非常高。
\2. Oracle支持大并发,大访问量,是OLTP最好的工具。
\3. 安装所用的空间差别也是很大的,Mysql安装完后才152M而Oracle有3G左右,且使用的时候Oracle占用特别大的内存空间和其他机器性能。
–****》
4.Oracle****也Mysql操作上的区别
\1. MYSQL有自动增长的数据类型,插入记录时不用操作此字段,会自动获得数据值。ORACLE没有自动增长的数据类型,需要建立一个自动增长的序列号
2.单引号处理:MYSQL里可以用双引号包起字符串,ORACLE里只可以用单引号包起字符串。在插入和修改字符串前必须做单引号的替换:把所有出现的一个单引号替换成两个单引号。
\3. MYSQL处理翻页的SQL语句比较简单,用LIMIT 开始位置, 记录个数;PHP里还可以用SEEK定位到结果集的位置。ORACLE处理翻页的SQL语句就比较繁琐
\4. 长字符串的处理ORACLE也有它特殊的地方。INSERT和UPDATE时最大可操作的字符串长度小于等于4000个单字节, 如果要插入更长的字符串, 请考虑字段用CLOB类型
\5. MYSQL日期字段分DATE和TIME两种,ORACLE日期字段只有DATE
\6. MYSQL的非空字段也有空的内容,ORACLE里定义了非空字段就不容许有空的内容。按MYSQL的NOT NULL来定义ORACLE表结构, 导数据的时候会产生错误。因此导数据时要对空字符进行判断,如果为NULL或空字符,需要把它改成一个空格的字符串。
\7. MYSQL里用 字段名 like ‘%字符串%’,ORACLE里也可以用 字段名 like ‘%字符串%’ 但这种方法不能使用索引, 速度不快,用字符串比较函数 instr(字段名,‘字符串’)>0 会得到更精确的查找结果。
\8. 程序和函数里,操作数据库的工作完成后请注意结果集和指针的释放。
为什么分表:一张表中的数据太多了、为什么分库:一个数据库中的表太多了、如果不分库分表的后果:检索速度慢、层次不清晰。
在这工作几年的过程中让我明白到,当遇到困难时不去退缩勇于面对才能让自己更快的成长,学会向内归因,不推卸责任和错误,积极主动承担责任和承认错误,吸取别人的优点来补足自己的缺点和不足,避免下次犯同样的错误。闲暇时喜欢上一些技术类的网站:比如开源中国、CSDN,推酷、博客园等网站,关注一些前沿的技术。因为我相信,学无止境,不进则退。
分表就是按照一定的规则把一张大表给分成多张小表,在数据库中看到的是几张不同的表,在硬盘上也是不同的文件,因此在进行增删改查操作的时候要根据同样的规则去找到具体操作的表。
分区是按照一定的规则把一张大表分成不同的区块,在数据库中看到的还是一张表,只不过在硬盘上存储的时候分成了几个不同的文件。这样在进行增删改查操作的时候就像操作一张普通的表一样简单方便。
他们都是为了处理单张表的大数据量问题,都能提高性能。
针对防sql注入,我们通常是这样做的:
首先在前台页面对用户输入信息进行js验证,对一些特殊字符进行屏蔽, 比如:or ,单引号,–,= ,还有就是限制用户名输入的长度,我们一般将其限制在6—13位。另外,对于用户的敏感信息我们进行Md5加密,还有,为了增加用户体验度和用户友好度,为了不使用户看到一些详细的异常信息我们会进行错误信息页面的定制,像404,500错误。另一个我层面讲,这样做也是为了保护我们的一些重要信息。此外,我们会给特定的人分配定定的权限,而不是给其分配管理员权限!
数据库连接池的优点运行原理:
在我们不使用数据库连接池的时候,每次访问数据库都需要创建连接,
使用完成之后需要释放关闭连接,而这样是很耗费资源的。当我们使用
数据库连接池的时候,在tomcat启动的时候就创建了指定数量的连接,
之后当我们程序使用的时候就直接从连接池里面取,而不需要创建,同理,
当我们使用完的时候也不需要关闭连接,而是将连接返回到连接池中,供其他请求继续使用。DBCP:比较稳定。C3P0: 性能比较高。
索引的优点
1.创建唯一性索引,保证数据库表中每一行数据的唯一性
2.大大加快数据的检索速度
3.减少磁盘IO(向字典一样可以直接定位)
索引的缺点
1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加
2.索引需要占用额外的物理空间
3.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度
第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。
第二范式(2NF): 首先是满足第一范式,另外包含两部分内容,一是表必须有一个主键,二是没有包含在主键中的列必须完全依赖于主键,而不是部分依赖。
第三范式(3NF): 首先满足第二范式,非主键列直接依赖于主键,消除传递依赖。
在数据库中,所谓事务是指一组逻辑操作单元即一组sql语句。当这个单元中的一部分操作失败,整个事务回滚,只有全部正确才完成提交。
事务的ACID属性
\1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,
要么都不发生。
\2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。(数据不被破坏)
\3. 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰.
\4. 持久性(Durability)
持久性是指一个事务一旦被提交,
它对数据库中数据的改变就是永久性的.
在JDBC中,
事务默认是自动提交的,
每次执行一个 SQL 语句时,如果执行成功,
就会向数据库自动提交,而不能回滚
为了让多个 SQL 语句作为一个事务执行:
(1)执行语句前调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
(2)在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
(3)在出现异常时,调用 rollback(); 方法回滚事务。
五个事务隔级别分别为:lsolation的属性值
1,default : 默认的事务隔离级别
2,read_uncommitted: 读未提交,一个事务可以操作另外一个未提交的事务,不能避免脏读,不可重复读,幻读,隔离级别最低,并发性 能最高
3,read_committed: 读已提交,一个事务不可以操作另外一个未提交的事务, 能防止脏读,不能避免不可重复读,幻读。
4,repeatable_read : 能够避免脏读,不可重复读,不能避免幻读
5,serializable: 隔离级别最高,消耗资源最低,代价最高,能够防止脏读, 不可重复读,幻读。
七个事务的传播行为
1,propagation_required 如果一个事务存在,则支持当前事务,如果不存在,则创建新的事务
2,propagation_supports 如果一个事务存在,则支持当前事务,如果不存在,则非事务的方法运行
3,propagation_mendatory 如果一个事务存在,则支持当前事务,如果存在,则抛出异常
4,propagation_requires_new 总是要开启一个新的事务,如果事务存在,将该事务挂起
5,propagation_not_supported 总是非事务方法运行,并挂起所有的事务
6,propagation_never 总是非事务方法运行,如果事务存在则抛出异常
7,propagation_nested 某一个事务存在,则运行在一个嵌套的事务中
Oracle****的存储过程:
存储过程就是封装一些sql的集合,也就是一条条的sql语句,过程的优点就是简化了SQL命令加上他是预编译的,所以他的执行效率相对较高。再者如果不调用过程的话就会和数据库发生多次交互,调用过程只需要传达一个命令多有的那些执行业务逻辑都在数据库端执行,所以它降低了网络的通信量,其次存储过程大大提高了安全性,这就是优点。
缺点就是不同的数据库对过程支持的关键字都是不一样的,所以它的移植性是非常差的,再者他的维护难度也比较大,因为他没有专业的调试和维护工具,所以维护起来比较麻烦,这就是存储过程的基本概述。
create or replace procedure 存储过程名
is
begin
null;
end;
行1: create or replace procedure 是一个SQL语句通知Oracle数据库去创建一个叫做skeleton存储过程, 如果存在就覆盖它;
行2: IS关键词表明后面将跟随一个PL/SQL体。
行3: begin关键词表明PL/SQL体的开始。
行4: null pl/sql语句表明什么事都不做,这句不能删去,因为PL/SQL体中至少需要有一句;
行5: END关键词表明PL/SQL体的结束
避免在 where 子句中对有索引的字段进行运算,这会导致索引失效,从而进行全表扫描。外键必须加索引
在 where 及 order by 涉及的列上建立索引,要尽量避免全表扫描。
在设计表时要避免表中字段出现null的情况,通常要为其设置默认值。
避免在查找时放弃使用索引而进行全表扫描。
Sql语句字段尽量大写。
SELECT语句中避免使用’*’,只查询需要返回的字段 ,这样可以减少oracle解析sql语句的时间。
数据结构有数组,栈,队列,链表
数据结构 优点 缺点
数组 插入快,如果知道下标,可以非常快的存取 查找慢,删除慢,大小固定
有序数组 比无序的数组查找快 删除和插入慢,大小固定
栈 提供后进先出方式的存取 存取其他项很慢
队列 提供先进先出方式的存取 存取其他项很慢
链表 插入快,删除快 查找慢
二叉树 查找,插入,删除都快(如果树保持平衡) 删除算法复杂
红-黑树 查找,插入,删除都快,树总是平衡的 算法复杂
2-3-4树 查找,插入,删除都快,树总是平衡的,类似的树对磁盘存储有用 算法复杂
哈希表 如果关键字已知,则存取极快,插入快 删除慢,如果不知道关键词则存取很慢,对存储空间使用不充分
堆 插入删除快,对最大数据项的存取很快 对其他数据项存取慢
图 对现实世界建模 有些算法慢且复杂
存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。
调用:
1)可以用一个命令对象来调用存储过程。
2)可以供外部程序调用,比如:java程序。
5.存储过程的优缺点?
优点:
1)存储过程是预编译过的,执行效率高。
2)存储过程的代码直接存放于数据库中,通过存储过程名直接调用,减少网络通讯。
3)安全性高,执行存储过程需要有一定权限的用户。
4)存储过程可以重复使用,可减少数据库开发人员的工作量。
缺点:移植性差
非关系型数据库的优势:
性能:NOSQL是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过SQL层的解析,所以性能非常高。
可扩展性:同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。
关系型数据库的优势:
复杂查询:可以用SQL语句方便的在一个表以及多个表之间做非常复杂的数据查询。
事务支持:使得对于安全性能很高的数据访问要求得以实现。
其他:
1.对于这两类数据库,对方的优势就是自己的弱势,反之亦然。
2.NOSQL数据库慢慢开始具备SQL数据库的一些复杂查询功能,比如MongoDB。
3.对于事务的支持也可以用一些系统级的原子操作来实现例如乐观锁之类的方法来曲线救国,比如Redis set nx。
第一范式:(确保每列保持原子性)所有字段值都是不可分解的原子值。
第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。
第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。
上表所示的用户信息遵循了第一范式的要求,这样在对用户使用城市进行分类的时候就非常方便,也提高了数据库的性能。
第二范式:(确保表中的每列都和主键相关)在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
第三范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。
比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键。
第三范式:(确保每列都和主键列直接相关,而不是间接相关) 数据表中的每一列数据都和主键直接相关,而不能间接相关。
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。
比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。
BCNF:符合3NF,并且,主属性不依赖于主属性。
若关系模式属于第二范式,且每个属性都不传递依赖于键码,则R属于BC范式。
通常BC范式的条件有多种等价的表述:每个非平凡依赖的左边必须包含键码;每个决定因素必须包含键码。
BC范式既检查非主属性,又检查主属性。当只检查非主属性时,就成了第三范式。满足BC范式的关系都必然满足第三范式。
还可以这么说:若一个关系达到了第三范式,并且它只有一个候选码,或者它的每个候选码都是单属性,则该关系自然达到BC范式。
一般,一个数据库设计符合3NF或BCNF就可以了。
第四范式:要求把同一表内的多对多关系删除。
第五范式:从最终结构重新建立原始结构。
何为索引:数据库索引,是数据库管理系统中一个排序的数据结构,索引的实现通常使用B树及其变种B+树。
在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引。
索引作用:
协助快速查询、更新数据库表中数据。
为表设置索引要付出代价的:
一是增加了数据库的存储空间
二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动)。
创建索引可以大大提高系统的性能(优点):
1.通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
2.可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
3.可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
4.在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
5.通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
增加索引也有许多不利的方面(缺点):
1.创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
2.索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
3.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
索引是建立在数据库表中的某些列的上面。在创建索引的时候,应该考虑在哪些列上可以创建索引,在哪些列上不能创建索引。
一般来说,应该在这些列上创建索引:
(1)在经常需要搜索的列上,可以加快搜索的速度;
(2)在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
(3)在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
(4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
(5)在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
(6)在经常使用在WHERE子句中的列上面创建索引,加快条件的判断速度。
对于有些列不应该创建索引:
(1)对于那些在查询中很少使用或者参考的列不应该创建索引。
这是因为,既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
(2)对于那些只有很少数据值的列也不应该增加索引。
这是因为,由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
(3)对于那些定义为text, image和bit数据类型的列不应该增加索引。
这是因为,这些列的数据量要么相当大,要么取值很少。
(4)当修改性能远远大于检索性能时,不应该创建索引。
这是因为,修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。因此,当修改性能远远大于检索性能时,不应该创建索引。
唯一、不为空、经常被查询的字段
事务是对数据库中一系列操作进行统一的回滚或者提交的操作,主要用来保证数据的完整性和一致性。
1 原子性(Atomicity):
原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
2一致性(Consistency):
事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到。
3隔离性(Isolation):
隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。
4持久性(Durability):
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
从理论上来说, 事务应该彼此完全隔离, 以避免并发事务所导致的问题,然而, 那样会对性能产生极大的影响, 因为事务必须按顺序运行, 在实际开发中, 为了提升性能, 事务会以较低的隔离级别运行, 事务的隔离级别可以通过隔离事务属性指定。
事务的并发问题
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据
2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致。
3、幻读:幻读解决了不重复读,保证了同一个事务里,查询的结果都是事务开始时的状态(一致性)。
事务的隔离级别
读未提交:另一个事务修改了数据,但尚未提交,而本事务中的SELECT会读到这些未被提交的数据脏读
不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果因此本事务先后两次读到的数据结果会不一致。
可重复读:在同一个事务里,SELECT的结果是事务开始时时间点的状态,因此,同样的SELECT操作读到的结果会是一致的。但是,会有幻读现象
串行化:最高的隔离级别,在这个隔离级别下,不会产生任何异常。并发的事务,就像事务是在一个个按照顺序执行一样
特别注意:
MySQL默认的事务隔离级别为repeatable-read,Oracle 默认使用的是READ COMMITTED ,
MySQL 支持 4 中事务隔离级别.
事务的隔离级别要得到底层数据库引擎的支持, 而不是应用程序或者框架的支持.
Oracle 支持的 2 种事务隔离级别:READ_COMMITED , SERIALIZABLE
SQL规范所规定的标准,不同的数据库具体的实现可能会有些差异
MySQL中默认事务隔离级别是“可重复读”时并不会锁住读取到的行
事务隔离级别:未提交读时,写数据只会锁住相应的行。
事务隔离级别为:可重复读时,写数据会锁住整张表。
事务隔离级别为:串行化时,读写数据都会锁住整张表。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大,鱼和熊掌不可兼得啊。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
4.事务传播行为
\1. PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
\2. PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
\3. PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
\4. PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
\5. PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
\6. PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
\7. PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
内连接 inner join
内连接 【掌握】 —拼接查询结果
内部有联系,使用内连接
格式: [inner] join ----inner可省
结果:
从左表中取出每一条记录,去右表中与所有的记录进行匹配;
匹配必须是某个条件是左表中与右表中相同,才会保留结果,否则不保留;
基本语法:
左表 [inner] join 右表
on 左表.字段 = 右表.字段;
注意事项:
1、on 表示连接条件;条件字段代表相同的业务含义
(如 stu.cid 和 cla.id)
学生stu表中的班级cid 和 班级cla表中的id
2、内连接可以没有连接条件,没有on之后的内容,
这时候系统会保留所有结果(笛卡尔集)
3、内连接可以使用where 代替 on
通常不用where,因为where没有on效率高
on指匹配到第一条成功的就结束,其他不匹配;
若没有,不进行匹配
而where会一直匹配,进行判断
4、通常使用字段别名 表别名
在查询数据的时候,不同表有同名字段,
这时候需要加上表名才能区分,
而若表名太长,使用表别名
若不想使用默认的字段名 可以通过[as] 设置字段别名
外连接 ,在外连接和右外连接
外连接 【掌握】–以某一张表为基表 进行相关查询
outer join 以某张表为主,取出里面的所有记录,然后每条与另外一张表进行连接, 不管能不能匹配上条件,最终都会保留,
能匹配,正确保留;不能匹配其他表的字段都置空null
分类:分为两种 左连接,右连接
left join 左外连接—左连接,以左表为主
right join 右外连接—右连接,以右表为主
基本语法:
左表 left/right join 右表
on 左表.字段 = 右表.字段;------条件必须有
(1) 不具有连续性,表中auto_increment最大值被删除,将不会被重用。就是说会跳号(如果设定的auto_increment_increment是1,那么下一次插入的id值将会从被删除的最大值算起,也就是被删除的最大值+1)
(2)历史数据表的主键id会与数据表的id重复,两张自增id做主键的表合并时,id会有冲突,但如果各自的id还关联了其他表,这就很不好操作。
(3) 很难处理分布式存储的数据表,尤其是需要合并表的情况下
(4) 在系统集成或割接时,如果新旧系统主键不同是数字型就会导致修改主键数据类型,这也会导致其它有外键关联的表的修改,后果同样很严重;
解决方案
自增ID的替代者UUID自增ID的替代者UUID
UUID是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。
UUID的目的,是让分布式系统中的所有元素,都能有唯一的辨识资讯,而不需要透过中央控制端来做辨识资讯的指定。如此一来,每个人都可以建立不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库建立时的名称重复问题。
Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value
时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 取模,
这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大
致均等的将哈希槽映射到不同的节点。
总结: 1对于目录操作:增加mkdir 删除rm 修改mv 查询ls 查看目录下的内容
2对文件操作: 增加: touch, 查看最后一页内容: cat ,删除: rm -rf ,修改: vi编辑器的使用
3 查看当前路径:pwd , 查看当前进程:ps , ,
查看进程: ps aux 显示所有用户通过终端和非终端启动的所有进程详细信息,杀死进程: kill -9 1114
4 清屏clear
5 打包解压 打包 tar -zcvf 解压: tar -zxvf
6 文件权限的授权: chmod a+x a.java 增加所有用户对a.java文件的执行权限
7 管道: ps aux | grep -i a 在ps aux的结果中,搜索字符串a,大小写不敏感,显示对应的行
ps -ef | grep 进程名 :查看指定进程是否启动。
8 一般启动重启停止都是 start, restart ,stop ,status ,enable开机启动。
比如启动docker systemctl start docker
chown 可以用来改文件和目录的所有者
chmod 可以用来改文件和目录的访问权限,但是不能变换所有者
chown -R root 更改当前目录的owner(就是所有者)为root,-R意思是循环遍历,如果当前目录下有子目录,同时更改那些子目录
\1. 出现此问题的原因:
在tomcat中发布了多个webapp引用,每个引用都引入了大量的外部jar包。导致tomcat内存不足。
http://blog.csdn.net/fengyie007/article/details/1780375(随便在网上找个就行)
修改过程:
\1. 查看linux系统的内存大小:
命令:free -m //数据以兆为单位返回。
硬盘:df -h
\2. 找到linux安装路径,我的是在 /home/system/Program/apache-tomcat7.0.34
找到/bin/catalina.sh文件
这里就要使用vi命令了!
首先到/home/system/Program/apache-tomcat7.0.34/bin/目录下:
#vi catalina.sh //打开catalina.sh文件
//下边的命令是在vi中执行。
:set nu //显示行号,已经显示的则不需要
/cygwin=false //查找命令:目的是查找到cygwin=false这句话所在的行,记住所在行的行号我的是103
:q //退出
#vi +103 catalina.sh //打开后会直接到目标行
//在“cygwin=false”上面加入以下行:
JAVA_OPTS="-Xms:512m -Xmx:512m"
JAVA_OPTS="$JAVA_OPTS -server -XX:PermSize=128m -XX:MaxPermSize=256m"
:wq //保存退出
说明
-Xms为512M,-Xmx最大内存为1024M。可以将Xms和Xmx设置为一样大,可以避免经常回收内存。
参数1:-Xms :JVM初始化堆内存的大小,
参数2:-Xmx:JVM堆内存的最大值,
参数3:-XX:PermSize:表示非堆区分配的内存初始值 128m
参数4:-XX:MaxPermSize:表示对非堆区分配的内存最大值 256m
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。