mybatis plus相对于直接使用mybatis有哪些优势

mybatis-plus是基于mybatis,相对于mybatis,他有许多特性是比较好用的,比如分页查询、表字段自动转换为实体类属性等,使用mybatis-plus与Spring Data JPA有点相似的地方,个人觉得mybatis-plus的分页比JPA的分页好用。

1.创建实体类,创建实体类与JPA特别相似,其中@TableName注解是为了指定此实体类对应数据库的哪一张表;@TableId指定的是主键,type属性指定的是该主键自增的方式,AUTO代表自增,UUID代表使用UUID增加主键;还有一个注解@TableFiled,此注解主要用在非主键实体属性上,下面是官方给出的此注解所有属性。

————————————————
版权声明:本文为CSDN博主「兮川」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zc_ad/article/details/83301911
2.创建mapper,与JPA有相像之处。使用@Mapper为了把mapper这个DAO交給Spring管理,且可以不再写mapper映射文件;继承BaseMapper可以方便使用mybatis-plus方法,泛型需要写对用的实体类。此处直接使用slelect *  from,mybatis-plus会根据实体类自动驼峰转下划线映射到表的字段中。
其中column-underline: true特别好用,会自动将下划线格式的表字段,转换为以驼峰格式命名的属性。

Mybatis优势:
1、SQL语句可以自由控制,更灵活、性能较高。
2、SQL与代码分离,易于阅读和维护。
3、提供XML标签,支持编写动态SQL语句。
JPA优势:
JPA移植性比较好(Hibernate方言)
提供了很多CRUD方法、开发效率高(不用编写sql语句)
对象化程度更高(面向对象开发思想)
Mybatis劣势:
简单CRUD操作需要编写SQL语句(单表仍需要编写Mapper接口方法和xml的sql)
XML中有大量sql需维护
mybatis自身功能有限
————————————————
版权声明:本文为CSDN博主「一个爱运动的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

Mybatis-plus简介:Mybatis增强工具,只做增强,不作改变,简化开发,提高效率。
1、无侵入:Mybatis-Plus 在 Mybatis 的基础上进行扩展,只做增强不做改变,引入 Mybatis-Plus 不会对您现有的 Mybatis 构架产生任何影响,而且 MP 支持所有 Mybatis 原生的特性
2、依赖少:仅仅依赖 Mybatis 以及 Mybatis-Spring
3、损耗小:启动即会自动注入基本CRUD,性能基本无损耗,直接面向对象操作
4、通用CRUD操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
5、多种主键策略:支持多达4种主键策略(内含分布式唯一ID生成器),可自由配置,完美解决主键问题
6、支持ActiveRecord:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可实现基本 CRUD 操作
7、支持代码生成:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用(P.S. 比 Mybatis 官方的 Generator 更加强大!)
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
8、内置分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于写基本List查询
9、内置性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询
10、内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,预防误操作
lombok作用:提高开发效率,通过注解形式使javabean生成get、set、有参数、无参数、toString等方法,无需手动实现。


建一个mp名的数据库,在建一个user表:
编写实体类:User.java
@Data 
@EqualsAndHashCode(callSuper = false) 
@TableName("user")
编写dao接口:
UserMapper.java:

public interface UserMapper extends BaseMapper<User> {
}
在启动类中添加注解:@MapperScan(“com.mp.first_mp.dao”)进行扫描
我们可以发现相对于使用MyBatis来说,MyBatis-plus不用编写xml文件编写那些繁琐的SQL语句,简单快速的   MyBatis-plus的就到这里

常用注解
mybatis-plus:
主键采用雪花算法生成值的前提是实体类的主键属性名称必须为id。
mybatis-plus:
数据表字段带有_的可以自动映射到驼峰式命名的属性上(t_user——》tUser)。
注解:
1.数据库名不同,在类上增加@TableName(“mp_user”)
2.主键ID的驼峰一般无法识别,在主键属性上增加@TableId
3.属性与字段名不相同,在属性上增加@TableField(“name”)
@TableName(“数据库表名”):
使用场景实体类名称和数据表名不一致时,通过它指定表名,此时就可以使用mp的单表操作。
@TableId(“主键名”):
使用场景实体类属性名称和数据表主键不是id时,通过它声明该属性为主键,就可以采用雪花算法生成主键值操作。
@TableField(“字段名”):
使用场景实体类属性名称和数据表字段名不一致时,通过它指定数据表字段名称,就可以和实体类属性对应。
3、排除非表字段的三种方式
使用场景: 实体类中的某个属性不对应表中的任何字段,只是用于保存临时数据,或者临时组装的数据。
使用方式
1、 transient修饰实体类属性(修饰的属性不会被序列化)。
缺陷:有些需求需要序列化该字段。
2、 static修饰属性(前提手动实现get、set方法,Lombok对静态属性不会提供get、set方法)。
缺陷:每个对象的属性值一致。
3、 @TableField(exist=false),这个注解用来表示数据表中不存在该字段,默认是true。
————————————————
版权声明:本文为CSDN博主「一个爱运动的程序员」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

网关之降级、熔断、限流、隔离、幂等性验证的相关知识简单整理


制作网关项目的好处:

