-- ************************************* 两年程序员知识 begin****************************************
spring特性:
1,方便解耦,简化开发 通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、
属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2,AOP编程的支持 通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3,声明式事务的支持 在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4,方便程序的测试 可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。
5,方便集成各种优秀框架 Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hession、Quartz)等的直接支持。
6,降低Java EE API的使用难度 Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
7,Java 源码是经典学习范例 Spring的源码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。
如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
对Spring aop的理解
AOP,也就是面向切面编程,我是这么理解的:学java的应该都知道面向对象编程(oop),而OOP是从静态解读考虑程序结构,但AOP是从动态角度考虑程序运行过程。
也可以说AOP是OOP的补充和完善。OOP引入了封装、继承和多态性等概念来建立一种对象层次结构, 用以模拟公共的一个集合。当我们需要为分散的对象引入公共行为的时候,
则OOP显得无能为力了。也就是说,OOP允许你定义从上到下的关系,但并不适合从左到右的关系,例如日志功能。日志代码往往水平的散步在所有对象层次中,
而与它所散步到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切代码,
在oop设计中,他导致了大量代码的重复,而不利于各个模块的重用。
而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将哪些影响了多个类的公共行为封装到一个可重用模块,简单的来说就是将那些与业务无关,
却为业务模块所共同调用的逻辑或责任封装起来。便于减少系统的重复代码,降低模块间的耦合度等。
aop 底层有两种代理(jdk代理,CGLIB代理):
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
cglib代理,是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。(例如service的事务处理)
对Spring IOC的理解
IoC(控制反转),将类的创建和依赖关系写在配置文件里,由配置文件注入,实现了松耦合
IOC 底层实现原理:JAVA反射
springmvc的运行流程,实现原理?
执行流程:
1). 若一个请求匹配 DispatcherServlet 的请求映射路径(在 web.xml 中指定), WEB 容器将该请求转交给 DispatcherServlet 处理
2). DispatcherServlet 接收到请求后, 将根据请求信息(包括 URL、HTTP 方法、请求 头、请求参数、Cookie 等)及 HandlerMapping 的配置找到处理请求的处理器(Handler). 可将 HandlerMapping 看成路由控制器,将 Handler 看成目标主机。
3). 当 DispatcherServlet 根据 HandlerMapping 得到对应当前请求的 Handler 后, 通过 HandlerAdapter 对 Handler 进行封装,再以统一的适配器接口调用 Handler。
4). 处理器完成业务逻辑的处理后将返回一个 ModelAndView 给 DispatcherServlet, ModelAndView 包含了视图逻辑名和模型数据信息
5). DispatcherServlet 借助 ViewResoler 完成逻辑视图名到真实视图对象的解析
6). 得 到 真 实 视 图 对 象 View 后 , DispatcherServlet 使用这个 View 对 ModelAndView 中的模型数据进行视图渲染
实现原理:
基于servlet实现,同样流行的mvc框架还有struts2,两者区别是:
1,SpringMVC的入口是servlet,而Struts2是filter(filter执行顺序 > serlvet)
2,spring会稍微比struts快。 spring mvc是基于方法的设计 ,而sturts是基于类 ,每次发一次请求都会实例一个action,每个action都会被注入属性,而spring基于方法,
粒度更细,但要小心把握像在servlet控制数据一样。spring3mvc是方法级别的拦截,拦截到方法后根据参数上的注解,把request数据注入进去,在spring3mvc中,一个方法对应一个request上下文,
所以说从架构本身上SpringMVC就容易实现restful,且SpringMVC执行和开发效率都应该是高于Struts2的。
来说说你认为写过的最复杂的业务逻辑吧;
这个嘛,我认为目前认为,我没有写过任何复杂的业务逻辑,任何复杂的业务逻辑,细化拆分都是有简单的业务组成,如果你很想听的话,
那我就以登录为例给你说说吧
1,首先,判断用户是否存在,若不存在,返回前端账户不存在信息(json形式),否则执行下一步;
2,判断输入密码是否错误,如果错误,返回前端密码错误,用户密码错误次数加一,若大于指定次数,锁定该用户,否则执行下一步;
3,判断用户是否首次登录,若是,跳转指定页面,强制修改密码,修改完毕之后,重新登录即可,否则进入下一步;
4,判断用户上次登录时间,如果超过指定时间,跳转强制修改页面,否则进入下一步;
5,判断用户上次修改密码时间,如果超过指定时间,跳转强制修改页面,否则进入下一步;
6,初始化一些信息(授权,基础信息保存【字典信息】等),放入session,修改上次用户登录时间,
7,返回成功登录页面
-- *************************************两年以上程序员 end****************************************
-- **************************************************************************************************
-- *************************************三年以上程序员 begin****************************************
-- **************************************************************************************************
深入分析ClassLoader
先说说为什么要知道java的类加载机制。个人认为主要有以下几个原因:
按需加载。JVM启动时不能确定我要加载哪些东西,或者有些类非常大,我只希望用到它时再加载,并非一次性加载所有的class,所以这时候了解了加载机制就可以按需加载了。
类隔离。比如web容器中部署多个应用,应用之间互相可能会有冲突,所以希望尽量隔离,这里可能就要分析各个应用加载的资源和加载顺序之间的冲突,针对这些冲突再自己定些规则,
让它们能够愉快地玩耍。
资源回收。如果你不了解java是如何加载资源的,又怎么理解java是如何回收资源的?
一般说到java的类加载机制,都要说到“双亲委派模型”(其实个人很不理解为什么叫“双亲”,其实英文叫“parent”)。使用这种机制,可以避免重复加载,当父亲已经加载了该类的时候,
就没有必要子ClassLoader再加载一次。JVM根据 类名+包名+ClassLoader实例ID 来判定两个类是否相同,是否已经加载过(所以这里可以略微扩展下,可以通过创建不同的classloader实例
来实现类的热部署)。
a) BootStrapClassLoader。它是最顶层的类加载器,是由C++编写而成, 已经内嵌到JVM中了。在JVM启动时会初始化该ClassLoader,它主要用来读取Java的核心类库JRE/lib/rt.jar中所有的class文件,这个jar文件中包含了java规范定义的所有接口及实现。
b) ExtensionClassLoader。它是用来读取Java的一些扩展类库,如读取JRE/lib/ext/*.jar中的包等(这里要注意,有些版本的是没有ext这个目录的)。
c) AppClassLoader。它是用来读取CLASSPATH下指定的所有jar包或目录的类文件,一般情况下这个就是程序中默认的类加载器。
d) CustomClassLoader。它是用户自定义编写的,它用来读取指定类文件 。基于自定义的ClassLoader可用于加载非Classpath中(如从网络上下载的jar或二进制)的jar及目录、还可以在加载前
对class文件优一些动作,如解密、编码等。
很多资料和文章里说,ExtClassLoader的父类加载器是BootStrapClassLoader,其实这里省掉了一句话,容易造成很多新手(比如我)的迷惑。严格来说,ExtClassLoader的父类加载器是null,
只不过在默认的ClassLoader 的 loadClass 方法中,当parent为null时,是交给BootStrapClassLoader来处理的,而且ExtClassLoader 没有重写默认的loadClass方法,所以,
ExtClassLoader也会调用BootStrapLoader类加载器来加载,这就导致“BootStrapClassLoader具备了ExtClassLoader父类加载器的功能”
查看classloader的源码可以发现三个重要的方法:
a) loadClass。classloader加载类的入口,此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从父ClassLoader中寻找,
如仍然没找到,则从BootstrapClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法,如加载顺序相同,则可通过覆盖findClass来做特殊的处理,
例如解密、固定路径寻找等,当通过整个寻找类的过程仍然未获取到Class对象时,则抛出ClassNotFoundException。如类需要 ,则调用resolveClass进行链接。
b) findClass。此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
C) defineClass。此方法负责将二进制的字节码转换为Class对象,这个方法对于自定义加载类而言非常重要,如二进制的字节码的格式不符合JVM Class文件的格式,抛出ClassFormatError;如需要生成的类名和二进制字节码中的不同,则抛出NoClassDefFoundError;如需要加载的class是受保护的、采用不同签名的或类名是以java.开头的,则抛出SecurityException;如需加载的class在此ClassLoader中已加载,则抛出LinkageError。
类的加载的过程
一个java文件从被加载到被卸载这个生命过程
加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载
类的加载全过程
加载->验证->准备->解析->初始化
1,首先是加载:
这一块虚拟机要完成3件事:
1.通过一个类的全限定名来获取定义此类的二进制字节流。
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3.在java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
关于第一点,很灵活,很多技术都是在这里切入,因为它并没有限定二进制流从哪里来:
从class文件来->一般的文件加载
从zip包中来->加载jar中的类
从网络中来->Applet
2,加载完成后就要开始对那些字节流进行检验
检验的目的:确保class文件的字节流信息符合jvm的口味,不会让jvm感到不舒服。
检验主要经历几个步骤:文件格式验证->元数据验证->字节码验证->符号引用验证
文件格式验证:验证字节流是否符合Class文件格式的规范并验证其版本是否能被当前的jvm版本所处理。
ok没问题后,字节流就可以进入内存的方法区进行保存了。后面的3个校验都是在方法区进行的。
元数据验证:对字节码描述的信息进行语义化分析,保证其描述的内容符合java语言的语法规范。
字节码检验:校验java编译成的字节码文件是否破损或格式错误
符号引用验证:来验证一些引用的真实性与可行性,比如代码里面引了其他类,这里就要去检测一下那些来究竟是否存在;
或者说代码中访问了其他类的一些属性,这里就对那些属性的可访问性进行检验。(这一步将为后面的解析工作打下基础)
3,接着就上面步骤完成后,就会进入准备阶段了:
这阶段会为类变量(指那些静态变量)分配内存并设置初始值的阶段,这些内存在方法区中进行分配。这里不包含用final修饰的static,因为final在编译的时候就会分配了
4,完成上步后,就要进行解析了。解析好像是对类的字段,方法等东西进行转换,具体涉及到Class文件的格式内容,并没深入去了解。
5,在前面的类加载过程中,除了在加载阶段用户可以通过自定义类加载器参与之外,其他的动作完全有jvm主导,到了初始化这块,才开始真正执行java里面的代码。
这一步将会执行一些预操作,注意区分在准备阶段,已经为类变量执行过一次系统赋值了。
其实说白了,这一步就是执行程序的构造器
GC:
垃圾检测方式:
引用计数法:给一个对象添加引用计数器,每当有个地方引用它,计数器就加1;引用失效就减1。
好了,问题来了,如果我有两个对象A和B,互相引用,除此之外,没有其他任何对象引用它们,实际上这两个对象已经无法访问,即是我们说的垃圾对象。但是互相引用,
计数不为0,导致无法回收,所以还有另一种方法:
可达性分析算法:以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java栈中引用的对象、方法区常良池中引用的对象本地方法中引用的对象等。
总之,JVM在做垃圾回收的时候,会检查堆中的所有对象是否会被这些根集对象引用,不能够被引用的对象就会被垃圾收集器回收。
回收算法:
1.标记-清除(Mark-sweep)
说明:算法和名字一样,分为两个阶段:标记和清除。标记所有需要回收的对象,然后统一回收。这是最基础的算法,后续的收集算法都是基于这个算法扩展的。
不足:效率低;标记清除之后会产生大量碎片
2.复制(Copying)
说明:此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。
缺点:就是需要两倍内存空间
3.标记-整理(Mark-Compact)
说明:此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题
缺点:
4.分代收集算法
说明:
这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的收集方式,以便提高回收效率。
为什么要运用分代垃圾回收策略?
在java程序运行的过程中,会产生大量的对象,因每个对象所能承担的职责不同所具有的功能不同所以也有着不一样的生命周期,有的对象生命周期较长,比如Http请求中的Session对象,
线程,Socket连接等;有的对象生命周期较短,比如String对象,由于其不变类的特性,有的在使用一次后即可回收。试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整
个堆空间进行回收,那么消耗的时间相对会很长,而且对于存活时间较长的对象进行的扫描工作等都是徒劳。因此就需要引入分治的思想,所谓分治的思想就是因地制宜,将对象进行代的
划分,把不同生命周期的对象放在不同的代上使用不同的垃圾回收方式。
如何划分?
将对象按其生命周期的不同划分成:年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,所以与java对象的回收关
系不大,与回收息息相关的是年轻代和年老代。这里有个比喻很形象
“假设你是一个普通的 Java 对象,你出生在 Eden 区,在 Eden 区有许多和你差不多的小兄弟、小姐妹,可以把 Eden 区当成幼儿园,在这个幼儿园里大家玩了很长时间。
Eden 区不能无休止地放你们在里面,所以当年纪稍大,你就要被送到学校去上学,这里假设从小学到高中都称为 Survivor 区。开始的时候你在 Survivor 区里面划分出来
的的“From”区,读到高年级了,就进了 Survivor 区的“To”区,中间由于学习成绩不稳定,还经常来回折腾。直到你 18 岁的时候,高中毕业了,该去社会上闯闯了。于是你
就去了年老代,年老代里面人也很多。在年老代里,你生活了 20 年 (每次 GC 加一岁),最后寿终正寝,被 GC 回收。有一点没有提,你在年老代遇到了一个同学,他的名
字叫爱德华 (慕光之城里的帅哥吸血鬼),他以及他的家族永远不会死,那么他们就生活在永生代。”
年轻代:是所有新对象产生的地方。年轻代被分为3个部分——Enden区和两个Survivor区(From和to)当Eden区被对象填满时,就会执行Minor GC。并把所有存活下来的对象转移到其中
一个survivor区(假设为from区)。Minor GC同样会检查存活下来的对象,并把它们转移到另一个survivor区(假设为to区)。这样在一段时间内,总会有一个空的survivor区。
经过多次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。通常这是在年轻代有资格提升到年老代前通过设定年龄阈值来完成的。需要注意,Survivor的两个区是
对称的,没先后关系,from和to是相对的。涉及了复制算法
年老代:在年轻代中经历了N次回收后仍然没有被清除的对象,就会被放到年老代中,可以说他们都是久经沙场而不亡的一代,都是生命周期较长的对象。对于年老代和永久代,就不能再采用
像年轻代中那样搬移腾挪的回收算法,因为那些对于这些回收战场上的老兵来说是小儿科。通常会在老年代内存被占满时将会触发Full GC,回收整个堆内存。涉及了“标记-整理(Mark-Sweep)”的算法。
持久代:用于存放静态文件,比如java类、方法等。持久代对垃圾回收没有显著的影响。
-- *************************************三年以上程序员 end****************************************
-- **************************************************************************************************
-- *************************************五年以上程序员 begin****************************************
-- **************************************************************************************************
简要说明oracle数据库是如何执行SQL语句的。
一、 基本阶段
当用户执行SQL语句(这里主要值数据操纵语言DML)时,通过连接,先将该语句发送到oracle服务器,再由服务器进程处理该语句。
服务器进程处理SQL语句的基本阶段是:解析、执行、返回结果。
1、解析(perse)
解析指检查SQL语句的语法和语义,生成SQL语句的执行计划,并将SQL语句和执行计划存放到SGA区的共享SQL区中。
在解析期间服务器进程会执行如下操作:
1)搜索SGA区的共享SQL区,检查其中是否存在相同的SQL语句及其执行计划。如果有,则直接执行该SQL语句。这样能够提高oracle的性能
如果没有该SQL语句,就检查该SQL的语法。如果语法不正确,就将语法错误消息返回给客户机
2)如果语法正确,就通过查询数据字典,检查该SQL语句的语义,以确定表名、列名是否正确。如果表名和列名不正确,就将语义错误消息返回给客户机
3)如果语义正确,就给相应的对象加解析锁,以防止在解析期间其他用户改变这些对象的结构(或删除这些对象)
4)检查用户是否具有访问相应对象的相应权限。如果没有相应权限,就将权限不够错误消息返回给客户机,如果具有相应的权限,
就由SQL语句的优化器来确定该SQL语句的最佳执行计划,为该SQL语句在SGA区的共享SQL区中分配空间,将该SQL语句及其执行计划装入其中,以便执行
2、 执行(execute)
执行指服务器进程按照SQL语句的执行计划执行SQL语句。在此期间,服务器进程执行如下操作:
1,确定被操纵对象的数据所在的数据块是否已经被读取到SGA区的数据高速缓存区中了。如果数据块在数据高速缓存中,则直接在其中操作
2,如果数据块不在数据高速缓存中,则从数据文件所对应的物理存储设备中读取该数据块,并在数据高速缓存中寻找空闲数据块,将读入的数据放入
3,对于update和delete语句,将需要修改或删除的行锁住,以便在事务结束之前相同的行不会被其他进程修改。对于select和insert语句,因为不会修改数据,所以不需要锁住行。
3、 返回结果
对于select语句,在执行阶段,要将查询到的结果(或被标示的行)返回给用户进程。加入查询结果需要排序,还要利用共享池的排序区,甚至临时表空间的临时段来排序。查询结果总是以列表格式显示。根据查询结果的大小不同,可以一次全部返回,也可以分多次逐步返回。对于其他DML语句,将执行是否成功等状态细心返回给用户进程。
-- *************************************五年以上程序员 end****************************************
-- **************************************************************************************************
-- *************************************必问问题 begin****************************************
-- **************************************************************************************************
说说你最近有什么计划吧;
这个嘛,是一个不错的问题啊,我呢,是搞技术的,也只想在技术方向走向辉煌;为此呢,我专门做了一些准备:
1,一直呢,听说spring是java程序员学习的典范,最想研究一下spring,Spring是于2003 年兴起的一个轻量级的Java 开发框架,它能够活到现在而没有被时间所打死,这足以证明它有足够的魅力诱惑那一群饥渴难耐的程序员,终于呢,我这个一向清高的小哥也沦陷了;其源码设计的精妙,结构清晰,及java设计模式的灵活运用,以及对java技术的高深造诣,都是我学习的典范,其IOC主从关系的转变,让我想到好莱坞的一句话,“别找我们,我们会找你的,你等着哈”,如果不是spring,也许我还在陷入永无止境的new Objec()的苦海深渊中
2,我还买了一些其他的书籍,mysql的从入门到精通,不要问我作者是谁,我看书从来不问出处,也懒的看,平常看到很无聊的电视节目的时候,我遥控器都懒得拿,sql基本程序员必备技能,但是如何快速写出高效的sql是一个值得学习的问题
3,学完这些,大概也过了小半年了吧,接下来干什么呢,唉哟,这就是个问题了,sql学习了,spring研究的差不多了,自己该写点东西,练练手了吧,光看不练,来装逼的吗?
4,练习之后,该干嘛了啊,嗯,分布式,缓存,事多着呢,没几年,估计也别想再技术上面有什么发展,提什么走向辉煌,恩哼
说说你遇到的问题:
1, 问题描述:
短信验证码被别人恶意刷,当时我们这个东西,加的是有验证码的,且后端对手机号格式,发送频率都有校验(每分钟最多六次),
但是还是出现了被恶意刷的问题,几个小时,两千块的短信费就没了,查看后台才知道,手机号没有重复的
解决方案:
原来也考虑过 判断 手机 mac ip,在实施过程中,才知道手机上网是随机分配网络地址的,是没有固定的IP的,
于是采用了另一种方案,数据加密,数据组包组完以后 对数据加密 然后再组包,相当于 多了一层,然后就没有问题了
你还有想问的吗?
1,公司现在做什么业务?进展到哪里了?
2,福利待遇?
3,公司人员怎么分配的,几个前端,几个后台?
-- *************************************必问问题 end****************************************
-- **************************************************************************************************
-- *************************************常问问题 begin****************************************
-- **************************************************************************************************
索引建设原则:
1、索引应该经常建在Where 子句经常用到的列上。如果某个大表经常使用某个字段进行查询,并且检索行数小于总表行数的5%。则应该考虑。
2、对于两表连接的字段,应该建立索引。如果经常在某表的一个字段进行Order By 则也经过进行索引。
3、不应该在小表上建设索引。
Sql 优化:
当Oracle数据库拿到SQL语句时,其会根据查询优化器分析该语句,并根据分析结果生成查询执行计划。
也就是说,数据库是执行的查询计划,而不是Sql语句。
查询优化器有rule-based-optimizer(基于规则的查询优化器) 和Cost-Based-optimizer(基于成本的查询优化器)。
其中基于规则的查询优化器在10g版本中消失。
对于规则查询,其最后查询的是全表扫描。而CBO则会根据统计信息进行最后的选择。
1、先执行From ->Where ->Group By->Order By
2、执行From 字句是从右往左进行执行。因此必须选择记录条数最少的表放在右边。这是为什么呢?
3、对于Where字句其执行顺序是从后向前执行、因此可以过滤最大数量记录的条件必须写在Where子句的末尾,而对于多表之间的连接,则写在之前。
因为这样进行连接时,可以去掉大多不重复的项。
4. SELECT子句中避免使用(*)ORACLE在解析的过程中, 会将’*’ 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间
5、索引失效的情况:
① Not Null/Null 如果某列建立索引,当进行Select * from emp where depto is not null/is null。 则会是索引失效。
② 索引列上不要使用函数,SELECT Col FROM tbl WHERE substr(name ,1 ,3 ) = 'ABC'
或者SELECT Col FROM tbl WHERE name LIKE '%ABC%' 而SELECT Col FROM tbl WHERE name LIKE 'ABC%' 会使用索引。
③ 索引列上不能进行计算SELECT Col FROM tbl WHERE col / 10 > 10 则会使索引失效,应该改成
SELECT Col FROM tbl WHERE col > 10 * 10
④ 索引列上不要使用NOT ( != 、 <> )如:SELECT Col FROM tbl WHERE col ! = 10
应该 改成:SELECT Col FROM tbl WHERE col > 10 OR col < 10 。
6、用UNION替换OR(适用于索引列)
union:是将两个查询的结果集进行追加在一起,它不会引起列的变化。 由于是追加操作,需要两个结果集的列数应该是相关的,
并且相应列的数据类型也应该相当的。union 返回两个结果集,同时将两个结果集重复的项进行消除。 如果不进行消除,用UNOIN ALL.
通常情况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将造成全表扫描. 注意, 以上规则只针对多个索引列有效.
如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低. 在下面的例子中, LOC_ID 和REGION上都建有索引.
高效:
SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10
UNION
SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE REGION = “MELBOURNE”
低效:
SELECT LOC_ID , LOC_DESC , REGION FROM LOCATION WHERE LOC_ID = 10 OR REGION = “MELBOURNE”
如果你坚持要用OR, 那就需要返回记录最少的索引列写在最前面.
7. 用EXISTS替代IN、用NOT EXISTS替代NOT IN
在许多基于基础表的查询中, 为了满足一个条件, 往往需要对另一个表进行联接. 在这种情况下, 使用EXISTS(或NOT EXISTS)通常将提高查询的效率.
在子查询中, NOT IN子句将执行一个内部的排序和合并. 无论在哪种情况下, NOT IN都是最低效的(因为它对子查询中的表执行了一个全表遍历).
为了避免使用NOT IN, 我们可以把它改写成外连接(Outer Joins)或NOT EXISTS.
例子:
高效: SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND EXISTS (SELECT ‘X’ FROM DEPT WHERE DEPT.DEPTNO = EMP.DEPTNO AND LOC = ‘MELB’)
低效: SELECT * FROM EMP (基础表) WHERE EMPNO > 0 AND DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = ‘MELB’)
购物车业务分析:
1)添加商品到购物车
a)从cookie中获取购物车列表
b)判断:此商品是否在购物车(cookie)存在。
i.如果存在,此商品数据库相加
ii.如果不存在,直接添加
c)把购物车写回cookie
2)查询购物车
a)展示购物车列表
3)修改购物车
a)修改购物车商品数量
b)修改商品价格
4)删除购物车
任一项目登录
a)接受页面用户名,密码
b)根据用户名查询数据库
c)校验此用户在系统中是否存在,密码需要进行md5加密校验
d)校验成功,登录成功。需要把用户身份信息放入redis系统。生成token(我们这个token是用的UUID),token相当于SESSION里面jsessionid,token就是redis的key。
e)返回token
f)把token写入cookie(顶级域名中),实现多系统之间共享。实现了单点登录。
紧接着其他项目登录
根据token查询redis服务器用户身份信息
a)如果cookie没有token,重新登录
b)如果cookie中有token,redis服务器过期,重新登录
c)登录,重置redis中用户身份认证过期时间。
跨域问题:
i.跨服务(不需要发请求)
ii.跨域名(需要发送请求)[表现层与表现层交互]
1.不能接受数据(普通Json数据)
2.接受js代码(callback(json))
iii.Jsonp(ajax的dataType为jsonp)
网页静态化
为什么网页静态化:使用Freemarker技术,生成静态化页面,用户不需要访问后台服务器,只需要访问html页面即可。访问只是单纯html页面,只是和存放静态化页面的服务器有交互,和后台淘淘商城没有交互。大大减轻服务器压力
秒杀活动技术挑战:
1,对现有网站业务造成冲击
秒杀只是一个附加活动,具有时间短,并发大的特点,如果和正常网站放到一块,可能导致整站瘫痪。
2,高并发下应用和数据库负载
因为用户会不断刷新页面,类似12306抢票,这些请求如果安装一般网站设计,对应用服务器和数据库造成极大的负载压力。
3,网络带宽压力
假如返回一个页面是200K,包括HTML和图片JS等,那么一万个人访问就是200K*10000,2G的带宽流量。这种压力,做过运维的应该清楚。
4,直接下单
秒杀规则就是开始后才能下单,之前只能浏览。下单页面也是一个URL,如果有人破解,不用秒杀就可以下单了。当然,直接下单后后端也必须判断(这话是我加的)。
秒杀系统应对策略
1,秒杀系统独立部署
蜂拥而至的用户访问,如果秒杀系统没有独立部署,会拖垮整个网站。一般我们的Java系统都是分布式部署,这里的独立部署不是简单的把特定请求指向特定服务器,我们甚至可以用二级域名来处理。
2,页面静态化
一般我们做java的,页面就是JSP,但是每个JSP都是动态生成HTML的,如果按照这个思维做秒杀网站,光JSP就能干掉自己的网站。不过现在互联网网站前端都是PHP的,JSP也只有内部系统用了。但是浏览器需要的就是一个HTML,我们在服务器放一个静态页面,不用任何程序处理。
3,租借秒杀活动网络带宽
服务器带宽对外网的话,其实是很贵的。所有之前有很多虚拟空间来做网站,大家公用带宽,节省资本。在比如我的论坛,虽然是独立服务器,但带宽其实很坑的。这里就要和运营商合作,甚至我们可以把静态页面放到CDN。
4,动态的下单URL
为了防止用户直接下单,下单URL应该是动态生成的,而且只有在活动开始的时候外层才能知道。
秒杀系统架构涉及
1,如何控制商品页面购买按钮点亮
购买按钮在活动开始的时候才能点击,在此之前是灰色。如果页面是动态的,那么可以在活动开始的时候改变这个页面,但是上面也说了,页面都存到CDN了,请求不会到应用服务器。
方法是用JS控制,在静态页面引入一个JS,JS文件中有是否开始的标记,和下单的URL。这个JS文件不被浏览和CDN和反向代理缓存。该文件还要非常小,避免对集群服务器造成带宽压力。
2,如果允许第一个提交的订单能下单
如果能秒杀成功的只有一个人,那么提交订单的时候,就得检查是否有订单提交。所以要控制下单页面入口,只有少数用户能进入下单页面,其他直接进入活动结束页面,和秒杀页面一样就是一个简单HTML。
以上,是别人书上说的,大体意思是这样的,我稍加修改让意思更明了。
那么其实他说的是整体思想,如果涉及到实现的时候,主要还是要看活动开始后,下单时这个抢的动作。
资源数量是特定的,大家都来抢了,怎么保证只有前几个能抢到呢?这里就涉及到了线程的问题,我面试的时候竟然说了用消息队列,往队列里面放特定数量消息,客户端消费完了就是抢光了。其实这是有问题的,这和消息队列的特性有关,另外这个时候效率也会有问题。
后来我又想了一下,简述一下我的思路。
有两个标量,1:是否开始。2:当前资源数量。3:是否结束。
刚才说的是JS级别控制了开始,那么其实在网站的后台,请求来了以后也要判断是否开始,防破解。另外要判断是否结束,从这里把运气不好的用户挡住。
如果活动是开始有效的,请求一个特定的接口,接口是线程安全的。
接口首先判断是否有资源,没有返回空,并设置活动结束。如果有则获取并把资源数量减一,返回该资源。
那么此时一个问题,该接口是线程安全的,大量用户访问的时候会不会堵塞?
因为这个接口的处理是足够快的,另外部分用户在是否结束判断时挡住了,我认为不会堵塞住。
框架就是别人说的那么回事,抢的这个操作,我想的是这样的。当然不一定是最好的方案,都说了是探讨。
FastDFS是一个开源的分布式文件系统,她对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件
为载体的在线服务,如相册网站、视频网站等等。
FastDFS服务端有两个角色:跟踪器(tracker)和存储节点(storage)。跟踪器主要做调度工作,在访问上起负载均衡的作用。
FastDFS架构包括 Tracker server和Storage server。客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。
Tracker server作用是负载均衡和调度,通过Tracker server在文件上传时可以根据一些策略找到Storage server提供文件上传服务。可以将tracker称为追踪服务器或调度服务器。
Storage server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己的文件系统而是利用操作系统 的文件系统来管理文件。可以将storage称为存储服务器。
如下图:
Tracker 集群
FastDFS集群中的Tracker server可以有多台,Tracker server之间是相互平等关系同时提供服务,Tracker server不存在单点故障。客户端请求Tracker server采用轮询方式,如果请求的tracker无法
提供服务则换另一个tracker。
Storage集群
Storage集群采用了分组存储方式。storage集群由一个或多个组构成,集群存储总容量为集群中所有组的存储容量之和。一个组由一台或多台存储服务器组成,组内的Storage server之间是平等关系,
不同组的Storage server之间不会相互通信,同组内的Storage server之间会相互连接进行文件同步,从而保证同组内每个storage上的文件完全一致的。一个组的存储容量为该组内存储服务器容量最
小的那个,由此可见组内存储服务器的软硬件配置最好是一致的。
采用分组存储方式的好处是灵活、可控性较强。比如上传文件时,可以由客户端直接指定上传到的组也可以由tracker进行调度选择。一个分组的存储服务器访问压力较大时,可以在该组增加存储服
务器来扩充服务能力(纵向扩容)。当系统容量不足时,可以增加组来扩充存储容量(横向扩容)。
**********************************************************************************************************************************
*****前端*****前端*******前端*****前端*****前端*****前端*****前端*****前端*****前端*****前端****前端****前端****前端****前端******
**********************************************************************************************************************************
前端都用过什么框架?
jquery,easyUI,boostrap(面试官问:"就这些吗?","是的,因为我主要做一些传统项目,都是一些老的项目,技术什么的都比较老,像easyui当时就是很新颖的框架了")
jquery都有哪些方法?
val(),html(),css(),addClass(),hide(),show(),append()[追加方法],live()[追加方法],toggle()【切换方法】,find(),each(),parent()[父亲辈分的],parents()[父亲的父亲,爷爷辈分的]
next(),prev()[上一个同级元素],siblings()【方法返回被选元素的所有同胞元素】,first()[获取元素列表的第一个元素],last()[获取元素列表的最后一个元素],eq()【返回被选元素中带有指定索引号的元素】
attr()【获取属性】,text()
css有哪些选择器?
类选择器,ID选择器,标签选择器
-------------------last updated time is 2017-03-12 by yangwenxue,soon,and continue to update-------------------