mysql使用的一些优化建议? (1)尽量避免使用join操作。因为使用join操作会增加锁竞争的风险,同时会给日后的数据库拆分带来不便,另外性能不一定比拆分成多条语句高。 (2)需要谨慎的考虑在varchar字段上建立索引,适当的时候可以考虑使用前缀索引。 (3)合理的使用 order by,避免出现using filesort。 (4)利用覆盖索引来进行查询操作,避免回表。 (5)利用延迟关联优化超多分页场景。
mysql中limit的优化方案? 在mysql中我们经常会遇到使用limit m,n进行分页的场景,当limit m,n中m越来越大时,分页查询的性能就会越来越差。一般我们会有两种方式解决这个问题,第一种是每次在查询时都将上次查询时的最后一条记录的主键id传入,然后利用主键id在去查下一批数据,但这种方式不是很好使用,因为它的限制比较多,面对需要跳转到某个特定页面的场景根本无法处理。其实还存在一种写法即mysql实例在实际使用中常按功能被划分为三层:(1)客户端层:负责处理连接、安全认证等等(2)服务器层:包含了mysql很多核心服务,包括sql语句的解析优化、执行计划的制定执行、存储过程的实现、触发器的实现、所有内置函数的实现、binlog的生产等等所有跨存储引擎的服务都在本层实现(3)储引擎层:负责mysql中数据的存储和提取。 limit m,n之所以性能比较差,是因为limit m,n的实现是由服务器层完成,存储引擎收到的命令只是获取满足where条件的m条记录,这一点和mysql5.6之前的“索引截断”问题是一样的,在idx_a_b_c的索引中若查询语句为where a = xxx and b > xxx and c = xxx,则服务器层在经过分析后只会把条件where a = xxx and b > xxx传递给存储引擎层,最后在服务器层根据c = xxx进行条件过滤,这样就会导致数据库读取了很多无用的数据,这个问题在mysql5.6后引入ICP技术得到了解决,但是limit m,n还是存在着相同的问题,每次都需要多读很多数据,随着m值的越来越大,语句的查询性能就越来越差。 上述语句之所以能够大幅度减少语句的执行时间,是因为子语句select id from table where XXXX limit m,n使用了覆盖索引,只会使用辅助索引查询m条主键id,然后从m条主键索引中取出n条主键id,最后实际只有n条记录会回表查询,这样就大大减少了数据库的IO操作,也就减少了语句的执行时间。
redis持久化的实现方式? redis的数据都保存在内存里,如果系统宕机就会导致数据丢失,redis通过其持久化机制来保障数据不会丢失。redis的持久化机制有两种:RDB快照、AOF增量日志,快照是某个时间点数据的二进制全量备份,而AOF日志记录的是修改内存数据的指令记录,两者都存储在磁盘文件中。在重启redis恢复数据时,会先加载RBD快照文件,然后再重放本次快照后的AOF文件。因为redis是单线程的,为了防止阻塞对请求的响应,redis会fork子进程来进行快照持久化,而持久化期间客户端请求进行的内存数据的修改都采取COW(copy on write)的方式。redis在内存修改指令执行后会将指令记录存入AOF文件中,由于是文件的形式存储的,所以需要使用fsync命令,确保将数据刷入磁盘,为了保证性能,redis通常每1s执行一次fsync命令。
Spring Bean的加载过程? 个人习惯将spring的启动过程分为三个阶段:容器初始化阶段、bean实例化阶段、bean初始化阶段。 (1)容器初始化阶段:该阶段主要是初始化容器上下文环境,检验系统属性和环境变量。这一阶段会注册所有的beanPostProcessor,同时会执行所有的beanFactoryPostProcessor,根据scanning-path扫描所有@Component、@Service等注解标识的类,并将其封装成BeanDefinition存储起来以便后续使用。 (2)bean实例化阶段:该阶段主要是选定合适的构造器构造对象实例,构造出的对象实例仍然需要进行属性的依赖注入。此阶段还会进行循环依赖的检查和处理,还会检查是否@DependsOn依赖的必须构建的实例并进行构建,最终会根据合适的构造函数通过反射或者cglib来构造一个实例对象。 (3)bean初始化阶段:该阶段主要是进行依赖的注入和执行自定义的初始化逻辑,同时也会注册对象实例的销毁逻辑。首先会处理所有的Aware接口,然后执行@PostConstruct定义的方法,之后执行InitializingBean接口的afterPropertiesSet方法,再之后执行自定义的init-method,再然后为对象实例生成动态代理,最后检查是否存在循环引用问题及注册bean销毁的回调接口。
Spring Bean的循环依赖问题? 在spring中使用三个HashMap处理循环依赖问题(也有人称之为"三级缓存"),这三个HashMap分别为:singletonObjects、singletonFactories、earlySingletonObjects。在每次bean实例化好后都会将获取该bean的工厂对象存入singletonFactories中,如果其他bean在实例化时若依赖该bean就会首先从singletonObjects中获取,获取不到就会去earlySingletonObjects中获取,在获取不到就会从singletonFactories中获取该bean的工厂对象,通过工厂对象获取该bean。之所以使用一个工厂对象是为了通过beanPostProcessor在不同场景下能够返回不同的”半成品“bean。目前spring中循环依赖主要有三种情况: (1)prototype类型的bean不支持循环依赖 (2)singleton类型的bean在使用构造器注入时不支持循环依赖。在使用构造器注入时,会先通过getBean()方法实例化构造器中的参数对象,如果参数对象又通过构造器注入当前bean,就会产生循环依赖,甚至可能产生死循环,spring在bean构建时会将其放入正在构建中的set集合中,如果出现重复构建就会抛出异常,spring就是通过这种方式解决构造器的循环依赖问题的。 (3)singleton类型bean使用field注入(即@Autowired或@Resource注入)时,在没有动态代理时支持循环依赖。当进行动态代理时分为两种情况:(1)使用普通切面生成代理支持循环依赖、(2)使用@Async等注解生成代理则不支持循坏依赖。这主要是因为使用普通代理时,在当前bean构建中若有其他bean想要注入该bean,那么普通代理会检查当前bean是否有代理切面,若存在的或就为该bean生成代理,并将代理对象返回。而使用@Async等注解在遇到这种情况时,就会直接将当前bean实例返回,最后在初始化bean后生成代理对象,这时其他bean获取的该bean引用是原生对象,而不是该bean的代理,这时spring就会抛出异常。
Spring Bean的生命周期? 目前spring容器中的bean主要有两种类型:singleton类型、prototype类型。其中singleton类型的bean几乎与spring容器拥有相同的生命周期,singleton类型的bean在容器中只有一个实例,所有对该对象的引用都共享这个实例,该实例在容器启动被第一次初始化后,将一直存活到容器退出。而prototype类型的bean,容器在收到请求时,每次都会返回一个新的对象实例给请求方,和new有点像,但是会进行依赖注入和代理操作,对象实例在返回给请求方后,容器就不在拥有该对象的引用,它的生命周期取决于应用方,在应用方使用完成后就有可能被jvm回收掉。
Spring IoC实现原理? 目前我们常用的spring容器是ApplicationContext,由容器来帮我实现具体的依赖注入。常用的依赖注入方式有两种:构造器注入、setter方法注入,目前spring官方推荐的是使用构造器注入,这主要是因为构造器注入能够保证对象在实例化后就能够正常的使用,而当我们没有使用构造器注入时,如果我们使用new操作去构建对象,构建出的对象不会进行依赖注入,使用时就有可能导致npe异常,而构造器注入就能够避免这个问题,但是构造器注入的写法很麻烦,在依赖的对象很多时代码就会很臃肿,同时使用构造器注入是不能够支持循环依赖的,同时在spring项目中直接new对象的逻辑也不多,所以还是可以直接使用setter注入的。setter注入实际就是我们常用的@Autowired、@Resource注解的注入方式,容器在对象进行依赖注入前就会在对象信息类BeanDefination中扫描出所有@Autowired、@Resource注解标识的属性字段,然后通过getBean()方法获取相应的实例,再通过反射将实例引用set给对应的属性字段。通过构造器注入的方式,在对象实例化时会选择合适的构造器,然后通过getBean获取所有的构造器参数对象,最后利用构造器通过反射或者cglib来生成对象实例,在利用对象实例进行后续的初始化和动态代理等操作。
Spring AOP实现原理? spring实现了自己的AOP框架(spring AOP),同时在spring2.0版本中也集成了AspectJ,spring的动态代理主要由两种实现方式:JDK动态代理和cglib动态代理。动态代理能够动态的为目标对象成代理对象,其中JDK动态代理需要目标对象实现特定的接口,而cglib动态代理主要是通过动态的生成目标对象的子类来覆写目标对象的方法。jdk动态代理需要实现InvocationHandler接口,同时通过Proxy类生成相应的代理对象,而cglib动态代理需要实现MethodInterceptor接口,并通过Enhancer类生成特定代理对象。由于是通过继承来实现代理对象,所以cglib动态代理的代理方法不能够使用final、private修饰,否则将导致代理失效,而一旦代理时效,接口的请求将会直接进入代理对象的方法中,因为生成的代理对象是不会进行依赖注入的,所以如果该方法内需要使用其他对象,那就会出现NPE异常,所以在使用cglib动态代理时一定要注意代理方法不能使用final修饰(private修饰的方法只在类内可见,外部对象无法使用,所以这里没提private)。在spring实现的cglib动态代理中,实际上对代理对象的所有接口调用,无论该接口是否有代理,都会先进入MethodInterceptor接口的intercept方法中,spring在intercept方法中会判断相应的接口是否有切面逻辑,如果有的话就执行相应的逻辑,否则就会调用目标对象的原生方法执行。实际上spring的动态代理存在一点的瑕疵,就是在我们对同一个类的接口A和接口B都进行代理时,若接口A中调用了接口B,那么当外部对象调用接口A时,只有接口A会执行切面逻辑,而接口A内部的接口B不会执行切面逻辑,这时因为接口A内保存的是原生对象的引用,所以在接口A内调用接口B,调用的是原生对象的接口B。如果想要解决这种问题需要让原生对象持有它的代理对象的引用,可以通过AopContext.currentProxy()方法获取原生对象当前的代理对象。 Spring Aop主要由几大组件构成,其中主要包括:Joinpoint、Pointcut、Advice、Advisor。其中Joinpoint指的是切面逻辑需要切入的点,在Spring Aop中目前只支持方法调用类型的切入点,而Pointcut就是切入点的具体表达式,我们通过Pointcut可以匹配到具体的Joinpoint。Advice指的是需要切入的具体逻辑,有多种类型的Advice,包括Before Advice、After Advice以及全能的Around Advice。Advisor是Spring Aop切面的概念实体,一般包含一个Pointcut和多个Advice。在同一个Advisor中,若存在多个Advice,则会按照在文件中的出现顺序先后执行,若在不同Advisor中的Advice都匹配到了同一个Joinpoint,那么此时需要通过@Order来标识相应切面类的优先级来指定执行顺序,否则Advice的执行顺序是不确定的。所以一定要注意:如果程序对切面的执行顺序有要求,一定要使用@Order注解标识切面的执行顺序。 Spring在对第一个对象实例化前就会解析所有使用@Aspect修饰的类,并通过反射获取Pointcut和Advice,最后生成Advisor存储起来,在对象初始化完成后,会检测是该对象是否匹配特定的Pointcut,并对需要代理的对象通过cglib或反射生成动态代理。
http和https的区别?https如何保证数据安全? HTTPS全称HTTP over SSL,简单的说就是在之前的HTTP传输上增加了SSL协议的加密能力,SSL全称安全套接字层,工作于传输层和应用层之间,为应用提供数据的加密传输。在普通的数据传输中,我们通常会在客户端和服务器端使用对称协议进行加密,即两端使用一个相同的秘钥来对传输的数据进行加密和解密。但是由于秘钥同样需要在网络上进行传输,我们仍然需要对秘钥进行保护,为了解决这个问题,我们可以使用非对称加密,就是我们常见的RSA算法,该算法能够生成公钥和私钥,公钥任何人都可见,用来对信息加密,而私钥用来对加密信息进行解密,这种非对称加密算法,加密和解密的耗时都比较长,所以只适合对少量的数据进行加密传输,就刚好可以用来进行对称加密算法的秘钥传输。有了这种加密方式后,整个传输流程看起来安全多了,但是如果在端对端的传输过程中,某一端的传输数据被黑客劫持,那么黑客同样可以自己生成公钥,并和另一端进行数据传输,为了解决这个问题,在使用HTTPS进行数据传输时,需要提供CA办法的数字证书来证明传输者的身份。
Java泛型原理? 泛型的本质是类型参数化,可以解决不确定具体对象类型的问题,如果不使用泛型而使用Object,则在使用不当的情况下可能出现类型转换异常,泛型实际上主要是为了在编译器进行类型检查,保证程序员使用泛型时能够安全的存储数据和使用数据,在完成编译泛型类型就会被擦除,使用泛型的好处:保证类型安全、提升代码的可读性。泛型使用时,约定俗成的符号包括:E代表Element,用于集合中的元素;T代表the Type of object,表示某个类;K代表Key,V代表Value,用于键值对元素。当List和泛型相结合时,可以把泛型的功能发挥到极致。List完全没有类型限制和赋值限制,如果随意使用,可能会造成类型转换异常。List<Object>并不完全等同于List,List<Object>不能接受其他泛型赋值,比如List<Object>不能接受List<Integer>类型变量的赋值,而List却可以。类型List<?>表示接受任何类型的集合赋值,但是赋值之后就不能在随意添加元素了,但仍可以执行remove和clear操作。<? extends T>表示T及T的子类集合,<? super T>表示T及T的父类集合。<? extends T>除了null外不能add任何元素,这主要是因为T及其子类类型很多,无法匹配添加元素的类型,<? extends T>可以进行get操作,但是get的元素类型都是T及其父类。<? super T>可以添加元素,但是只能添加T及T子类的对象,这主要是因为满足了上转型,<? super T>在进行get操作时,虽然能够返回对象,但是只能返回Object类型对象。