(1)网关层对外部和内部进行了隔离,保障了后台服务的安全性。
(2)对外访问控制由网络层面转换成了运维层面,减少变更的流程和错误成本
(3)减少客户端与服务的耦合,服务可以独立发展。通过网关层来做映射。
(4)通过网关层聚合,减少外部访问的频次,提升访问效率。
(5)节约后端服务开发成本,减少上线风险。
(6)为服务熔断,灰度发布,线上测试提供简单方案。
(7)便于扩展。

从接口安全方面说,它时属于微服务架构的基本功能。微服务架构的特点就是:“一解释就懂,一问就不知,一讨论就吵架”,对于我这个学艺不精的人来说,估计问什么都会吵架。

 

1. 应用场景

假设服务A依赖服务B和服务C,而B服务和C服务有可能继续依赖其他的服务,继续下去会使得调用链路过长,技术上称1->N扇出。
如果在A的链路上某个或几个被调用的子服务不可用或延迟较高,则会导致调用A服务的请求被堵住,堵住的请求会消耗占用掉系统的线程、io等资源,
当该类请求越来越多,占用的计算机资源越来越多的时候,会导致系统瓶颈出现,造成其他的请求同样不可用,最终导致业务系统崩溃,又称:雪崩效应。

 

2.处理方式

降级:在服务器压力剧增的情况下,根据实际业务情况和流量,对一些服务和页面不处理或简单处理,从而释放服务器资源保证核心交易正常运作。  熔断:熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。  限流:限制服务/接口访问的流量。可以从总量+速率进行处理  隔离:保证其中某个服务坏掉,不会影响到其他服务。线程池/信号量隔离模式  幂等性验证: 可能因为网络抖动或其他位置原因,服务端在短时间内可能会受到多笔相同的交易  超时机制:一种是请求的等待超时,一种是请求运行超时。

其中,降级、熔断、隔离属于出现问题后的处理机制。限流属于预防。
降级:在服务器压力剧增的情况下,根据实际业务情况和流量,对一些服务和页面不处理或简单处理,从而释放服务器资源保证核心交易正常运作。 
熔断:熔断一般是指软件系统中,由于某些原因使得服务出现了过载现象,为防止造成整个系统故障,从而采用的一种保护措施,所以很多地方把熔断亦称为过载保护。  
限流:限制服务/接口访问的流量。可以从总量+速率进行处理  
隔离:保证其中某个服务坏掉,不会影响到其他服务。线程池/信号量隔离模式  
幂等性验证: 可能因为网络抖动或其他位置原因,服务端在短时间内可能会受到多笔相同的交易  
超时机制:一种是请求的等待超时,一种是请求运行超时。

其中,降级、熔断、隔离属于出现问题后的处理机制。限流属于预防。


spring boot

SpringBoot是Spring社区发布的一个开源项目,在帮助开发者快速并且更简单的构建项目。它使用习惯优于配置的理念让你的项目快速运行起来,使用Spring Boot很容易创建一个独立运行(运行jar,内置Servlet容器,Tomcat、jetty)、准生产级别的基于Spring框架的项目,使用SpringBoot框架,你可以不用或者只需要很少的配置文件。


SpringBoot核心功能

独立运行的Spring项目:可以以jar包形式独立运行,通过java -jar xx.jar即可运行。

内嵌Servlet容器:可以选择内嵌Tomcat、Jetty等。

提供starter简化maven配置:一个maven项目,使用了spring-boot-starter-web时,会自动加载Spring Boot的依赖包。

自动配置Spring:Spring Boot会根据在类路径中的jar包、类,为jar包中的类自动配置Bean。

准生产的应用监控:提供基于http、ssh、telnet对运行时的项目进行监控。


1.Spring 最初的两大核心功能 Spring Ioc 和 Spring Aop

2.发明 Spring Boot 不是为了取代 Spring ,是为了让人们更容易地使用 Spring 
Spring Boot Starter 是 Spirng Boot 约定优于配置理念的最佳实现。Spring Boot Starter 有两个核心组件:自动配置代码和提供自动配置模块及其它有用的依赖。也就意味着,当项目中引入某个组件的 Starter ,项目启动时就会针对此组件进行默认配置,从而达到“开箱即用”,一般情况下仅需要少量的配置或者不配置即可使用组件对应的功能。
————————————————
版权声明:本文为CSDN博主「lzcWHUT」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

Spring Boot 整合了主流的开源软件形成了一系列的 Starter,让我们有了一致的编程体验来集成各种软件,Spring Boot 在集成的时候做了大量的优化,让我们在集成的时候往往只需要很少的配置和代码就可以完成。可以说各种 Starters 就是 Spring Boot 最大的优势之一。

Spring Cloud 是基于 Spring Boot 开发的一套微服务架构下的服务治理方案。
Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。

Spring Cloud 是为了解决微服务架构中服务治理而提供的一系列功能的开发框架,并且 Spring Cloud 是完全基于 Spring Boot 而开发。Spring Cloud 利用 Spring Boot 特性整合了开源行业中优秀的组件,整体对外提供了一套在微服务架构中服务治理的解决方案。


  1. 用户认证和用户授权


其实用户认证,就是主体在进行身份认证时需要提供身份信息和凭证信息。

其实,用户认证就是在用户登录的时候认证用户是否存在,密码是否正确。在用户登录之后是什么样子,就要靠授权了,首先给不同的用户分配不同的角色,然后给不同的角色授权不同的资源,不同的用户也就有了不同的权限,即可使用或者可看到的资源就会因人因角色而异了。

单点登录的过程,单点登录

