1)java基础:
一、集合:
(1)集合类:List和Set比较,各自的子类比较(ArrayList,Vector,LinkedList;HashSet,TreeSet);
List:有顺序,可重复,每个元素有自己的角标(索引)
|–ArrayList:底层的数据结构是数组结构,特点是:查询很快,增删稍微慢点,线程不同步
|–LinkedList:底层使用的是链表数据结构,特点是:增删很快,查询慢。
|–Vector:底层是数组数据结构,线程同步,被ArrayList代替了,现在用的只有他的枚举。
Set:无序,不可重复(存入和取出的顺序不一定一致),线程不同步。
|–HashSet:底层是哈希表数据结构。根据hashCode和equals方法来确定元素的唯一性,可用来去重。
|–TreeSet:可排序(自然循序),底层的数据结构是二叉树,也可以自己写个类实现Comparable或者Comparator接口,定义自己的比较器,将其作为参数传递给TreeSet的构造函数。
Map:存储键值对。
|–HashTable:底层是哈希表数据结构,不可存null键和null值,线程同步,效率比较低。出现于JDK1.0
|–HashMap:底层是哈希表数据结构,可存null键和null值,线程不同步,效率较高,代替了HashTable,出现于JDK 1.2
|–TreeMap:底层是二叉树数据结构,线程不同步,可以用于map集合中的键进行排序
(2)HashMap的底层实现,ConcurrentHashMap的底层实现?
HashMap实际上是一个“链表的数组”的数据结构,即数组和链表的结合体。
HashMap底层就是一个数组,数组中的每一项又是一个链表。当新建一个HashMap的时候,就会初始化一个数组。
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁ReentrantLock,在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。
一个ConcurrentHashMap里包含一个Segment数组,Segment的结构和HashMap类似,是一种数组和链表结构,一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护者一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。
(3)如何实现HashMap顺序存储:可以参考LinkedHashMap的底层实现;
Java里面有个容器LinkedHashMap,它能实现按照插入的顺序输出结果。
它的原理也是维护一张表,但它是链表,并且hashmap中维护指向链表的指针,这样可以快速定位链表中的元素进行删除。
它的时间复杂度也是O(n),空间上要比上面少些。
(4)HashTable和ConcurrentHashMap的区别;
HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。
因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。
ConcurrentHashMap使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
二、杂类:
(1)String,StringBuffer和StringBuilder的区别;
1、长度是否可变
- String 是被 final 修饰的,他的长度是不可变的,就算调用 String 的concat 方法,那也是把字符串拼接起来并重新创建一个对象,把拼接后的 String 的值赋给新创建的对象。
- StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。调用StringBuffer 的 append 方法,来改变 StringBuffer 的长度,并且,相比较于 StringBuffer,String 一旦发生长度变化,是非常耗费内存的。
- StringBuffer 与 StringBuilder 中的方法和功能完全是等价的,StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰。
2、执行效率
- 三者在执行速度方面的比较:StringBuilder (线程非安全)> StringBuffer (线程安全)> String
3、应用场景
- 如果要操作少量的数据用 = String
- 单线程操作字符串缓冲区 下操作大量数据 = StringBuilder
- 多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
(2)Java的多态表现在哪里;
对象的多态:
父类引用指向子类实例
接口引用指向实现类实例
方法的多样性:方法名相同,执行不同
方法的重写:子类中对父类继承来的方法进行完善
方法的重载:方法名相同参数列表不同
(3)接口有什么用;
1、简单、规范性:接口的命名规范可以表明要实现的业务。
2、维护、拓展性:满足开闭原则,对修改关闭,对拓展开放,系统维护更新时,只要换个类引用即可。
3、安全、严密性:接口是实现软件松耦合的重要手段,它描叙了服务,而不涉及任何具体实现细节。
(4)接口和抽象类有什么区别:
(5):自动拆箱、自动装箱:
自动装箱都是通过包装类的valueOf()方法来实现的.自动拆箱都是通过包装类对象的xxxValue()来实现的。
(6)访问修饰权限符:
private使用范围仅限本类中
protected使用范围为含继承关系的类中(子类可以使用父类)
什么都不写叫友好类,默认是本包中
public特别随意,包内包外,类内类外都可访问
2)多线程:
线程创建方式:
实现Runnable接口或者继承Thread类。接口可以多实现。
线程的生命周期:
线程被实例化,还未执行时处于new新生状态;当线程调用start()方法后,线程处于Runnable就绪状态;线程获取CPU,处理时进入Running运行状态;线程在运行状态中遇到特殊情况,进入Blocked阻塞状态,被阻塞的线程会等待合适的时机重新进入就绪状态。线程run方法执行完毕,正常结束,或者直接调用stop方法强制被终止后,处于dead死亡状态。这就是线程的生命周期。
yield():
当前线程调用yield方法,线程让步,会使线程暂停,使线程进入就绪状态。但是,只有优先级大于等于当前的线程,才会获得执行机会。
join():
线程A调用了线程B的join,A线程要等B线程执行完后,才能继续执行。
sleep(int ms):
Thread类提供的静态方法,使当前线程进入休眠状态,指定时间过了之后自动苏醒,进入可运行状态。
wait():
Object类提供的方法,使当前线程进入无休止的等待状态,需要其他的线程调用notify方法(object类提供)唤醒,注意,调用两个方法的必须是同一对象。
多线程同步:
锁:
对象锁:任意的对象都可以被当做锁来使用
类锁:把一个类当做锁,语法为:类名.class
要保证锁的是同一个对象。
Synchronized关键字:
synchronized(锁) {
//需要访问临界资源的代码段
}
ReentrantLock类:实现了Lock接口,显示加锁。
static ReentrantLock lock = new ReentrantLock();// 定义一个ReentrantLock类的对象
lock.lock();
lock.unlock();//最好将 unlock的操作放到finally块中
死锁:所有线程在等待其他线程拥有的资源,并且其他线程都不会放弃自己拥有的资源。
解决办法:只能人为干预,杀进程或者杀线程,在线程杀不掉的情况下杀进程。如果数据库里出现死锁,就数据库重启。
悲观锁:读锁,不允许其他线程进行任何操作。
乐观锁:写锁,不允许修改,但允许访问。
3)设计模式:
单例模式:
饿汉式:
public class Singleton{
private static Singleton singleton = new Singleton ();
private Singleton (){} //私有构造器
public static Singleton getInstance(){
return singletion;
}
}
懒汉式:
线程安全的单例模式:
public class Singleton{
private static Singleton singleton = null;
public static synchronized Singleton getInstance(){
if(singleton==null){
singleton = new Singleton();
}
return singleton;
}
}
4)JVM:
(1)内存:
1、寄存器:
记录当前线程所执行的字节码的行号。字节码解释器工作时改变当前线程的程序计数器,选取下一条字节码指令来工作。每个线程拥有的程序计数器是独立的。
2、栈:
线程私有,内存分配上非常高效,当线程运行完毕后,这些内存也就被自动回收。每次创建线程时会创建JVM栈。存放局部基本类型变量,非基本类型对象在栈上存放的是指向堆上的地址。所以java中基本类型变量为值传递,非基本类型变量是引用传递。
3、堆:
存放对象实例、数组值的区域。可认为所有通过new出来的对象被存放在堆中。
4、方法区:
存放了所加载的类的信息,包括类的名称、修饰符。存放了类中的静态变量,类中的final常量,类中的属性,类中的方法信息。
5、运行时常量池:
存放类中的固定信息,固定的常量、方法、属性,从方法区中分配空间。
6、本地方法堆栈:
存储每个native方法的调用状态。
(2)强引用,软引用和弱引用的区别;
JDK1.2之后,引用分为四种。
强引用(Strong Reference):类似“Object obj=new Object()”这类引用,只要引用存在,永远不会被回收。
软引用(Soft Reference):一些还有用但并非必要的对象。在内存溢出之前,列为回收范围进行第二次回收,若还没有足够内存,才抛内存溢出异常。
弱引用(Weak Reference):GC工作时,无论内存是否足够,都会清理掉。
虚引用(Phantom Reference):唯一目的就是希望能在这个对象被收集器回收时收到一个系统通知。
- servlet:
servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,主要功能在于交互式地浏览和修改数据,生成动态Web内容。
创建方式:继承HttpServlet、实现接口Servlet、继承GenericServlet类。
配置方式:1、注释。
@WebServlet(value="/hello",loadOnStartup=1)
loadOnStartup,启动的优先级,数字越小越先起作用当值为0或者大于0时,表示容器在应用启动时就加载并初始化这个servlet。
2、web.xml。
生命周期:
1、实例化。Servlet容器调用Servlet的构造器创建一个具体的Servlet对象的过程。未配置loadOnStartup时,在服务器启动时不会创建,第一次请求到达时才会创建实例。配置loadOnStartup时,根据优先级创建实例。
2、初始化。调用init()方法初始化,只执行一次。
3、就绪/服务。调用service()方法响应用户请求,每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务,调用doGet/doPost方法。
4、销毁。调用destroy()方法销毁。
5、GC回收。
特点:单实例多线程。一个servlet在第一次被访问时加载到内存实例化,不同线程访问同一个servlet共享同一个servlet。成员变量才会有线程安全的问题,局部变量是单线程的,没有线程安全问题。但是,一个servlet继承了SingleThreadModel接口,就会初始化多实例。
servlet处理中文乱码问题:
1、get请求:name=new String(name.getBytes("ISO8859-1"),"UTF-8");
2、post请求:request.setCharacterEncoding("UTF-8"); //设置请求参数的编码格式--对GET无效
3、页面显示乱码:response.setContentType("text/html;charset=UTF-8");
servlet处理路径:
重定向:
response.sendRedirect("register.html");
原理:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址----》服务器根据此请求寻找资源并发送给客户。
特点:2次跳转之间传输的信息会丢失(request范围)在客户浏览器路径栏显示的是其重定向的路径,路径显示变化。
转发:
request.setAttribute("list", list); //可带参数
request.getRequestDispatcher("LoginServlet").forward(request, response);
客户浏览器发送http请求----》web服务器接受此请求–》调用内部的一个方法在容器内部完成请求处理和转发动作----》将目标资源发送给客户。
转发的路径必须是同一个web容器下的url,其不能转向到其他的web路径上去,中间传递的是自己的容器内的request,数据不会丢失,可以传输数据。路径显示不变。
单servlet实现多个请求:
业务层写baseServlet,根据?method=add,通过方法名反射调用继承baseServlet的servlet里的业务方法。
jsp和servlet的区别和联系:
jsp:java server pages,是一种可以写java代码的动态页面。
jsp会被编译成servlet然后运行,本质上是servlet。
jsp注重于页面显示,但是servlet侧重于逻辑。
Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来,要形成动态页面,要写超多的system.out.print,而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。
Servlet中没有内置对象,Jsp中的内置对象都是必须通过HttpServletRequest对象,HttpServletResponse对象以及HttpServlet对象得到.
jsp的九大内置对象:
6)springmvc:
springmvc是基于servlet的前端控制框架,核心是ioc和aop(基于spring实现)
核心架构的具体流程步骤如下:
1、首先用户发送请求——>DispatcherServlet,前端控制器收到请求后委托给其他的解析器进行处理,作为统一访问点,全局流程控制;
2、DispatcherServlet——>HandlerMapping,HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象),通过这种策略模式,很容易添加新的映射策略;
3、DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;
4、HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);
5、ModelAndView的逻辑视图名——>ViewResolver,ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;
6、View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;
7、返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束
IOC控制反转的实现是基于spring的bean工厂,通过获取要创建的类的class全限定名称,反射创建对象
7)Proxy:
8)spring:
- spring-core:依赖注入IoC与DI的最基本实现
- spring-beans:Bean工厂与bean的装配
- spring-context:spring的context上下文即IoC容器
- spring-context-support
- spring-expression:spring表达式语言。
IOC:(inverse of control)控制反转,不需要关心对象的创建过程,交给Spring容器完成。具体的过程是,程序读取Spring 配置文件,获取需要创建的 bean 对象,通过反射机制创建对象的实例。DI:(Dependency Injection)依赖注入。
AOP: AOP(Aspect Oriented Programming),面向切面编程。将与业务无关的代码,但是又要被业务模块共同调用的代码封装起来,减少重复代码,降低代码之间的耦合度,便于维护和拓展。
1、连接点(Joinpoint):能够插入切面的一个点。
2、切点(Pointcut):特定的连接点。
3、增强/通知(Advice):包括要织入的程序代码,连接点的方位信息。Spring所提供的增强接口都是带方位名的.如BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等。
4、目标对象(Target):增强逻辑的目标类。代理的真实对象。
5、织入(Weaving):将通知(增强) 添加到目标类具体链接点上的过程。
- 编译期织入,这要求使用特殊的Java编译器.
- 类装载期织入,这要求使用特殊的类装载器.
- 动态代理织入,在运行期为目标类添加增强生成子类的方式.
6、代理(Proxy):一个类被AOP织入增强后,就产生了一个结果类。它是融合了原类和增强逻辑的代理类,能是和原类具有相同接口的类,也可能就是原类的子类。
7、切面(Aspect):将要集中解决的内容,抽取出来的代码封装的类称为切面。
Spring的 AOP底层实现原理:
1、静态织入:通过配置,编译期将切面中的内容织入到业务中。
配置/注解;
2、动态代理:
(1)jdk动态代理:对实现了接口的类进行代理。
(2)CGlib动态代理:没有接口,字节码增强技术,生成当前类的子类对象。
Spring的jdbc数据库和事务:
Spring连接数据库,jdbctemplate,数据源datasource…
事务:一个操作序列,要么都做,要么都不做。事务通常是以begin transaction开始,以commit或rollback结束。Commint表示提交,即提交事务的所有操作。
事务的传播行为(propa’gation behavior):当事务方法(method B)被另一个事务方法(method A)调用时,事务应该如何传播。
PROPAGATION_REQUIRED:若该事务A存在,事务方法(method b)在事务A中运行;若不存在,启动新事务。
PROPAGATION_SUPPORTS:表示当前方法不需要事务上下文,但若事务A存在,那么该方法会在这个事务中运行。
PROPAGATION_MANDATORY:必须在事务中运行,如果当前事务不存在,则会抛出一个异常。
PROPAGATION_REQUIRED_NEW:必须运行在它自己的事务中。一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager。
PROPAGATION_NOT_SUPPORTED:不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
PROPAGATION_NEVER:不应该运行在事务上下文中。如果当前正有一个事务在运行,则会抛出异常。
PROPAGATION_NESTED:表示如果当前已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。注意各厂商对这种传播行为的支持是有所差异的。可以参考资源管理器的文档来确认它们是否支持嵌套事务
事务的特性:(ACID)
1、原子性(ato’micity):整个事务要么全执行,要么全不执行。
2、一致性(con’sistency):事务完成时,所有的数据要保持一致状态。例如,3个用户相互转账,转账前总额是500,转账后总额还是500。
3、隔离性(iso’lation):一个事务执行时不能被其他事务所影响。
4、永久性(durability):事务一旦提交,事务的操作便永久保存在数据库中,回滚也不能更改。
事务的并发问题:
1、脏读(dirty read):一个事务读到了另一个事务未提交的数据。这个数据称为脏数据。
2、不可重复读(nonrepeatable read)(虚读):一个事务两次读取同一行数据,得到的结果不同。(另一事务修改了数据)
3、幻读(Phantom):一个事务两次查询,第二次查询结果比第一次多或者少。(另一事务插入或删除了数据)
事务的隔离级别:
1、读未提交(Read uncommitted):最低级别,以上情况均无法保证。
2、读已提交(Read committed):可避免脏读情况发生。
3、可重复读(Repeatable read):可避免脏读、不可重复读情况的发生。不可以避免虚读。
4、串行化读 (Serializable):事务只能一个一个执行,避免了脏读、不可重复读、幻读。执行效率慢,使用时慎重。
9)mybatis:
动态sql
映射:
//鉴别器
<discriminator javaType="int" column="sex">
<case value="1" resultMap="maleEmployeeMap" />
<case value="2" resultMap="femaleEmployeeMap" />
</discriminator>
延迟加载:需要关联信息时再去按需加载关联信息。这样会大大提高数据库性能,因为查询单表要比关联查询多张表速度要快。倘若只希望某些查询支持深度延迟加载的话可以在resultMap中的collection或association添加fetchType属性,配置为lazy之后是开启深度延迟,配置eager是不开启深度延迟。
mybatis缓存:
一级缓存:
指的就是sqlsession,不同的sqlSession之间的缓存数据区域(HashMap)是互相不影响的。在sqlsession中有一个数据区域,是map结构,这个区域就是一级缓存区域。一级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。value就是查询出的结果对象。一级缓存是默认使用的。
缺点:sqlSession方法结束,sqlSession就关闭,一级缓存就清空。
二级缓存:
指的就是同一个namespace下的mapper,二级缓存中,也有一个map结构,这个区域就是二级缓存区域。二级缓存中的key是由sql语句、条件、statement等信息组成一个唯一值。二级缓存中的value,就是查询出的结果对象。二级缓存需要手动开启。
缺点:因为二级缓存是mapper级别的,当一个商品的信息发送更新,所有的商品信息缓存数据都会清空。对于访问响应速度要求高,但是实时性不高的查询,可以采用二级缓存技术。
10)MQ消息中间件:
ActiveMq:8161是后台管理系统(url中访问后台管理页用此端口),61616是给java用的tcp端口
一、通信方式:
点对点(point to point):
1、每个消息只有一个消费者,一旦被消费,消息就不再在消息队列中;
2、发送者和接收者之间在时间上没有依赖性。接收者是否正在运行,它不会影响到消息被发送到队列;
3、接收者在成功接收消息之后需向队列应答成功。
Pub/Sub的特点:
1、每个消息可以有多个消费者;
2、发布者和订阅者之间有时间上的依赖性。针对某个主题(Topic)的订阅者,它必须创建一个订阅者之后,才能消费发布者的消息,而且为了消费消息,订阅者必须保持运行的状态。
3、为了缓和这样严格的时间相关性,JMS允许订阅者创建一个可持久化的订阅。这样,即使订阅者没有被激活(运行),它也能接收到发布者的消息。
二、消息的消费:
同步 :订阅者或接收者调用receive方法来接收消息。
异步 :订阅者或接收者可以注册为一个消息监听器。当消息到达之后,系统自动调用监听器的onMessage方法。在 onMessage 方法执行完毕后,消息才会被确认,此时只要在方法中抛出异常,该消息就不会被确认。
三、消息过滤:
选择器检查了传入消息的“JMS_TYPE”属性,并确定了这个属性的值是否等于某个值。如果相等,则消息被消费,如果不相等,那么消息会被忽略。
Question:
1、如果消息提交失败怎么办(消息丢失)?
Pub/Sub:默认只通知一次,收不到消息消息就会消失,因此消息如果必须要送到,要设置持久订阅。如果当时没收到消息,消息会持久化到(服务端)硬盘上,直到接收为止。
点对点:消失如果没有接收到,默认会被保存在硬盘上,直到收到。
2、如果消息发送重复怎么办?
业务端的表记录已经处理消息的 id,消息进来的时候先判断消息是不是已经被处理过,处理过就放弃,没处理就处理,处理之后记录下id。
3、ActiveMq如何调优:
(1)使用非持久化消息
(2)需要确保消息发送成功时使用事务来将消息分批组合.
4、什么是死信列队?
不能被处理的消息一直被发送、获取、退回,陷入死循环,ActiveMq在重试 6 次后,会认为这条消息是“有毒”的,就会把消息丢到死信队列里。
5、为什么不所有信息都持久化?
非持久化的消息是存在内存里的,持久化的消息是写在磁盘里的,必然导致性能的下降,因为写磁盘比较慢。
6、ActiveMQ 服务器宕机怎么办?
大量生产持久化消息直到文件达到最大限制,此时生产者阻塞,但消费者可正常连接并消费消息,等消息消费掉一部分,文件删除又腾出空间之后,生产者又可继续发送消息,服务自动恢复正常。
大量生产非持久化消息并写入临时文件,在达到最大限制时,生产者阻塞,消费者可正常连接但不能消费消息,或者原本慢速消费的消费者,消费突然停止。整个系统可连接,但是无法提供服务,就这样挂了。
结论:尽量不要用非持久化消息,非要用的话,将临时文件限制尽可能的调大。
7、持久化消息非常慢,怎么处理?
默认的情况下,非持久化的消息是异步发送的,持久化的消息是同步发送的,遇到慢一点的硬盘,发送消
息的速度是无法忍受的。但是在开启事务的情况下,消息都是异步发送的,效率会有 2 个数量级的提升。
所以在发送持久化消息时,请务必开启事务模式。其实发送非持久化消息时也建议开启事务,因为根本不
会影响性能。
8、削峰:
9、TCC分布式事务:
Try阶段:一个方法里一连串的业务相关联,现在表中添加预设字段,即给它们一个更改中ing的状态,或者需要改变的数据进行冻结,也就是 Try 阶段。
Confirm阶段:TCC 分布式事务框架感知到各个服务的 Try 阶段都成功了以后,就会执行各个服务的 Confirm 逻辑,完成各个服务的所有业务逻辑的执行。
Cancel阶段:主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。(Cancel 操作满足幂等性)
10、消息补偿机制:
定时任务
11)支付:
流程图:
业务流程说明:
(1)商户生成订单。
(2)用户确认支付后调用微信支付【统一下单API】生成预支付交易;
(3)微信支付系统收到请求后生成预支付交易单,并返回交易会话的二维码链接code_url,注意:code_url有效期为2小时,过期后扫码不能再发起支付。
(4)商户根据返回的code_url生成二维码。
(5)用户扫描二维码,微信客户端将扫码内容发送到微信支付系统。
(6)微信支付系统收到客户端请求,验证链接有效性后发起用户支付,要求用户授权。
(7)用户输入密码,确认支付后,微信客户端提交授权。
(8)微信支付系统根据用户授权完成支付交易。
(9)微信支付系统完成支付交易后给微信客户端返回交易结果,并将交易结果通过短信、微信消息提示用户。微信客户端展示支付交易结果页面。
(10)微信支付系统通过发送异步消息通知商户后台系统支付结果。商户后台系统需回复接收情况,通知微信后台系统不再发送该单的支付通知。
(11)未收到支付通知的情况,商户后台系统调用【查询订单API】。
(12)商户确认订单已支付后给用户发货。
Question:
1、当商户后台、网络、服务器等出现异常,商户系统最终未接收到支付通知,怎么办?
商户可以通过查询订单接口主动查询订单状态,完成下一步的业务逻辑。交易是否成功需要查看trade_state来判断 ,如果trade_state不为 SUCCESS,则只返回out_trade_no(必传)和attach(选传)。
2、商户订单支付失败,生成新单号重新发起支付,要对原订单号调用关单,避免重复支付;
系统下单后,用户支付超时,系统退出不再受理,避免用户继续,请调用关单接口。
注意:订单生成后不能马上调用关单接口,最短调用时间间隔为5分钟。
3、申请退款:
12)solr搜索引擎:
全量同步:首次进行全量同步,后面用增量同步。
增量同步:
增量导入的标准就是数据库中的最后更新时间update_at字段,这个时间需要和我们看到的dataimport.properties里面的last_index_time进行对比来进行增量同步。
一、data-config.xml
二、数据库设置时间字段,与solr上次更新时间比较。
三、dataimport.properties,设置定时任务。
13)redis:
(1)redis是如何持久化的:rdb和aof
RDB持久化配置
Redis会将数据集的快照dump到dump.rdb文件中。此外,我们也可以通过配置文件来修改Redis服务器dump快照的频率,在打开6379.conf文件之后,我们搜索save,可以看到下面的配置信息:
save 900 1#在900秒(15分钟)之后,如果至少有1个key发生变化,则dump内存快照。
save 300 10#在300秒(5分钟)之后,如果至少有10个key发生变化,则dump内存快照。
save 60 10000#在60秒(1分钟)之后,如果至少有10000个key发生变化,则dump内存快照。
AOF持久化配置
在Redis的配置文件中存在三种同步方式,它们分别是:
appendfsync always#每次有数据修改发生时都会写入AOF文件。
appendfsync everysec#每秒钟同步一次,该策略为AOF的缺省策略。
appendfsync no#从不同步。高效但是数据不会被持久化。
(2)redis的淘汰策略?
redis.conf中的maxmemory为0的时候表示我们对Redis的内存使用没有限制。
6种淘汰策略:
·noeviction:申请内存的命令会报错。
·allkeys-lru:在主键空间中,优先移除最近未使用的key。
·allkeys-random:在主键空间中,随机移除某个key。
·volatile-lru:在设置了过期时间的键空间中,优先移除最近未使用的key。
·volatile-random:在设置了过期时间的键空间中,随机移除某个key。
·volatile-ttl:在设置了过期时间的键空间中,具有更早过期时间的key优先移除。
配置指定:Redis也支持Runtime修改淘汰策略,我们不需要重启Redis实例而实时的调整内存淘汰策略。
#maxmemory-policy noeviction
应用场景:
·allkeys-lru:如果对缓存的访问存在相对热点数据,或者不太清楚缓存访问分布状况。
·allkeys-random:如果key的访问概率相等。
·volatile-ttl:这种策略使得我们可以向Redis提示哪些key更适合被eviction(逐出)。
volatile-lru和volatile-random:一个Redis实例既应用于缓存和又应用于持久化存储的时候,然而我们也可以通过使用两个Redis实例来达到相同的效果,将key设置过期时间实际上会消耗更多的内存,因此我们建议使用allkeys-lru策略从而更有效率的使用内存。
这里补充一下主键空间和设置了过期时间的键空间,举个例子,假设我们有一批键存储在Redis中,则有那么一个哈希表用于存储这批键及其值,如果这批键中有一部分设置了过期时间,那么这批键还会被存储到另外一个哈希表中,这个哈希表中的值对应的是键被设置的过期时间。设置了过期时间的键空间为主键空间的子集。
(3)redis有哪些数据结构;
String——字符串
Hash——字典
List——列表
Set——集合
Sorted Set——有序集合
12)优化:
(1)mysql优化经验:
1、选择正确的存储引擎
MyISAM:适合大量查询。update一个字段,整个表都会被锁起来。另外,MyISAM对于SELECT COUNT(*)这类的计算是超快无比的。不支持事务。
InnoDB:支持“行锁”,适合写操作比较多。InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比MyISAM还慢。支持事务。
2、
17)说说http,https协议;
HTTP协议的主要特点如下:
1.支持客户/服务器模式。
2.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
3.灵活:允许传输任意类型的数据对象。传输的类型由Content-Type加以标记。
4.无连接:限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
5.无状态:指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
HTTPS(Secure Hypertext Transfer Protocol)安全超文本传输协议:简单来说它是HTTP的安全版。
HTTPS和HTTP的区别:
https协议需要到ca申请证书,一般免费证书很少,需要交费。
http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
http和https使用的是完全不同的连接方式用的端口也不一样,前者是80,后者是443。
http的连接很简单,是无状态的。
HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议要比http协议安全。
22)cookie和session的区别;
1、session保存在服务器,客户端不知道其中的信息;cookie保存在客户端,服务器能够知道其中的信息。
2、session中保存的是对象,cookie中保存的是字符串。
3、session不能区分路径,同一个用户在访问一个网站期间,所有的session在任何一个地方都可以访问到。而cookie中如果设置了路径参数,那么同一个网站中不同路径下的cookie互相是访问不到的。
4、session需要借助cookie才能正常。如果客户端完全禁止cookie,session将失效。
5)volatile关键字的用法:使多线程中的变量可见;
volatile关键字,作用是强制线程去公共堆栈中访问isContinuePrint的值。
使用volatile关键字增加了实例变量在多个线程之间的可见性,但volatile关键字有一个致命的缺陷是不支持原子性
synchronized与volatile关键字之间的比较
关键字volatile是线程同步的轻量实现,所以volatile关键字性能比synchronized好。volatile只能修饰变量,synchronized可以修饰方法,代码块
volatile不会阻塞线程,synchronized会阻塞线程
volatile能保证数据的可见性,不保证原子性,synchronized可以保证原子性,可以间接保证可见性,它会将公共内存和私有内存的数据做同步处理。
volatile解决的是变量在多个线程之间的可见性,synchronized解决的是多个线程之间访问资源的同步性
请记住Java的同步机制都是围绕两点:原子性,线程之间的可见性.只有满足了这两点才能称得上是同步的。Java中的synchronized和volatile两个关键字分别执行的是原子性和线程之间的可见性
四.数据库相关(MySQL):
1)msyql优化经验:
当只要一行数据时使用LIMIT 1
为搜索字段建索引
千万不要ORDER BY RAND()
避免SELECT*
永远为每张表设置一个ID,使用数字自增
使用ENUM而不是VARCHAR
尽可能的使用NOT NULL
Prepared Statements很像存储过程,是一种运行在后台的SQL语句集合,我们可以从使用prepared statements获得很多好处,无论是性能问题还是安全问题
固定长度的表会更快
拆分大的DELETE或INSERT语句
越小的列会越快
2)mysql的语句优化;
1.对查询进行优化,应尽量避免全表扫描,首先应考虑在where及order by涉及的列上建立索引。
2.应尽量避免在where子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
3.应尽量避免在where子句中对字段进行null值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0
4.应尽量避免在where子句中使用or来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
5.下面的查询也将导致全表扫描:
select id from t where name like’%abc%’
若要提高效率,可以考虑全文检索。
6.in和not in也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用between就不要用in了:
select id from t where num between 1 and 3
7.如果在where子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名))where num=@num
8.应尽量避免在where子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=‘abc’–name以abc开头的id
select id from t where datediff(day,createdate,‘2005-11-30’)=0–'2005-11-30’生成的id
应改为:
select id from t where name like’abc%’
select id from t where createdate>='2005-11-30’and createdate<‘2005-12-1’
10.不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
12.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into#t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table#t(…)
13.很多时候用exists代替in是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
14.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
15.索引并不是越多越好,索引固然可以提高相应的select的效率,但同时也降低了insert及update的效率,因为insert或update时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
16.应尽可能的避免更新clustered索引数据列,因为clustered索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新clustered索引数据列,那么需要考虑是否应将该索引建为clustered索引。
17.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
18.尽可能的使用varchar/nvarchar代替char/nchar,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
19.任何地方都不要使用selectfrom t,用具体的字段列表代替“”,不要返回用不到的任何字段。
20.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
21.避免频繁创建和删除临时表,以减少系统表资源的消耗。
22.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
23.在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免造成大量log,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
24.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。
25.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
26.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
27.与临时表一样,游标并不是不可使用。对小型数据集使用FAST_FORWARD游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
28.在所有的存储过程和触发器的开始处设置SET NOCOUNT ON,在结束时设置SET NOCOUNT OFF。无需在执行存储过程和触发器的每个语句后向客户端发送DONE_IN_PROC消息。
29.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
30.尽量避免大事务操作,提高系统并发能力
3)mysql的索引分类:B+,hash;什么情况用什么索引;
MySQL目前主要有以下几种索引类型:FULLTEXT,HASH,BTREE,RTREE。
那么,这几种索引有什么功能和性能上的不同呢?
FULLTEXT
即为全文索引,目前只有MyISAM引擎支持。其可以在CREATE TABLE,ALTER TABLE,CREATE INDEX使用,不过目前只有CHAR、VARCHAR,TEXT列上可以创建全文索引。
值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE INDEX创建FULLTEXT索引,要比先为一张表建立FULLTEXT然后再将数据写入的速度快很多。
全文索引并不是和MyISAM一起诞生的,它的出现是为了解决WHERE name LIKE“%word%"这类针对文本的模糊查询效率较低的问题。在没有全文索引之前,这样一个查询语句是要进行遍历数据表操作的,
可见,在数据量较大时是极其的耗时的,如果没有异步IO处理,进程将被挟持,很浪费时间,当然这里不对异步IO作进一步讲解,想了解的童鞋,自行谷哥。
全文索引的使用方法并不复杂:
创建ALTER TABLE table ADD INDEXFULLINDEXUSING FULLTEXT(cname1[,cname2…]);
使用SELECT*FROM table WHERE MATCH(cname1[,cname2…])AGAINST('word’MODE);
其中,MODE为搜寻方式(IN BOOLEAN MODE,IN NATURAL LANGUAGE MODE,IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION/WITH QUERY EXPANSION)。
关于这三种搜寻方式,愚安在这里也不多做交代,简单地说,就是,布尔模式,允许word里含一些特殊字符用于标记一些具体的要求,如+表示一定要有,-表示一定没有,*表示通用匹配符,是不是想起了正则,类似吧;
自然语言模式,就是简单的单词匹配;含表达式的自然语言模式,就是先用自然语言模式处理,对返回的结果,再进行表达式匹配。
对搜索引擎稍微有点了解的同学,肯定知道分词这个概念,FULLTEXT索引也是按照分词原理建立索引的。西文中,大部分为字母文字,分词可以很方便的按照空格进行分割。
但很明显,中文不能按照这种方式进行分词。那又怎么办呢?这个向大家介绍一个mysql的中文分词插件Mysqlcft,有了它,就可以对中文进行分词,想了解的同学请移步Mysqlcft,当然还有其他的分词插件可以使用。
HASH
Hash这个词,可以说,自打我们开始码的那一天起,就开始不停地见到和使用到了。其实,hash就是一种(key=>value)形式的键值对,如数学中的函数映射,允许多个key对应相同的value,但不允许一个key对应多个value。
正是由于这个特性,hash很适合做索引,为某一列或几列建立hash索引,就会利用这一列或几列的值通过一定的算法计算出一个hash值,对应一行或几行数据(这里在概念上和函数映射有区别,不要混淆)。
在Java语言中,每个类都有自己的hashcode()方法,没有显示定义的都继承自object类,该方法使得每一个对象都是唯一的,在进行对象间equal比较,和序列化传输中起到了很重要的作用。
hash的生成方法有很多种,足可以保证hash码的唯一性,例如在MongoDB中,每一个document都有系统为其生成的唯一的objectID(包含时间戳,主机散列值,进程PID,和自增ID)也是一种hash的表现。
由于hash索引可以一次定位,不需要像树形索引那样逐层查找,因此具有极高的效率。那为什么还需要其他的树形索引呢?
在这里愚安就不自己总结了。引用下园子里其他大神的文章:来自14的路的MySQL的btree索引和hash索引的区别
(1)Hash索引仅仅能满足"=",“IN"和”<=>"查询,不能使用范围查询。
由于Hash索引比较的是进行Hash运算之后的Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的Hash算法处理之后的Hash值的大小关系,并不能保证和Hash运算前完全一样。
(2)Hash索引无法被用来避免数据的排序操作。
由于Hash索引中存放的是经过Hash计算之后的Hash值,而且Hash值的大小关系并不一定和Hash运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;
(3)Hash索引不能利用部分索引键查询。
对于组合索引,Hash索引在计算Hash值的时候是组合索引键合并后再一起计算Hash值,而不是单独计算Hash值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash索引也无法被利用。
(4)Hash索引在任何时候都不能避免表扫描。
前面已经知道,Hash索引是将索引键通过Hash运算之后,将Hash运算结果的Hash值和所对应的行指针信息存放于一个Hash表中,由于不同索引键存在相同Hash值,所以即使取满足某个Hash键值的数据的记录条数,
也无法从Hash索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。
(5)Hash索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。
对于选择性比较低的索引键,如果创建Hash索引,那么将会存在大量记录指针信息存于同一个Hash值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下。
愚安我稍作补充,讲一下HASH索引的过程,顺便解释下上面的第4,5条:
当我们为某一列或某几列建立hash索引时(目前就只有MEMORY引擎显式地支持这种索引),会在硬盘上生成类似如下的文件:
hash值存储地址
1db54bc745a1 77#45b5
4bca452157d4 76#4556,77#45cc…
…
hash值即为通过特定算法由指定列数据计算出来,磁盘地址即为所在数据行存储在硬盘上的地址(也有可能是其他存储地址,其实MEMORY会将hash表导入内存)。
这样,当我们进行WHERE age=18时,会将18通过相同的算法计算出一个hash值==>在hash表中找到对应的储存地址==>根据存储地址取得数据。
所以,每次查询时都要遍历hash表,直到找到对应的hash值,如(4),数据量大了之后,hash表也会变得庞大起来,性能下降,遍历耗时增加,如(5)。
BTREE
BTREE索引就是一种将索引值按一定的算法,存入一个树形的数据结构中,相信学过数据结构的童鞋都对当初学习二叉树这种数据结构的经历记忆犹新,反正愚安我当时为了软考可是被这玩意儿好好地折腾了一番,不过那次考试好像没怎么考这个。
如二叉树一样,每次查询都是从树的入口root开始,依次遍历node,获取leaf。
BTREE在MyISAM里的形式和Innodb稍有不同
在Innodb里,有两种形态:一是primary key形态,其leaf node里存放的是数据,而且不仅存放了索引键的数据,还存放了其他字段的数据。二是secondary index,其leaf node和普通的BTREE差不多,只是还存放了指向主键的信息.
而在MyISAM里,主键和其他的并没有太大区别。不过和Innodb不太一样的地方是在MyISAM里,leaf node里存放的不是主键的信息,而是指向数据文件里的对应数据行的信息.
RTREE
RTREE在mysql很少使用,仅支持geometry数据类型,支持该类型的存储引擎只有MyISAM、BDb、InnoDb、NDb、Archive几种。
相对于BTREE,RTREE的优势在于范围查找.
各种索引的使用情况
(1)对于BTREE这种Mysql默认的索引类型,具有普遍的适用性
(2)由于FULLTEXT对中文支持不是很好,在没有插件的情况下,最好不要使用。其实,一些小的博客应用,只需要在数据采集时,为其建立关键字列表,通过关键字索引,也是一个不错的方法,至少愚安我是经常这么做的。
(3)对于一些搜索引擎级别的应用来说,FULLTEXT同样不是一个好的处理方法,Mysql的全文索引建立的文件还是比较大的,而且效率不是很高,即便是使用了中文分词插件,对中文分词支持也只是一般。真要碰到这种问题,Apache的Lucene或许是你的选择。
(4)正是因为hash表在处理较小数据量时具有无可比拟的素的优势,所以hash索引很适合做缓存(内存数据库)。如mysql数据库的内存版本Memsql,使用量很广泛的缓存工具Mencached,NoSql数据库Redis等,都使用了hash索引这种形式。
当然,不想学习这些东西的话Mysql的MEMORY引擎也是可以满足这种需求的。
(5)至于RTREE,愚安我至今还没有使用过,它具体怎么样,我就不知道了。有RTREE使用经历的同学,到时可以交流下!
4)mysql的存储引擎有哪些,区别是什么;
MySQL5.5以后默认使用InnoDB存储引擎,其中InnoDB和BDB提供事务安全表,其它存储引擎都是非事务安全表。
若要修改默认引擎,可以修改配置文件中的default-storage-engine。可以通过:show variables like’default_storage_engine’;查看当前数据库到默认引擎。
命令:show engines和show variables like’have%'可以列出当前数据库所支持到引擎。其中Value显示为disabled的记录表示数据库支持此引擎,而在数据库启动时被禁用。
在MySQL5.1以后,INFORMATION_SCHEMA数据库中存在一个ENGINES的表,它提供的信息与show engines;语句完全一样,可以使用下面语句来查询哪些存储引擎支持事物处理:select engine from information_chema.engines where transactions=‘yes’;
可以通过engine关键字在创建或修改数据库时指定所使用到引擎。
主要存储引擎:MyISAM、InnoDB、MEMORY和MERGE介绍:
在创建表到时候通过engine=…或type=…来指定所要使用到引擎。show table status from DBname来查看指定表到引擎。
(一)MyISAM
它不支持事务,也不支持外键,尤其是访问速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表。
每个MyISAM在磁盘上存储成3个文件,其中文件名和表名都相同,但是扩展名分别为:
.frm(存储表定义)
MYD(MYData,存储数据)
MYI(MYIndex,存储索引)
数据文件和索引文件可以放置在不同的目录,平均分配IO,获取更快的速度。要指定数据文件和索引文件的路径,需要在创建表的时候通过DATA DIRECTORY和INDEX DIRECTORY语句指定,文件路径需要使用绝对路径。
每个MyISAM表都有一个标志,服务器或myisamchk程序在检查MyISAM数据表时会对这个标志进行设置。MyISAM表还有一个标志用来表明该数据表在上次使用后是不是被正常的关闭了。
如果服务器以为当机或崩溃,这个标志可以用来判断数据表是否需要检查和修复。如果想让这种检查自动进行,可以在启动服务器时使用–myisam-recover现象。这会让服务器在每次打开一个MyISAM数据表是自动检查数据表的标志并进行必要的修复处理。
MyISAM类型的表可能会损坏,可以使用CHECK TABLE语句来检查MyISAM表的健康,并用REPAIR TABLE语句修复一个损坏到MyISAM表。
MyISAM的表还支持3种不同的存储格式:
静态(固定长度)表
动态表
压缩表
其中静态表是默认的存储格式。静态表中的字段都是非变长字段,这样每个记录都是固定长度的,这种存储方式的优点是存储非常迅速,容易缓存,出现故障容易恢复;
缺点是占用的空间通常比动态表多。静态表在数据存储时会根据列定义的宽度定义补足空格,但是在访问的时候并不会得到这些空格,这些空格在返回给应用之前已经去掉。
同时需要注意:在某些情况下可能需要返回字段后的空格,而使用这种格式时后面到空格会被自动处理掉。
动态表包含变长字段,记录不是固定长度的,这样存储的优点是占用空间较少,但是频繁到更新删除记录会产生碎片,需要定期执行OPTIMIZE TABLE语句或myisamchk-r命令来改善性能,并且出现故障的时候恢复相对比较困难。
压缩表由myisamchk工具创建,占据非常小的空间,因为每条记录都是被单独压缩的,所以只有非常小的访问开支。
(二)InnoDB
InnoDB存储引擎提供了具有提交、回滚和崩溃恢复能力的事务安全。但是对比MyISAM的存储引擎,InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。
1)自动增长列:
InnoDB表的自动增长列可以手工插入,但是插入的如果是空或0,则实际插入到则是自动增长后到值。可以通过"ALTER TABLE…AUTO_INCREMENT=n;"语句强制设置自动增长值的起始值,默认为1,但是该强制到默认值是保存在内存中,数据库重启后该值将会丢失。
可以使用LAST_INSERT_ID()查询当前线程最后插入记录使用的值。如果一次插入多条记录,那么返回的是第一条记录使用的自动增长值。
对于InnoDB表,自动增长列必须是索引。如果是组合索引,也必须是组合索引的第一列,但是对于MyISAM表,自动增长列可以是组合索引的其他列,这样插入记录后,自动增长列是按照组合索引到前面几列排序后递增的。
2)外键约束:
MySQL支持外键的存储引擎只有InnoDB,在创建外键的时候,父表必须有对应的索引,子表在创建外键的时候也会自动创建对应的索引。
在创建索引的时候,可以指定在删除、更新父表时,对子表进行的相应操作,包括restrict、cascade、set null和no action。其中restrict和no action相同,是指限制在子表有关联的情况下,父表不能更新;
casecade表示父表在更新或删除时,更新或者删除子表对应的记录;set null则表示父表在更新或者删除的时候,子表对应的字段被set null。
当某个表被其它表创建了外键参照,那么该表对应的索引或主键被禁止删除。
可以使用set foreign_key_checks=0;临时关闭外键约束,set foreign_key_checks=1;打开约束。
(三)MEMORY
memory使用存在内存中的内容来创建表。每个MEMORY表实际对应一个磁盘文件,格式是.frm。MEMORY类型的表访问非常快,因为它到数据是放在内存中的,并且默认使用HASH索引,但是一旦服务器关闭,表中的数据就会丢失,但表还会继续存在。
默认情况下,memory数据表使用散列索引,利用这种索引进行“相等比较”非常快,但是对“范围比较”的速度就慢多了。
因此,散列索引值适合使用在"=“和”<=>“的操作符中,不适合使用在”<“或”>“操作符中,也同样不适合用在order by字句里。如果确实要使用”<“或”>"或betwen操作符,可以使用btree索引来加快速度。
存储在MEMORY数据表里的数据行使用的是长度不变的格式,因此加快处理速度,这意味着不能使用BLOB和TEXT这样的长度可变的数据类型。VARCHAR是一种长度可变的类型,但因为它在MySQL内部当作长度固定不变的CHAR类型,所以可以使用。
create table tab_memory engine=memory select id,name,age,addr from man order by id;
使用USING HASH/BTREE来指定特定到索引。
create index mem_hash using hash on tab_memory(city_id);
在启动MySQL服务的时候使用–init-file选项,把insert into…select或load data infile这样的语句放入到这个文件中,就可以在服务启动时从持久稳固的数据源中装载表。
服务器需要足够的内存来维持所在的在同一时间使用的MEMORY表,当不再使用MEMORY表时,要释放MEMORY表所占用的内存,应该执行DELETE FROM或truncate table或者删除整个表。
每个MEMORY表中放置到数据量的大小,受到max_heap_table_size系统变量的约束,这个系统变量的初始值是16M,同时在创建MEMORY表时可以使用MAX_ROWS子句来指定表中的最大行数。
(四)MERGE
merge存储引擎是一组MyISAM表的组合,这些MyISAM表结构必须完全相同,MERGE表中并没有数据,对MERGE类型的表可以进行查询、更新、删除的操作,这些操作实际上是对内部的MyISAM表进行操作。
对于对MERGE表进行的插入操作,是根据INSERT_METHOD子句定义的插入的表,可以有3个不同的值,first和last值使得插入操作被相应的作用在第一个或最后一个表上,不定义这个子句或者为NO,表示不能对这个MERGE表进行插入操作。可以对MERGE表进行drop操作,
这个操作只是删除MERGE表的定义,对内部的表没有任何影响。MERGE在磁盘上保留2个以MERGE表名开头文件:.frm文件存储表的定义;.MRG文件包含组合表的信息,包括MERGE表由哪些表组成,插入数据时的依据。
可以通过修改.MRG文件来修改MERGE表,但是修改后要通过flush table刷新。
create table man_all(id int,name varchar(20))engine=merge union=(man1,man2)insert_methos=last;
5)说说事务的特性和隔离级别;
MySQL数据库为我们提供的四种隔离级别:
①Serializable(串行化):可避免脏读、不可重复读、幻读的发生。
②Repeatable read(可重复读):可避免脏读、不可重复读的发生。
③Read committed(读已提交):可避免脏读的发生。
④Read uncommitted(读未提交):最低级别,任何情况都无法保证。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。
在MySQL数据库中默认的隔离级别为Repeatable read(可重复读)。
在MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read(可重复读);而在Oracle数据库中,只支持Serializable(串行化)级别和Read committed(读已提交)这两种级别,其中默认的为Read committed级别。
6)悲观锁和乐观锁的区别,怎么实现;
在数据库中,悲观锁的流程如下:
在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。
如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。
如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。
其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。
MySQL InnoDB中使用悲观锁
要使用悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。set autocommit=0;
//0.开始事务
begin;/begin work;/start transaction;(三者选一就可以)
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders(id,goods_id)values(null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;/commit work;
上面的查询语句中,我们使用了select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁。此时在t_goods表中,id为1的那条数据就被我们锁定了,其它的事务必须等本次事务提交之后才能执行。这样我们可以保证当前的数据不会被其它事务修改。
上面我们提到,使用select…for update会把数据给锁住,不过我们需要注意一些锁的级别,MySQL InnoDB默认行级锁。行级锁都是基于索引的,如果一条SQL语句用不到索引是不会使用行级锁的,会使用表级锁把整张表锁住,这点需要注意。
优点与不足
悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;
另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数
乐观锁
在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。
它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。
如果其他事务有更新的话,正在提交的事务会进行回滚。乐观事务控制最早是由孔祥重(H.T.Kung)教授提出。
乐观锁(Optimistic Locking)相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让返回用户错误的信息,让用户决定如何去做。
相对于悲观锁,在对数据库进行处理的时候,乐观锁并不会使用数据库提供的锁机制。一般的实现乐观锁的方式就是记录数据版本。
数据版本,为数据增加的一个版本标识。当读取数据时,将版本标识的值一同读出,数据每更新一次,同时对版本标识进行更新。
当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的版本标识进行比对,如果数据库表当前版本号与第一次取出来的版本标识值相等,则予以更新,否则认为是过期数据。
实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳。
使用版本号实现乐观锁
使用版本号时,可以在数据初始化时指定一个版本号,每次对数据的更新操作都对版本号执行+1操作。并判断当前版本号是不是该数据的最新的版本号。
1.查询出商品信息
select(status,status,version)from t_goods where id=#{id}
2.根据商品信息生成订单
3.修改商品status为2
update t_goods
set status=2,version=version+1
where id=#{id}and version=#{version};
优点与不足
乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题
五.mq:
1)mq的原理是什么:有点大。。都可以说;
消息队列技术是分布式应用间交换信息的一种技术。消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读走。通过消息队列,应用程序可独立地执行–它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。
在分布式计算环境中,为了集成分布式应用,开发者需要对异构网络环境下的分布式应用提供有效的通信手段。为了管理需要共享的信息,对应用提供公共的信息交换机制是重要的。
消息队列为构造以同步或异步方式实现的分布式应用提供了松耦合方法。消息队列的API调用被嵌入到新的或现存的应用中,通过消息发送到内存或基于磁盘的队列或从它读出而提供信息交换。消息队列可用在应用中以执行多种功能,比如要求服务、交换信息或异步处理等。
中间件是一种独立的系统软件或服务程序,分布式应用系统借助这种软件在不同的技术之间共享资源,管理计算资源和网络通讯。它在计算机系统中是一个关键软件,它能实现应用的互连和互操作性,能保证系统的安全、可靠、高效的运行。
中间件位于用户应用和操作系统及网络软件之间,它为应用提供了公用的通信手段,并且独立于网络和操作系统。
中间件为开发者提供了公用于所有环境的应用程序接口,当应用程序中嵌入其函数调用,它便可利用其运行的特定操作系统和网络环境的功能,为应用执行通信功能。
如果没有消息中间件完成信息交换,应用开发者为了传输数据,必须要学会如何用网络和操作系统软件的功能,编写相应的应用程序来发送和接收信息,且交换信息没有标准方法,每个应用必须进行特定的编程从而和多平台、不同环境下的一个或多个应用通信。
例如,为了实现网络上不同主机系统间的通信,将要求具备在网络上如何交换信息的知识(比如用TCP/IP的socket程序设计);为了实现同一主机内不同进程之间的通讯,将要求具备操作系统的消息队列或命名管道(Pipes)等知识。
MQ的通讯模式
1)点对点通讯:点对点方式是最为传统和常见的通讯方式,它支持一对一、一对多、多对多、多对一等多种配置方式,支持树状、网状等多种拓扑结构。
2)多点广播:MQ适用于不同类型的应用。其中重要的,也是正在发展中的是"多点广播"应用,即能够将消息发送到多个目标站点(Destination List)。
可以使用一条MQ指令将单一消息发送到多个目标站点,并确保为每一站点可靠地提供信息。MQ不仅提供了多点广播的功能,而且还拥有智能消息分发功能,在将一条消息发送到同一系统上的多个用户时,MQ将消息的一个复制版本和该系统上接收者的名单发送到目标MQ系统。
目标MQ系统在本地复制这些消息,并将它们发送到名单上的队列,从而尽可能减少网络的传输量。
3)发布/订阅(Publish/Subscribe)模式:发布/订阅功能使消息的分发可以突破目的队列地理指向的限制,使消息按照特定的主题甚至内容进行分发,用户或应用程序可以根据主题或内容接收到所需要的消息。
发布/订阅功能使得发送者和接收者之间的耦合关系变得更为松散,发送者不必关心接收者的目的地址,而接收者也不必关心消息的发送地址,而只是根据消息的主题进行消息的收发。
在MQ家族产品中,MQ Event Broker是专门用于使用发布/订阅技术进行数据通讯的产品,它支持基于队列和直接基于TCP/IP两种方式的发布和订阅。
4)群集(Cluster):为了简化点对点通讯模式中的系统配置,MQ提供Cluster(群集)的解决方案。群集类似于一个域(Domain),群集内部的队列管理器之间通讯时,不需要两两之间建立消息通道,而是采用群集(Cluster)通道与其它成员通讯,从而大大简化了系统配置。
此外,群集中的队列管理器之间能够自动进行负载均衡,当某一队列管理器出现故障时,其它队列管理器可以接管它的工作,从而大大提高系统的高可靠性
1)mq的持久化是怎么做的;
以ActiveMq为例
ActiveMQ消息持久化方式,分别是:文件、mysql数据库、oracle数据库
a.文件持久化:
ActiveMQ默认的消息保存方式,一般如果没有修改过其他持久化方式的话可以不用修改配置文件。
如果是修改过的,打开盘符:\apache-activemq-版本号\conf\activemq.xml,然后找到节点,将其替换成以下代码段
然后修改配置文件(此处演示为spring+ActiveMQ),找到消息发送者所对应的JmsTemplate配置代码块,增加以下配置
!–是否持久化DeliveryMode.NON_PERSISTENT=1:非持久;DeliveryMode.PERSISTENT=2:持久–
<property name="deliveryMode"value=“2”/>
以下是JmsTemplate配置完整版
<bean id="jmsTemplateOne"class=“org.springframework.jms.core.JmsTemplate”>
<property name="connectionFactory"ref=“connectionFactory”/>
<property name="defaultDestination"ref=“queueDestination”/>
<property name="receiveTimeout"value=“10000”/>
!–是否持久化DeliveryMode.NON_PERSISTENT=1:非持久;DeliveryMode.PERSISTENT=2:持久–
<property name="deliveryMode"value=“2”/>
这样就算配置完成了文件持久化方式了,重启项目和ActiveMQ,发送一定消息队列之后关闭ActiveMQ服务,再启动,你可以看到之前发送的消息未消费的依然保持在文件里面,继续让监听者消费。
b.MySQL持久化
首先需要把MySql的驱动放到ActiveMQ的Lib目录下,我用的文件名字是:mysql-connector-java-5.1.27.jar
然后打开盘符:\apache-activemq-版本号\conf\activemq.xml,然后找到节点,将其替换成以下代码段
<jdbcPersistenceAdapter dataDirectory="${activemq.base}/data"dataSource="#derby-ds"/>
在配置文件中的broker节点外增加以下代码
<bean id="derby-ds"class="org.apache.commons.dbcp.BasicDataSource"destroy-method=“close”>
<property name="driverClassName"value=“com.mysql.jdbc.Driver”/>
<property name="url"value=“jdbc:mysql://localhost/activemq?relaxAutoCommit=true”/>
<property name="username"value=“root”/>
<property name="password"value=“123456”/>
<property name="maxActive"value=“200”/>
<property name="poolPreparedStatements"value=“true”/>
这样就算完成了mysql持久化配置了,验证方式同a,打开mysql数据库你能看到三张表,分别是:activemq_acks,activemq_lock,activemq_msgs。
c.Oracle持久化
oracle的配置和mysql一样,在Lib目录下,放入oracle的驱动包,然后配置一下配置文件即可。
<bean id="derby-ds"class="org.apache.commons.dbcp.BasicDataSource"destroy-method=“close”>
<property name="driverClassName"value=“oracle.jdbc.driver.OracleDriver”/>
<property name="url"value=“jdbc:oracle:thin:@localhsot:1521:orcl”/>
<property name="username"value=“activemq”/>
<property name="password"value=“amqadmin”/>
<property name="maxActive"value=“200”/>
<property name="poolPreparedStatements"value=“true”/>
4)redis集群如何同步;
配置主从服务器
Redis主从服务器的搭建很简单,只要少许配置即可,为了演示的方便,我们就在一台服务器上配置:
前提是你已经有了一台redis服务器,如果没有可以参考我以前的文章安装。
下面看看如何配置从服务器:
假设主服务器的配置文件是:/etc/redis.conf,我们复制一份作为从服务器的配置文件:
cp/etc/redis.conf/etc/redis_slave.conf
并作修改:
#vi/etc/redis_slave.conf
port 6380
dbfilename dump_slave.rdb
slaveof 127.0.0.1 6379
主服务器的端口使用的是缺省的6379,从服务器的端口我们设置成6380。
然后插入一些测试数据:
redis-benchmark
由于我们没有设定任何参数,所以使用的是缺省端口(6379),在本例中就是主服务器。
然后启动从服务器:
redis-server/etc/redis_slave.conf
确认一下是否都正常启动了:
ps-ef|grep redis
进入数据目录,查一下数据文件的散列:
md5sum*.rdb
你会发现数据文件散列都一样,自动同步了。
然后我们关闭一下从服务器(不关也行,我就是为了告诉你如何正确关闭redis服务器):
redis-cli-p 6380 shutdown
接着再往主服务器上写入测试数据:
redis-benchmark-l
这会循环插入测试数据,数据量的大小取决于时间的长短,你可以在适当的时候按ctrl+c停止。
如果从服务器没有启动的话,接着再重新启动从服务器:
redis-server/etc/redis_slave.conf
通过观察文件大小你会发现数据会自动同步,如果没有重启动从服务器,那么数据文件的md5sum散列值可能不同,这是正常的,不要紧。
在操作过程中,有时候你会发现主从服务器的数据文件大小不一样,一般来说也不是问题,因为redis是异步写入磁盘的,此时可能有部分数据还在内存中,没有同步到磁盘,所以文件大小略显不同,可以分别在主从服务器上执行:
redis-cli save(redis-cli-p 6380 save)
这条命令强制同步到磁盘,再看大小就应该一样了。
配置文件redis.conf里有一部分和save相关的参数,缺省如下:
#Save the DB on disk:
#save
#Will save the DB if both the given number of seconds and the given
#number of write operations against the DB occurred.
#In the example below the behaviour will be to save:
#after 900 sec(15 min)if at least 1 key changed
#after 300 sec(5 min)if at least 10 keys changed
#after 60 sec if at least 10000 keys changed
save 900 1
save 300 10
save 60 10000
在主服务器上,我们可以去掉上面的设置,改成类似下面的设置(只要参数值够大即可):
save 10000000000 10000000000
如此一来主服务器变成一个完全的内存服务器,所有的操作都在内存里完成,“永远”不会再往磁盘上持久化保存数据,异步的也没有。持久化则通过从服务器来完成,这样在操作主服务器的时候效率会更高。
不过要注意的一点是此方法不适合保存关键数据,否则一旦主服务器挂掉,如果你头脑一热简单的重启服务,那么从服务器的数据也会跟着消失,此时,必须拷贝一份备份数据到主服务器,然后再重启服务才可以,数据的恢复稍显麻烦。
从服务器也可以通过设置这个参数来调整从内存同步到磁盘的频率。
利用主从服务器备份
可以利用主从服务器的方便性来备份,专门做一台从服务器用于备份功能,当需要备份的时候,在从服务器上执行下列命令:
redis-cli save
redis-cli shutdown
然后拷贝数据目录下的rdb文件即可
九、关于内存溢出问题的查找方式
原因分析
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.使用的第三方软件中的BUG;
5.启动参数内存值设定的过小;
解决思路:
第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
使用工具:
常用的有BoundsCheaker、Deleaker、Visual Leak Detector等
十、事务的作用
保证数据的原子性一致性隔离性和永久性。一般说为了保证数据的原子性即可。
十一、java.lang包下的常用工具类,本次考到了Random和Math
java.lang:提供利用Java编程语言进行程序设计的基础类。
java.io:通过数据流、序列化和文件系统提供系统输入和输出。
java.lang.ref:强引用,软引用,弱引用,虚引用。
java.math:提供用于执行任意精度整数算法(BigInteger)和任意精度小数算法(BigDecimal)的类。
java.concurrent:在并发编程中很常用的实用工具类(ThreadFactory)
十二、Mysql查询
索引:缺点是索引过多写操作性能低,而且占的空间大。优点是查询快。数据量较大的情况下索引一般加在变化量较大的字段上。
十三、MySQL事务
隔离级别:http://blog.csdn.net/fg2006/article/details/6937413
事务特征:https://zhidao.baidu.com/question/212549640.html
事物的隔离级别:脏读(读取未提交的记录),读取提交,重复读(幻读),序列化。
事物的特性:原子性,一致性,隔离性,永久性。
十四、MySQL行锁、表锁
悲观锁:select…for update行锁,执行时其他事物不可对此记录进行操作。
乐观锁:根据时间戳,版本号,update…set version=version+1.where version=#{version}
Spring使用注解方式进行事务管理
使用步骤:
步骤一、在spring配置文件中引入tx:命名空间
<beans xmlns=“http://www.springframework.org/schema/beans”
xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance”
xmlns:tx=“http://www.springframework.org/schema/tx”
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
步骤二、具有@Transactional注解的bean自动配置为声明式事务支持
<bean id="defaultTransactionManager"class=“org.springframework.orm.hibernate3.HibernateTransactionManager”>
<property name="sessionFactory"ref=“sessionFactory”/>
<tx:annotation-driven transaction-manager="defaultTransactionManager"proxy-target-class=“true”/>
步骤三、在接口或类的声明处,写一个@Transactional.
要是只在接口上写,接口的实现类就会继承下来、接口的实现类的具体方法,可以覆盖类声明处的设置
@Transactional//类级的注解、适用于类中所有的public的方法
事务的传播行为和隔离级别
大家在使用spring的注解式事务管理时,对事务的传播行为和隔离级别可能有点不知所措,下边就详细的介绍下以备方便查阅。
事物注解方式:@Transactional
当标于类前时,标示类中所有方法都进行事物处理,例子:
@Transactional
public class TestServiceBean implements TestService{}
当类中某些方法不需要事物时:
@Transactional
public class TestServiceBean implements TestService{
private TestDao dao;
public void setDao(TestDao dao){
this.dao=dao;
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public ListgetAll(){
return null;
}
}
事物传播行为介绍:
@Transactional(propagation=Propagation.REQUIRED)
如果有事务,那么加入事务,没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
事物超时设置:
@Transactional(timeout=30)//默认是30秒
事务隔离级别:
@Transactional(isolation=Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读,不可重复读)基本不使用
@Transactional(isolation=Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation=Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation=Isolation.SERIALIZABLE)
串行化
MYSQL:默认为REPEATABLE_READ级别
SQLSERVER:默认为READ_COMMITTED
脏读:一个事务读取到另一事务未提交的更新数据
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同,换句话说,
后续读取可以读到另一事务已提交的更新数据.相反,"可重复读"在同一事务中多次
读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据
幻读:一个事务读到另一个事务已提交的insert数据
@Transactional注解中常用参数说明
readOnly
该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)
rollbackFor
该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:
指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(rollbackFor={RuntimeException.class,Exception.class})
rollbackForClassName
该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:
指定单一异常类名称:@Transactional(rollbackForClassName=“RuntimeException”)
指定多个异常类名称:@Transactional(rollbackForClassName={“RuntimeException”,“Exception”})
noRollbackFor
该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:
指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)
指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class,Exception.class})
noRollbackForClassName
该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:
指定单一异常类名称:@Transactional(noRollbackForClassName=“RuntimeException”)
指定多个异常类名称:
@Transactional(noRollbackForClassName={“RuntimeException”,“Exception”})
propagation
该属性用于设置事务的传播行为,具体取值可参考表6-7。
例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation
该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置
timeout
该属性用于设置事务的超时秒数,默认值为-1表示永不超时
注意的几点:
1@Transactional只能被应用到public方法上,对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2用spring事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new
RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例
外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上
@Transactional(rollbackFor={Exception.class,其它异常}).如果让unchecked例外不回滚:
@Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class)//指定回滚,遇到异常Exception时回滚
public void methodName(){
throw new Exception(“注释”);
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException(“注释”);)会回滚
public ItimDaoImpl getItemDaoImpl(){
throw new RuntimeException(“注释”);
}
3、@Transactional注解应该只被应用到public可见度的方法上。如果你在protected、private或者package-visible的方法上使用@Transactional注解,它也不会报错,但是这个被注解的方法将不会展示已配置的事务设置。
4、@Transactional注解可以被应用于接口定义和接口方法、类定义和类的public方法上。然而,请注意仅仅@Transactional注解的出现不足于开启事务行为,它仅仅是一种元数据,能够被可以识别@Transactional注解和上述的配置适当的具有事务行为的beans所使用。
上面的例子中,其实正是tx:annotation-driven/元素的出现开启了事务行为。
5、Spring团队的建议是你在具体的类(或类的方法)上使用@Transactional注解,而不要使用在类所要实现的任何接口上。
你当然可以在接口上使用@Transactional注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,
而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用@Transactional注解。
1.2 MyBatis简介
iBATIS的着力点,则在于POJO与SQL之间的映射关系。然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。相对Hibernate“O/R”而言,iBATIS是一种“Sql Mapping”的ORM实现。
Mybatis调优方案
MyBatis在Session方面和Hibernate的Session生命周期是一致的,同样需要合理的Session管理机制。MyBatis同样具有二级缓存机制。MyBatis可以进行详细的SQL优化设计。
SQL优化方面
Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。而Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。
Hibernate HQL语句的调优需要将SQL打印出来,而Hibernate的SQL被很多人嫌弃因为太丑了。MyBatis的SQL是自己手动写的所以调整方便。但Hibernate具有自己的日志统计。Mybatis本身不带日志统计,使用Log4j进行日志记录。
扩展性方面
Hibernate与具体数据库的关联只需在XML文件中配置即可,所有的HQL语句与具体使用的数据库无关,移植性很好。MyBatis项目中所有的SQL语句都是依赖所用的数据库的,所以不同数据库类型的支持不好。
第六章Hibernate与Mybatis对比总结
两者相同点
Hibernate与MyBatis都可以是通过SessionFactoryBuider由XML配置文件生成SessionFactory,然后由SessionFactory生成Session,最后由Session来开启执行事务和SQL语句。其中SessionFactoryBuider,SessionFactory,Session的生命周期都是差不多的。
Hibernate和MyBatis都支持JDBC和JTA事务处理。
Mybatis优势
MyBatis可以进行更为细致的SQL优化,可以减少查询字段。
MyBatis容易掌握,而Hibernate门槛较高。
MyBatis中使用#和$书写占位符有什么区别?
答:#将传入的数据都当成一个字符串,会对传入的数据自动加上引号;将传入的数据直接显示生成在SQL中。注意:使用占位符可能会导致SQL注射攻击,能用#的地方就不要使用,写order by子句的时候应该用而不是#。
MyBatis中的动态SQL是什么意思?
答:对于一些复杂的查询,我们可能会指定多个查询条件,但是这些条件可能存在也可能不存在,例如在58同城上面找房子,我们可能会指定面积、楼层和所在位置来查找房源,也可能会指定面积、价格、户型和所在位置来查找房源,此时就需要根据用户指定的条件动态生成SQL语句。
如果不使用持久层框架我们可能需要自己拼装SQL语句,还好MyBatis提供了动态SQL的功能来解决这个问题。MyBatis中用于实现动态SQL的元素主要有:
-if
-choose/when/otherwise
-trim
-where
-set
-foreach
Spring中Bean的作用域有哪些?
答:在Spring的早期版本中,仅有两个作用域:singleton和prototype,前者表示Bean以单例的方式存在;后者表示每次从容器中调用Bean时,都会返回一个新的实例,prototype通常翻译为原型。
补充:设计模式中的创建型模式中也有一个原型模式,原型模式也是一个常用的模式,例如做一个室内设计软件,所有的素材都在工具箱中,而每次从工具箱中取出的都是素材对象的一个原型,可以通过对象克隆来实现原型模式。
Spring 2.x中针对WebApplicationContext新增了3个作用域,分别是:request(每次HTTP请求都会创建一个新的Bean)、session(同一个HttpSession共享同一个Bean,不同的HttpSession使用不同的Bean)和globalSession(同一个全局Session共享一个Bean)。
说明:单例模式和原型模式都是重要的设计模式。一般情况下,无状态或状态不可变的类适合使用单例模式。在传统开发中,由于DAO持有Connection这个非线程安全对象因而没有使用单例模式;但在Spring环境下,所有DAO类对可以采用单例模式,因为Spring利用AOP和java API中的ThreadLocal对非线程安全的对象进行了特殊处理。
ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。ThreadLocal,顾名思义是线程的一个本地化对象,当工作于多线程中的对象使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程分配一个独立的变量副本,所以每一个线程都可以独立的改变自己的副本,而不影响其他线程所对应的副本。从线程的角度看,这个变量就像是线程的本地变量。
ThreadLocal类非常简单好用,只有四个方法,能用上的也就是下面三个方法:
-void set(T value):设置当前线程的线程局部变量的值。
-T get():获得当前线程所对应的线程局部变量的值。
-void remove():删除当前线程中线程局部变量的值。
ThreadLocal是如何做到为每一个线程维护一份独立的变量副本的呢?在ThreadLocal类中有一个Map,键为线程对象,值是其线程对应的变量的副本
选择使用Spring框架的原因(Spring框架为企业级开发带来的好处有哪些)?
答:可以从以下几个方面作答:
非侵入式:支持基于POJO的编程模式,不强制性的要求实现Spring框架中的接口或继承Spring框架中的类。
IoC容器:IoC容器帮助应用程序管理对象以及对象之间的依赖关系,对象之间的依赖关系如果发生了改变只需要修改配置文件而不是修改代码,因为代码的修改可能意味着项目的重新构建和完整的回归测试。
有了IoC容器,程序员再也不需要自己编写工厂、单例,这一点特别符合Spring的精神"不要重复的发明轮子"。
AOP(面向切面编程):将所有的横切关注功能封装到切面(aspect)中,通过配置的方式将横切关注功能动态添加到目标代码上,进一步实现了业务逻辑和系统服务之间的分离。另一方面,有了AOP程序员可以省去很多自己写代理类的工作。
MVC:Spring的MVC框架是非常优秀的,从各个方面都可以甩Struts 2几条街,为Web表示层提供了更好的解决方案。
事务管理:Spring以宽广的胸怀接纳多种持久层技术,并且为其提供了声明式的事务管理,在不需要任何一行代码的情况下就能够完成事务管理。
其他:选择Spring框架的原因还远不止于此,Spring为Java企业级开发提供了一站式选择,你可以在需要的时候使用它的部分和全部,更重要的是,你甚至可以在感觉不到Spring存在的情况下,在你的项目中使用Spring提供的各种优秀的功能。
阐述Spring框架中Bean的生命周期?
答:
①Spring IoC容器找到关于Bean的定义并实例化该Bean。
②Spring IoC容器对Bean进行依赖注入。
③如果Bean实现了BeanNameAware接口,则将该Bean的id传给setBeanName方法。
④如果Bean实现了BeanFactoryAware接口,则将BeanFactory对象传给setBeanFactory方法。
⑤如果Bean实现了BeanPostProcessor接口,则调用其postProcessBeforeInitialization方法。
⑥如果Bean实现了InitializingBean接口,则调用其afterPropertySet方法。
⑦如果有和Bean关联的BeanPostProcessors对象,则这些对象的postProcessAfterInitialization方法被调用。
⑧当销毁Bean实例时,如果Bean实现了DisposableBean接口,则调用其destroy方法。
大型网站在架构上应当考虑哪些问题?
答:
分层:分层是处理任何复杂系统最常见的手段之一,将系统横向切分成若干个层面,每个层面只承担单一的职责,然后通过下层为上层提供的基础设施和服务以及上层对下层的调用来形成一个完整的复杂的系统。
计算机网络的开放系统互联参考模型(OSI/RM)和Internet的TCP/IP模型都是分层结构,大型网站的软件系统也可以使用分层的理念将其分为持久层(提供数据存储和访问服务)、业务层(处理业务逻辑,系统中最核心的部分)和表示层(系统交互、视图展示)。
需要指出的是:(1)分层是逻辑上的划分,在物理上可以位于同一设备上也可以在不同的设备上部署不同的功能模块,这样可以使用更多的计算资源来应对用户的并发访问;(2)层与层之间应当有清晰的边界,这样分层才有意义,才更利于软件的开发和维护。
分割:分割是对软件的纵向切分。我们可以将大型网站的不同功能和服务分割开,形成高内聚低耦合的功能模块(单元)
。在设计初期可以做一个粗粒度的分割,将网站分割为若干个功能模块,后期还可以进一步对每个模块进行细粒度的分割,这样一方面有助于软件的开发和维护,另一方面有助于分布式的部署,提供网站的并发处理能力和功能的扩展。
分布式:除了上面提到的内容,网站的静态资源(JavaScript、CSS、图片等)也可以采用独立分布式部署并采用独立的域名,这样可以减轻应用服务器的负载压力,也使得浏览器对资源的加载更快。
数据的存取也应该是分布式的,传统的商业级关系型数据库产品基本上都支持分布式部署,而新生的NoSQL产品几乎都是分布式的。当然,网站后台的业务处理也要使用分布式技术,例如查询索引的构建、数据分析等,这些业务计算规模庞大,可以使用Hadoop以及MapReduce分布式计算框架来处理。
集群:集群使得有更多的服务器提供相同的服务,可以更好的提供对并发的支持。
缓存:所谓缓存就是用空间换取时间的技术,将数据尽可能放在距离计算最近的位置。使用缓存是网站优化的第一定律。我们通常说的CDN、反向代理、热点数据都是对缓存技术的使用。
异步:异步是实现软件实体之间解耦合的又一重要手段。异步架构是典型的生产者消费者模式,二者之间没有直接的调用关系,只要保持数据结构不变,彼此功能实现可以随意变化而不互相影响,这对网站的扩展非常有利。
使用异步处理还可以提高系统可用性,加快网站的响应速度(用Ajax加载数据就是一种异步技术),同时还可以起到削峰作用(应对瞬时高并发)。";能推迟处理的都要推迟处理"是网站优化的第二定律,而异步是践行网站优化第二定律的重要手段。
冗余:各种服务器都要提供相应的冗余服务器以便在某台或某些服务器宕机时还能保证网站可以正常工作,同时也提供了灾难恢复的可能性。冗余是网站高可用性的重要保证。
你用过的网站前端优化的技术有哪些?
答:
①浏览器访问优化:
-减少HTTP请求数量:合并CSS、合并javascript、合并图片(CSS Sprite)
-使用浏览器缓存:通过设置HTTP响应头中的Cache-Control和Expires属性,将CSS、JavaScript、图片等在浏览器中缓存,当这些静态资源需要更新时,可以更新HTML文件中的引用来让浏览器重新请求新的资源
-启用压缩
-CSS前置,JavaScript后置
-减少Cookie传输
②CDN加速:CDN(Content Distribute Network)的本质仍然是缓存,将数据缓存在离用户最近的地方,CDN通常部署在网络运营商的机房,不仅可以提升响应速度,还可以减少应用服务器的压力。当然,CDN缓存的通常都是静态资源。
③反向代理:反向代理相当于应用服务器的一个门面,可以保护网站的安全性,也可以实现负载均衡的功能,当然最重要的是它缓存了用户访问的热点资源,可以直接从反向代理将某些内容返回给用户浏览器。
你使用过的应用服务器优化技术有哪些?
答:①分布式缓存:缓存的本质就是内存中的哈希表,如果设计一个优质的哈希函数,那么理论上哈希表读写的渐近时间复杂度为O(1)。缓存主要用来存放那些读写比很高、变化很少的数据,这样应用程序读取数据时先到缓存中读取,如果没有或者数据已经失效再去访问数据库或文件系统,并根据拟定的规则将数据写入缓存。对网站数据的访问也符合二八定律(Pareto分布,幂律分布),即80%的访问都集中在20%的数据上,如果能够将这20%的数据缓存起来,那么系统的性能将得到显著的改善。当然,使用缓存需要解决以下几个问题:-频繁修改的数据;-数据不一致与脏读;-缓存雪崩(可以采用分布式缓存服务器集群加以解决,memcached是广泛采用的解决方案);-缓存预热;-缓存穿透(恶意持续请求不存在的数据)。
②异步操作:可以使用消息队列将调用异步化,通过异步处理将短时间高并发产生的事件消息存储在消息队列中,从而起到削峰作用。电商网站在进行促销活动时,可以将用户的订单请求存入消息队列,这样可以抵御大量的并发订单请求对系统和数据库的冲击。目前,绝大多数的电商网站即便不进行促销活动,订单系统都采用了消息队列来处理。
③使用集群。
④代码优化:-多线程:基于Java的Web开发基本上都通过多线程的方式响应用户的并发请求,使用多线程技术在编程上要解决线程安全问题,主要可以考虑以下几个方面:A.将对象设计为无状态对象(这和面向对象的编程观点是矛盾的,在面向对象的世界中被视为不良设计),这样就不会存在并发访问时对象状态不一致的问题。B.在方法内部创建对象,这样对象由进入方法的线程创建,不会出现多个线程访问同一对象的问题。使用ThreadLocal将对象与线程绑定也是很好的做法,这一点在前面已经探讨过了。C.对资源进行并发访问时应当使用合理的锁机制。-非阻塞I/O:使用单线程和非阻塞I/O是目前公认的比多线程的方式更能充分发挥服务器性能的应用模式,基于Node.js构建的服务器就采用了这样的方式。Java在JDK 1.4中就引入了NIO(Non-blocking I/O),在Servlet 3规范中又引入了异步Servlet的概念,这些都为在服务器端采用非阻塞I/O提供了必要的基础。-资源复用:资源复用主要有两种方式,一是单例,二是对象池,我们使用的数据库连接池、线程池都是对象池化技术,这是典型的用空间换取时间的策略,另一方面也实现对资源的复用,从而避免了不必要的创建和释放资源所带来的开销。