单点登录全称Single Sign On(以下简称SSO),是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分
sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。间接授权通过令牌实现,sso认证中心验证用户的用户名密码没问题,创建授权令牌,在接下来的跳转过程中,授权令牌作为参数发送给各个子系统,子系统拿到令牌,即得到了授权,可以借此创建局部会话,局部会话登录方式与单系统的登录方式相同。这个过程,也就是单点登录的原理

使用JWT实现单点登录



Spring 如何解决循环依赖的问题

1. 什么是循环依赖?  what?

   (1)循环依赖-->循环引用。--->即2个或以上bean 互相持有对方,最终形成闭环。

   eg:A依赖B,B依赖C,C又依赖A。【注意:这里不是函数的循环调用是个死循环,除非有终结条件】,是对象相互依赖关系

 ①:构造器的循环依赖。【这个Spring解决不了

StudentA有参构造是StudentB。StudentB的有参构造是StudentC,StudentC的有参构造是StudentA ,这样就产生了一个循环依赖的情况,

②【setter循环依赖】field属性的循环依赖【setter方式 单例,默认方式-->通过递归方法找出当前Bean所依赖的Bean,然后提前缓存【会放入Cach中】起来。通过提前暴露 -->暴露一个exposedObject用于返回提前暴露的Bean。】

 如何检测是否有循环依赖?how to  find?

   可以 Bean创建的时候给其打个标记,如果递归调用回来发现正在创建中的话--->即可说明循环依赖。


 Spring的单例对象初始化主要分为三步: 
  ①:createBeanInstance:实例化,其实也就是 调用对象的构造方法实例化对象

    ②:populateBean:填充属性,这一步主要是多bean的依赖属性进行填充

    ③:initializeBean:调用spring xml中的init() 方法。

从上面讲述的单例bean初始化步骤我们可以知道,循环依赖主要发生在第一、第二步。也就是构造器循环依赖和field循环依赖。

那么我们要解决循环引用也应该从初始化过程着手,对于单例来说,在Spring容器整个生命周期内,有且只有一个对象,所以很容易想到这个对象应该存在Cache中,
Spring为了解决单例的循环依赖问题,使用了三级缓存。

  
调整配置文件,将构造函数注入方式改为 属性注入方式 即可
————————————————
版权声明:本文为CSDN博主「Zeus_龙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

 singletonFactories : 单例对象工厂的cache 
 earlySingletonObjects :提前暴光的单例对象的Cache 。【用于检测循环引用,与singletonFactories互斥

 singletonObjects:单例对象的cache

Spring首先从一级缓存singletonObjects中获取。如果获取不到,并且对象正在创建中,就再从二级缓存earlySingletonObjects中获取。如果还是获取不到且允许singletonFactories通过getObject()获取,就从三级缓存singletonFactory.getObject()(三级缓存)获取

这里就是解决循环依赖的关键,这段代码发生在createBeanInstance之后,也就是说单例对象此时已经被创建出来(调用了构造器)。这个对象已经被生产出来了,虽然还不完美(还没有进行初始化的第二步和第三步),但是已经能被人认出来了(根据对象引用能定位到堆中的对象),所以Spring此时将这个对象提前曝光出来让大家认识,让大家使用。


让我们来分析一下“A的某个field或者setter依赖了B的实例对象,同时B的某个field或者setter依赖了A的实例对象”这种循环依赖的情况。A首先完成了初始化的第一步,并且将自己提前曝光到singletonFactories中,此时进行初始化的第二步,发现自己依赖对象B,此时就尝试去get(B),发现B还没有被create,所以走create流程,B在初始化第一步的时候发现自己依赖了对象A,于是尝试get(A),尝试一级缓存singletonObjects(肯定没有,因为A还没初始化完全),尝试二级缓存earlySingletonObjects(也没有),尝试三级缓存singletonFactories,由于A通过ObjectFactory将自己提前曝光了,所以B能够通过ObjectFactory.getObject拿到A对象(虽然A还没有初始化完全,但是总比没有好呀),B拿到A对象后顺利完成了初始化阶段1、2、3,完全初始化之后将自己放入到一级缓存singletonObjects中。此时返回A中,A此时能拿到B的对象顺利完成自己的初始化阶段2、3,最终A也完成了初始化,进去了一级缓存singletonObjects中,而且更加幸运的是,由于B拿到了A的对象引用,所以B现在hold住的A对象完成了初始化。

知道了这个原理时候,肯定就知道为啥Spring不能解决“A的构造方法中依赖了B的实例对象,同时B的构造方法中依赖了A的实例对象”这类问题了!因为加入singletonFactories三级缓存的前提是执行了构造器,所以构造器的循环依赖没法解决

Spring五个事务隔离级别和七个事务传播行为






单例模式下synchronize加锁后为什么要用volatile

解决并发共享变量问题需要解决三个问题:原子性、可见性、有序性

synchronize:保证原子性、可见性、有序性(多线程顺序执行)

volatile:保证可见性、有序性(禁止指令重排序)

public class Singleton {
    private static volatile Singleton s;//1,volatile修饰的必要性
    private Singleton(){};//必须是private
    public static Singleton getInstance() {
        if(s == null) {//2
            synchronized (Singleton.class) {//3
                if(s == null) {//4
                    s = new Singleton();//问题关键:步骤5分为三步,存在指令重排可能性
                }
            }
        }
        return s;//6
    }
}
位置5可分为三个步骤:

1、memory=allocate();// 分配内存 相当于c的malloc
2、ctorInstanc(memory) //初始化对象
3、s=memory //设置s指向刚分配的地址

由于synchronize不保证单个线程内部指令的顺序,比如线程A在位置5的执行顺序有可能是1-2-3,也有可能是1-3-2,如果是1-3-2的情况,线程A执行完步骤3给s对象分配了地址;此时来个线程B,那么在位置2判断s就不为null,但是此时线程A还没有初始化对象,就会导致线程B拿到的s出错,线程A不会出错。

所以位置3加锁后,只能保证多线程串行执行加锁部分的代码块,不能保证位置5里面的指令执行顺序,所以对象s用volatile修饰 ,禁止指令重排序,从而保证了代码执行的正确性。

总结:步骤1用volatile修饰对象,就是为了步骤5禁止指令重排
————————————————
版权声明:本文为CSDN博主「章绍龙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shaolong1013/article/details/94824574


微服务

此时的服务调用将分为两个过程:

  1. Server A 去服务注册中心拿到 Server B 的地址,如果 Server B 是单机部署,这个地址就只有一个,如果 Server B 是集群化部署,这个地址就有多个;
  2. 拿到 Server B 的地址之后,Server A 再去调用 Server B。

创建一个 Provider 作为消息提供者
,这就是一个普通的 Spring Boot 工程,创建时候注意添加Web依赖以及 Eureka Discovery 依赖

三行配置含义分别如下:

  • spring.application.name 表示当前服务的名字,这个名字将作为服务的标记存储在Eureka上,当其他服务需要调用这个服务的时候,都是通过这个名字来查找服务。
  • server.port 当前服务的端口。
  • 最后一个地址表示当前服务需要注册到的服务注册中心地址,这里需要注意,如果服务注册中心是一个集群,这里也可以只写集群中的一个节点,Eureka 集群会自动进行服务同步。

在微服务中,只要当前项目的 classpath 下存在 spring-cloud-starter-netflix-eureka-client 依赖,并且提供了 eureka 注册中心的地址,该服务就会自动注册到 Eureka Server 上。

然后在 provider 中提供一个 /hello 接口,供其他服务调用,


在 application.properties 中添加如下配置:









主要调用逻辑
步骤如下:

  1. 服务提供者启动hsf容器,上报服务信息到服务注册中心.
  2. 服务提供方上报元数据信息。
  3. 服务消费方启动hsf容器,拉取提供者信息.
  4. 拉取配置服务信息进行调用。






HTTP1.0、HTTP1.1 和 HTTP2.0 的区别





udp如何实现可靠性传输

TCP(TransmissionControl Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

 UDP是User Datagram Protocol,一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。可靠性由上层应用实现,所以要实现udp可靠性传输,必须通过应用层来实现和控制。



UDP它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

         传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。

         实现确认机制、重传机制、窗口确认机制。

如果你不利用Linux协议栈以及上层socket机制,自己通过抓包和发包的方式去实现可靠性传输,那么必须实现如下功能:

         发送:包的分片、包确认、包的重发

         接收:包的调序、包的序号确认

         目前有如下开源程序利用udp实现了可靠的数据传输。分别为RUDP、RTP、UDT。




TCP协议—如何提高数据发送

1.滑动窗口
TCP协议中,由于一发一收的方式性能较低,那么我们一次发送多条数据,就可以大大提高性能(将多个段的等待时间重叠在一起)

窗口大小指的是无需确认应答而可以继续发送数据的最大值,上图窗口大小就是4000个字节(四个段)
发送前四个段,不需要等待任何ACK,直接发送
收到第一个ACK,滑动窗口向后移动,继续发送第五个段的数据,依次类推
操作系统为了维护这个滑动窗口,需要开辟一个发送缓冲区来记录当前没有应答的数据,而确认应答过的数据,会从缓冲区删除
窗口越大,则网络的吞吐率越高
数据包到达,ACK丢了    这种情况下,部分ACK丢了不要紧,可以通过后续ACK进行确认;


当某一段报文段丢失后,发送端会一直收到1001这样的ACK,当发送端主机连续收到了三次同样的”1001“这样的应答,就会重发对应的数据1001—2000。
此时接收端收到了1001之后。再次返回的ACK就是7001了(因为2001——7000)接收端其实之前就已经收到了,被放到了接收端操作系统内核的接收缓冲区中,这种机制被称为”高速重发机制“(快重传)。

————————————————
版权声明:本文为CSDN博主「浩然849」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_56763572/article/details/119659186

2.流量控制。
由于接收端处理数据是有限的,如果发送端发的太快,导致接收端缓冲区满了,这时发送端继续发送数据将会导致丢包,继而会引起一系列重传反应,因此TCP根据接收端的处理能力来决定发送的速度,这就是流量控制。

接收端将自己可以接受的缓冲区大小放入TCP首部中的“窗口大小”字段,通过ACK段通知发送端窗口的字段越大,网络吞吐量越高。
接收端一旦发现自己快满了,就会将窗口大小设置成一个更小的值传给发送端,发送端接收到这个数据后就会减缓发送速度。
如果接收端缓冲区满了,此时窗口大小设置为0,发送端将不会在发送数据,而是定期发送一个窗口探测数据段,使接收端将窗口大小告诉发送端。

3.拥塞控制
核心就是防止开始传输时由于传输数据过大,网络不好而引起的大量丢包问题,引入慢启动机制,开始先发送少量数据看网络是否堵塞,然后在决定以多大的速度传输数据。

引入一个概念称为拥塞窗口,发送开始的时候, 定义拥塞窗口大小为1,每次收到一个ACK应答, 拥塞窗口加1。
每次发送数据包的时候, 将拥塞窗口和流量控制的窗口大小做比较, 取较小的值作为实际发送的窗口。
拥塞窗口是按指数增加的,为了避免拥塞窗口增长的太快,引入一个慢启动阈值,当拥塞窗口超过这个阈值的时候,变为线性增长。
慢启动开始的时候,阈值等于窗口的最大值,每次超时重传时阈值变为原来的一半,拥塞窗口置回1。
当TCP开始通信的时候,网络吞吐量开始上升,随着网络拥堵,吞吐量立刻下降。

拥塞控制归根结底就是 TCP协议尽可能的将数据传输给对方,但又要避免给网络造成太大的压力的折中方案。

4.延迟应答
如果接收端接收到数据就返回一个ACK的话,此时返回的ACK就会很小,比如说:如果接收端缓冲区为800KB的话,发送一个400KB的数据,立即返回的话窗口大小只有400KB,但是延迟一会再发的话,缓冲区可能就会把这400KB的数据处理掉了,这时返回的窗口大小就为800KB,极大的提高传输效率。
但并不是所有的包都可以延迟应答,他会收到数量限制和时间限制,每隔一定数量的包就应答一次,超过最大延迟时间就应答一次。

5.捎带应答
在延迟应答基础上,客户端服务器在应用层上也是“一发一收的”,那么此时ACK就可以搭顺风车和服务器回应的数据一起回给客户端。

Linux删除指令

使用rm -rf 目录名字 命令即可

-r 就是向下递归,不管有多少级目录,一并删除
-f 就是直接强行删除,不作任何提示的意思

eg

删除文件夹实例:rm -rf /var/log/httpd/access
将会删除/var/log/httpd/access目录以及其下所有文件、文件夹

删除文件使用实例:rm -f /var/log/httpd/access.log
将会强制删除/var/log/httpd/access.log这个文件


深翻页

MySQL的limit工作原理就是先读取前面n条记录,然后抛弃前n条,读后面m条想要的,所以n越大,偏移量越大,性能就越差。

为了实现分页,每次收到分页请求时,数据库都需要进行低效的全表扫描。

“ 什么是全表扫描?全表扫描 (又称顺序扫描) 就是在数据库中进行逐行扫描,顺序读取表中的每一行记录,然后检查各个列是否符合查询条件。这种扫描是已知最慢的,因为需要进行大量的磁盘 I/O,而且从磁盘到内存的传输开销也很大。

你要在本地保存上一次接收到的主键 (通常是一个 ID) 和 LIMIT,而不是 OFFSET 和 LIMIT,那么每一次的查询可能都与此类似。

为什么?因为通过显式告知数据库最新行,数据库就确切地知道从哪里开始搜索(基于有效的索引),而不需要考虑目标范围之外的记录。

要使用这种基于游标的分页,需要有一个惟一的序列字段 (或多个),比如惟一的整数 ID 或时间戳,但在某些特定情况下可能无法满足这个条件。

如果我们的表没有主键,比如是具有多对多关系的表,那么就使用传统的 OFFSET/LIMIT 方式,只是这样做存在潜在的慢查询问题。我建议在需要分页的表中使用自动递增的主键,即使只是为了分页。



避免深分页查询,去除掉最后一页,以及指定页码访问这种操作,降低查询带来的影响。

在查询页面每次记录下头尾的主键id,在查询条件中增加主键范围查找,避免深分页。

一般正常查看列表不会有去看最后一页的这种需求,像天猫,淘宝,京东,搜索商品也是只能点击下一页,也没有跨页去查找的这种功能。通常都会有指定的条件去筛选出需要的结果。



跳页
如果LIMIT m,n不可避免的话,要优化效率,只有尽可能的让m小一下,我们扩展前面的clue做法,还是SELECT * FROM message ORDER BY id DESC,按id降序分页,每页20条,当前是第10页,记录当前页条目id最大的是2519,最小的是2500;【加粗字体是精髓!!!!】

原理还是一样,记录住当前页id的最大值和最小值,计算跳转页面和当前页相对偏移,由于页面相近,这个偏移量不会很大,这样的话m值相对较小,大大减少扫描的行数。
【其实传统的limit m,n,相对的偏移一直是第一页,这样的话越翻到后面,效率越差,而上面给出的方法就没有这样的问题。】

注意SQL语句里面的ASC和DESC,如果是ASC取出来的结果,显示的时候记得倒置一下。

Elasticsearch

1、Elasticsearch和MongoDB/Redis/Memcache一样,是非关系型数据库

是一个接近实时的搜索平台,从索引这个文档到这个文档能够被搜索到只有一个轻微的延迟,企业应用定位:采用Restful API标准的可扩展和高可用的实时数据分析的全文搜索工具

2、可拓展:支持一主多从且扩容简易,只要cluster.name一致且在同一个网络中就能自动加入当前集群;本身就是开源软件,也支持很多开源的第三方插件。

3、高可用:在一个集群的多个节点中进行分布式存储,索引支持shards和复制,即使部分节点down掉,也能自动进行数据恢复和主从切换

4、采用RestfulAPI标准:通过http接口使用JSON格式进行操作数据。

5、数据存储的最小单位是文档,本质上是一个JSON 文本:

实际项目开发中,几乎每个系统都会有一个搜索的功能,数据量少时可以直接从主数据库中比如Mysql搜索。

但当搜索做到一定程度时,比如系统数据量上了10亿、100亿条的时候,传统的关系型数据库的I/O性能和统计分析性能就难以满足用户需要了。

所以很多公司都会把搜索单独做成一个独立的模块,用ElasticSearch等来实现。

虽然内存缓存数据库的读写性能很高,但完全把数据放在内存中是不太现实的,比如到PB级别的数据,按照每个节点96G内存计算,

在内存完全装满的数据情况下,需要的机器是:1PB=1024T=1048576G ,节点数就是1048576/96=10922个 ,再考虑到数据备份,节点数还需要翻倍,成本巨大决定了其不现实!







mysql中怎么样可以实现:有一个数据存在的时候执行查找操作,该数据不存在的时候执行插入操作

  • MySQL插入一条数据,如果数据已存在就跳过,不存在就插入:使用insert ignore语句:(要求必须有唯一字段参与匹配,该唯一字段可以为主键)
    insert ignore into dept(deptno) values(20); # 如果已存在deptno为20的行,则跳过不执行操作,否则执行插入(注意deptno必须唯一或为主键

  • 插入一条数据,如果数据存在则更新,不存在则插入(必须要有唯一字段或主键),使用 insert...on duplicate key update语句,例如:
    insert into dept(deptno,dname) values(30,'null') on duplicate key update dname='xiaoming';
    该条语句,如果已经存在了对应的deptno(deptno为unique或者主键),那么就执行后面的update操作,即其就等价于:
    update dept set dname='xiaoming' where deptno=30;
    如果并不存在对应的deptno,则执行insert操作,即相当于只存在on 关键字前面半部分。
    如果最终执行的是行插入操作,则该条语句执行结果中返回的受影响的行数为1;如果是原有的记录被更新,则返回的受影响的行的值为2;

  • Replace关键字的使用:Replace关键字最主要的作用就是可以将delete和insert操作合二为一,实现一个原子操作。在使用Replace时,表中必须存在唯一索引,即存在unique字段,且该字段不允许为空值。
    replace into dept(deptno,dname,loc) values(50,'xiaohong','BeiJing');
    当deptno=50存在时,就会执行delete这条记录,并重新insert一个新的记录,新纪录dname和loc为当前设定的值;当deptno=50不存在时,则直接执行插入操作;
    在执行REPLACE后,系统返回了所影响的行数,如果返回1,说明没有重复的记录,直接执行插入操作;如果返回2,说明有重复记录,系统先DELETE这条记录,然后再INSERT这条记录。




UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;

DELETE FROM table_name
WHERE some_column=some_value;

SELECT DISTINCT column_name,column_name
FROM table_name;

SELECT column_name,column_name
FROM table_name
ORDER BY column_name,column_name ASC|DESC;









CMS实现原理





该示例取自一篇牛逼的论文,解释我们的场景完全足够。整个堆内存有4页,包含了7个对象。在初始标记阶段,4页都标记为clean,对象a是从根直接可达的,所以将其标记为活对象。

1a处于并发标记的过程中,对象b,c,e都被标记为活对象。在这个时候,对象g应用d被删除了,对象b引用c修改为引用d.因为g和b发生了变更,所以第1页和第3页被标记为脏页。

1c表示在并发标记结束时的样子。很明显,标记还不完整,因为b的引用对象d还没有被标记。在重新标记阶段才会被标记:在这个阶段所有的脏页会重新扫描,d会被标记上。

1d表示就是重新标记后的状态,这时候标记就结束了。下一个阶段就是并发清除了,最终f会被回收。

在回收的时候,虽然c现在是不可达的对象,但它被标记了,所以不会被回收,它会在下一次垃圾回收的时候会被回收。


从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC,对老年代GC称为Major GC,而Full GC是对整个堆来说的

新建的对象都是用新生代分配内存,Eden空间不足的时候,会把存活的对象转移到Survivor中,新生代的大小可以由-Xmn来控制,也可以用-XX:SurvivorRatio来控制Eden和Survivor的比例.
旧生代:Old Generation。用于存放新生代中经过多次垃圾回收仍然存活的对象,例如缓存对象。旧生代占用大小为-Xmx值减去-Xmn对应的值。
————————————————
版权声明:本文为CSDN博主「Franco蜡笔小强」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/w372426096/article/details/81360083

Java基本类型+字节






粘包和拆包

TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠***。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。即面向流的通信是无消息保护边界的。

假设客户端分别发送了两个数据包D1和D2给服务端,由于服务端一次读取到字节数是不确定的,故可能存在以下四种情况:

    1.服务端分两次读取到了两个独立的数据包,分别是D1和D2,没有粘包和拆包

    2.服务端一次接受到了两个数据包,D1和D2粘合在一起,称之为TCP粘包

    3.服务端分两次读取到了数据包,第一次读取到了完整的D1包和D2包的部分内容,第二次读取到了D2包的剩余内容,这称之为TCP拆包

    4.服务端分两次读取到了数据包,第一次读取到了D1包的部分内容D1_1,第二次读取到了D1包的剩余部分内容D1_2和完整的D2包。

      特别要注意的是,如果TCP的接受滑窗非常小,而数据包D1和D2比较大,很有可能会发生第五种情况,即服务端分多次才能将D1和D2包完全接受,期间发生多次拆包。

粘包、拆包问题的解决方案:定义通信协议

     目前业界主流的协议(protocol)方案可以归纳如下:

     1 定长协议:假设我们规定每3个字节,表示一个有效报文。

     2.特殊字符分隔符协议:在包尾部增加回车或者空格符等特殊字符进行分割 。

     3.长度编码:将消息分为消息头和消息体,消息头中用一个int型数据(4字节),表示消息体长度的字段。在解析时,先读取内容长度Length,其值为实际消息体内容(Content)占用的字节数,之后必须读取到这么多字节的内容,才认为是一个完整的数据报文。


CAS不断自旋

  • 使用AtomicLong时,在高并发下大量线程会同时去竞争更新同一个原子变量,但是由于同时只有一个线程的CAS会成功,所以其他线程会不断尝试自旋尝试CAS操作,这会浪费不少的CPU资源。
  • 而LongAdder可以概括成这样:内部核心数据value分离成一个数组(Cell),每个线程访问时,通过哈希等算法映射到其中一个数字进行计数,而最终的计数结果,则为这个数组的求和累加
    • 简单来说就是将一个值分散成多个值,在并发的时候就可以分散压力,性能有所提高。


  • 作者:Java3y
    链接:https://www.zhihu.com/question/39130725/answer/1006948362
    来源:知乎
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    MySQL存储过程

    MySQL 5.0 版本开始支持存储过程。

    存储过程(Stored Procedure)是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。

    存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。

    存储过程思想上很简单,就是数据库 SQL 语言层面的代码封装与重用。

    优点

    • 存储过程可封装,并隐藏复杂的商业逻辑。
    • 存储过程可以回传值,并可以接受参数。
    • 存储过程无法使用 SELECT 指令来运行,因为它是子程序,与查看表,数据表或用户定义函数不同。
    • 存储过程可以用在数据检验,强制实行商业逻辑等。

    缺点

    • 存储过程,往往定制化于特定的数据库上,因为支持的编程语言不同。当切换到其他厂商的数据库系统时,需要重写原有的存储过程。
    • 存储过程的性能调校与撰写,受限于各种数据库系统。



    数据库分区、分表、分库、分片




    高并发编程-ExecutorCompletionService深入解析

    假设现在有一大批需要进行计算的任务,为了提高整批任务的执行效率,你可能会使用线程池,向线程池中不断submit异步计算任务,同时你需要保留与每个任务关联的Future,最后遍历这些Future,通过调用Future接口实现类的get方法获取整批计算任务的各个结果。

    虽然使用了线程池提高了整体的执行效率,但遍历这些Future,调用Future接口实现类的get方法是阻塞的,也就是和当前这个Future关联的计算任务真正执行完成的时候,get方法才返回结果,如果当前计算任务没有执行完成,而有其它Future关联的计算任务已经执行完成了,就会白白浪费很多等待的时间,所以最好是遍历的时候谁先执行完成就先获取哪个结果,这样就节省了很多持续等待的时间。

    而ExecutorCompletionService可以实现这样的效果,它的内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,通过调用它的take方法或poll方法可以获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果。
    ————————————————
    版权声明:本文为CSDN博主「wind瑞」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/windrui/article/details/101366444



    一些补充




    用jstack命令打出这个进程的全部线程堆栈。拿到线程dump文件之后,搜索自己的worker名字

    刚刚通过“BLOCKED”关键字搜到了线程堆栈,找到它的线程名“DefaultQuartzScheduler_Worker-10”。OK,然后,把最后的10改成1,也就是“DefaultQuartzScheduler_Worker-1”,然后再拿这个关键字搜索整个进程堆栈。

    这个才是真正有用的堆栈!它告诉了我程序是在执行SQL的时候,SQL发生死锁,于是线程被阻塞。它还提供了更有用的信息,那就是到底是哪个SQL导致的死锁。堆栈的倒数第三行指示了导致死锁的SQL。

    在dump中,线程一般存在如下几种状态:
    1、RUNNABLE,线程处于执行中
    2、BLOCKED,线程被阻塞
    3、WAITING,线程正在等待

    在linux环境下,可以通过top命令查看各个进程的cpu使用情况,默认按cpu使用率排序
    1、上图中可以看出pid为23344的java进程占用了较多的cpu资源;
    2、通过top -Hp 23344可以查看该进程下各个线程的cpu使用情况;

    上图中可以看出pid为25077的线程占了较多的cpu资源,利用jstack命令可以继续查看该线程当前的堆栈状态。

    jstack命令

    通过top命令定位到cpu占用率较高的线程之后,继续使用jstack pid命令查看当前java进程的堆栈状态

    jstack命令生成的thread dump信息包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈,应该如何找?

    在top命令中,已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;隔段时间再执行一次stack命令获取thread dump,区分两份dump是否有差别,在nid=0x246c的线程调用栈中,发现该线程一直在执行JstackCase类第33行的calculate方法,得到这个信息,就可以检查对应的代码是否有问题。


    MySQL  insert死锁

    简单的insert会在insert的行对应的索引记录上加一个排它锁,这是一个record lock,并没有gap,所以并不会阻塞其他session在gap间隙里插入记录。

    不过在insert操作之前,还会加一种锁,官方文档称它为insertion intention gap lock,也就是意向的gap锁。这个意向gap锁的作用就是预示着当多事务并发插入相同的gap空隙时,只要插入的记录不是gap间隙中的相同位置,则无需等待其他session就可完成,这样就使得insert操作无须加真正的gap lock。
    假设有一个记录索引包含键值4和7,不同的事务分别插入5和6,每个事务都会产生一个加在4-7之间的插入意向锁,获取在插入行上的排它锁,但是不会被互相锁住,因为数据行并不冲突。

    假设发生了一个唯一键冲突错误,那么将会在重复的索引记录上加读锁。当有多个session同时插入相同的行记录时,如果另外一个session已经获得该行的排它锁,那么将会导致死锁。




    Java单元测试

    单元测试又称模块测试,属于白盒测试,是最小单位的测试。模块分为程序模块和功能模块。功能模块指实现了一个完整功能的模块(单元),一个完整的程序单元具备输入、加工和输出三个环节。而且每个程序单元都应该有正规的规格说明,使之对其输入、加工和输出的关系做出名明确的描述。

    JUnit是一个回归测试框架(regression testing framework)。Junit测试是程序员测试,即所谓白盒测试,因为程序员知道被测试的软件如何(How)完成功能和完成什么样(What)的功能。Junit是一套框架,继承TestCase类,就可以用Junit进行自动测试了。
    ————————————————
    版权声明:本文为CSDN博主「___cc木槿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_36568192/article/details/79857185

    ①测试框架可以帮助我们对编写的程序进行有目的地测试,帮助我们最大限度地避免代码中的bug,以保证系统的正确性和稳定性。

    ②很多人对自己写的代码,测试时就简单写main,然后sysout输出控制台观察结果。这样非常枯燥繁琐,不规范。缺点:测试方法不能一起运行,测试结果要程序猿自己观察才可以判断程序逻辑是否正确。

    ③JUnit的断言机制,可以直接将我们的预期结果和程序运行的结果进行一个比对,确保对结果的可预知性。

    assertSame()用来测试两个引用是否指向同一个对象

    assertEquals()用来测试两个对象是否相等

    注意事项:

    1、测试方法上面必须使用@Test注解进行修饰。

    2、测试方法必须使用public void 进行修饰,不能带有任何参数。

    3、新建一个源代码目录用来存放测试代码。

    4、测试类的包应该与被测试类的包保持一致。

    5、测试单元中的每一个方法必须独立测试,每个测试方法之间不能有依赖。

    6、测试类使用Test做为类名的后缀(非必要)。

    7、测试方法使用test作为方法名的前缀(非必要)。

    1、@BeforeClass所修饰的方法在所有方法加载前执行,而且他是静态的在类加载后就会执行该方法,

     在内存中只有一份实例,适合用来加载配置文件。

    2、@AfterClass所修饰的方法在所有方法执行完毕之后执行,通常用来进行资源清理,例如关闭数据库连接。

    3、@Before和@After在每个测试方法执行前都会执行一次。

    4>常用注解

    1、@Test(excepted=XX.class) 在运行时忽略某个异常。

    2、@Test(timeout=毫秒) 允许程序运行的时间。

    3、@Ignore 所修饰的方法被测试器忽略。

    4、RunWith 可以修改测试运行器 org.junit.runner.Runner




    代码的覆盖程度:评测测试过程中已经执行的代码的多少。
    代码的覆盖程度,一种度量方式。针对代码的测试覆盖率有许多种度量方式。

    语句覆盖( StatementCoverage ):也称为行覆盖( lin EC overage ) , 段覆盖(segmentcoverage)和基本块覆盖(bASicblockcoverage)。它度量每一个可执行语句是否被执行到了。

    判定覆盖(DecisionCoverage):也被称为分支覆盖(branchcoverage),所有边界覆盖(alledgescoverage), 基本路径覆盖( basispathcoverage ), 判定路径覆盖(decisiondecisionpath或DDPtesting)。它度量是否每个 BOOL 型的表达式取值true 和 false 在控制结构中都被测试到了。

    条件覆盖(ConDItionCoverage): 它独立的度量每一个子表达式,报告每一个子表达式的结果的 true 或 false。这个度量和判定覆盖(decisioncoverage)相似,但是对控制流更敏感。不过,完全的条件覆盖并不能保证完全的判定覆盖。

    路径覆盖(PathCoverage):也称为断言覆盖(prEDIcatecoverage),它度量了是否函数的每一个可能的分支都被执行了。路径覆盖的一个好处是:需要彻底的测试。但有两个缺点:一是,路径是以分支的指数级别增加的,例如:一个函数包含 10个 IF 语句,就有 1024 个路径要测试。如果加入一个 IF 语句,路径数就达到 2048;二是,许多路径不可能与执行的数据无关。

    循环覆盖(LOOPCoverage):这个度量报告你是否执行了每个循环体零次、只有一次还是多余一次(连续地)。对于 dowhile循环,循环覆盖报告你是否执行了每个循环体只有一次还是多余一次(连续地)。这个度量的有价值的方面是确定是否对于 while 循环和 for 循环执行了多于一次,这个信息在其它的覆盖率报告中是没有的。
    ————————————————
    版权声明:本文为CSDN博主「___cc木槿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/qq_36568192/article/details/79857185