当前位置: 首页 > news >正文

哪些网站做英语比较好浦口区网站建站公司

哪些网站做英语比较好,浦口区网站建站公司,阿里巴巴官方网站,做邀请函的网站流程 算法题简历上项目用到技术、流程、遇到问题HR 准备 常考的题型和回答思路刷100算法题#xff0c;理解其思想#xff0c;不要死记最近一家公司所负责的业务和项目#xff1a; 项目背景、演进之路#xff0c;有哪个阶段#xff0c;每个阶段主要做什么项目中技术选型…流程 算法题简历上项目用到技术、流程、遇到问题HR 准备 常考的题型和回答思路刷100算法题理解其思想不要死记最近一家公司所负责的业务和项目 项目背景、演进之路有哪个阶段每个阶段主要做什么项目中技术选型使用一些工具和框架的调研为啥选这个项目的亮点最牛的事复杂的需求方案设计、性能优化、线上问题处理、项目重构等 架构设计平台化思想。如DDD领域驱动设计项目管理如何高效的协调各个团队工作使用那些方法来保障项目按时交付。当遇到困难时如何应对等 知识点大纲 GC和JVM强弱引用数据库是双重检查的单例模式什么是亮点技术点 技术点快照、MAP 开始 微服务 微服务架构就是将单体的应用程序分成多个应用程序这多个应用程序就成为微服务每个微服务运行在自己的进程中并使用轻量级的机制通信。这些服务围绕业务能力来划分并通过自动化部署机制来独立部署。这些服务可以使用不同的编程语言不同数据库以保证最低限度的集中式管理。 微服务特点 解耦 – 系统内的服务很大程度上是分离的。因此整个应用程序可以轻松构建更改和扩展组件化 – 微服务被视为可以轻松更换和升级的独立组件业务能力 – 微服务非常简单专注于单一功能自治 – 开发人员和团队可以彼此独立工作从而提高速度持续交付 – 通过软件创建测试和批准的系统自动化允许频繁发布软件责任 – 微服务不关注应用程序作为项目。相反他们将应用程序视为他们负责的产品分散治理 – 重点是使用正确的工具来做正确的工作。这意味着没有标准化模式或任何技术模式。开发人员可以自由选择最有用的工具来解决他们的问题敏捷 – 微服务支持敏捷开发。任何新功能都可以快速开发并再次丢弃 Springboot ⚫ 静态代理和动态代理 代理是一种常用的设计模式目的是为其他对象提供一个代理以控制对某个对象的访问将两个类的关系解耦。代理类和委托类都要实现相同的接口因为代理真正调用的是委托类的方法。 ⚫ 静态代理由程序员创建或是由特定工具生成在代码编译时就确定了被代理的类是哪 一个是静态代理。静态代理通常只代理一个类 ⚫ 动态代理在代码运行期间运用反射机制动态创建生成。动态代理代理的是一个接口 下的多个实现类 实现步骤 a.实现 InvocationHandler 接口创建自己的调用处理器 b.给 Proxy 类提供 ClassLoader 和代理接口类型数组创建动态代理类 c.利用反射机制得到动态代理类的构造函数 d.利用动态代理类的构造函数创建动态代理类对象 Transactional 它允许为事务设置传播、隔离、超时、只读和回滚条件。 还可以指定事务管理器。 Spring 创建一个代理或者操作类字节码来管理事务的创建、提交和回滚。 在代理的情况下Spring 会忽略内部方法调用中的 Transactional。优先级接口–》超类–》类–》接口方法–》超类方法–》类方法从低到高。必须是public失效场景非public同一个类中方法调用代理原理引起异常被catch“吃了” 传播性–【活动事务的复用】没有创建、没有不用、没有异常、不用 REQUIRED默认值。Transactional(propagation Propagation.REQUIRED)SUPPORTSSpring 首先检查是否存在活动事务。 如果存在事务则将使用现有事务。 如果没有事务则以非事务方式执行。MANDATORY如果存在活动事务则将使用它。 如果没有活动事务则 Spring 会抛出异常NEVER不用NOT_SUPPORTED如果当前存在事务首先Spring将其挂起然后在没有事务的情况下执行业务逻辑。REQUIRES_NEW如果当前事务存在Spring 将挂起它然后创建一个新事务NESTEDSpring 检查事务是否存在如果存在则标记一个保存点。 这意味着如果业务逻辑执行抛出异常那么事务将回滚到这个保存点。 如果没有活动事务它的工作方式类似于 REQUIRED。JpaTransactionManager 仅支持 JDBC 连接的 NESTED if (isExistingTransaction()) {if (isValidateExistingTransaction()) {validateExisitingAndThrowExceptionIfNotValid();}return existing; } return createNewTransaction(); /* if后面是核心*/ //----------------------------------------------------------if(isExistingTransaction()){略} return emptyTransaction; /*SUPPORTS*/if(isExistingTransaction()){略} throw IllegalTransactionStateException; /*MANDATORY*/ //--------------------------------------------------if (isExistingTransaction()) {throw IllegalTransactionStateException; } return emptyTransaction; /*NEVER*/ 事务隔离 隔离是常见的 ACID 属性之一原子性、一致性、隔离性和持久性。 脏读读取并发事务未提交的变化不可重复读如果并发事务更新同一行并提交则在重新读取行时获得不同的值幻读如果另一个事务添加或删除范围内的某些行并提交则在重新执行范围查询后获取不同的行 DEFAULT默认当 Spring 创建新事务时隔离级别将是RDBMS 的默认隔离。 因此如果更改数据库应该小心。在正常流程中隔离仅在创建新事务时适用。 因此如果出于某种原因不想让一个方法在不同的隔离中执行必须将 TransactionManager::setValidateExistingTransaction 设置为 true。READ_UNCOMMITTED最低的隔离级别。受到三个副作用影响。Transactional(isolation Isolation.READ_UNCOMMITTED)READ_COMMITTED可防止脏读。但是如果一个事务提交了它的更改可能会通过重新查询而改变结果。Postgres、SQL Server 和 Oracle 的默认级别REPEATABLE_READ 可防止脏读和不可重复读。它是防止丢失更新所需的最低级别。 当两个或多个并发事务读取和更新同一行时就会发生更新丢失。 REPEATABLE_READ 根本不允许同时访问一行。 因此丢失的更新不会发生。SERIALIZABLE一组可串行化事务的并发执行与串行执行的结果相同 REPEATABLE_READ原理当前记录的行锁被其他事务占用的话就需要进入锁等待。 Autowired 和 Resource Resource 是 JDK 提供的而 Autowired 是 Spring 提供的Resource 不允许找不到 bean 的情况而 Autowired 允许Autowired(required false)指定 name 的方式不一样Resource(name “baseDao”),Autowired()Qualifier(“baseDao”)Resource 默认通过 name 查找当找不到与名称匹配的 bean 时才按照类型进行装配。但是需要注意的是如果 name 属性一旦指定就只会按照名称进行装配而 Autowired 默认通过 type 查找。 Spring Bean的生命周期 根据配置情况调用 Bean 构造方法或工厂方法实例化 Bean。利用依赖注入完成 Bean 中所有属性值的配置注入。如果 Bean 实现了 BeanNameAware 接口则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值实际不建议使用就是spring通过map管理bean如果 Bean 实现了 BeanFactoryAware 接口则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。如果 Bean 实现了 ApplicationContextAware 接口则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。如果 BeanPostProcessor 和 Bean 关联则 Spring 将调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操作此处非常重要Spring 的 AOP 就是利用它实现的。如果 Bean 实现了 InitializingBean 接口则 Spring 将调用 afterPropertiesSet() 方法。如果在配置文件中通过 init-method 属性指定了初始化方法则调用该初始化方法。如果 BeanPostProcessor 和 Bean 关联则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时Bean 已经可以被应用系统使用了。如果在 中指定了该 Bean 的作用范围为 scope“singleton”则将该 Bean 放入 Spring IoC 的缓存池中将触发 Spring 对该 Bean 的生命周期管理如果在 中指定了该 Bean 的作用范围为 scope“prototype”则将该 Bean 交给调用者调用者管理该 Bean 的生命周期Spring 不再管理该 Bean。如果 Bean 实现了 DisposableBean 接口则 Spring 会调用 destory() 方法将 Spring 中的 Bean 销毁如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法则 Spring 将调用该方法对 Bean 进行销毁。 Aware子接口描述BeanNameAware获取容器中 Bean 的名称BeanFactoryAware获取当前 BeanFactory 这样可以调用容器的服务ApplicationContextAware同上MessageSourceAware获取 Message Source 相关文本信息ApplicationEventPublisherAware发布事件ResourceLoaderAware获取资源加载器这样获取外部资源文件 Spring Bean的作用域 ⚫ 单例 singleton : bean 在每个 Spring IOC 容器中只有一个实例。 ⚫ 原型 prototype一个 bean 的定义可以有多个实例。 ⚫ request每次 http 请求都会创建一个 bean。 ⚫ session在一个 HTTP Session 中一个 bean 定义对应一个实例。 ⚫ globalsession ⚫ application Spring AOP原理 【类】代理对象实现了某个接口用JDK Proxy创建【接口】没有实现接口对象用Cglib生成被代理对象的子类。 代理就是创建一个和impl实现类平级的一个对象这个对象是一个代理对象实现和impl相同的功能aop的横向机制原理。 能够将那些与业务无关却为业务模块所共同调用的逻辑或责任例如事务处理、日志管理、权限控制等封装起来便于减少系统的重复代码降低模块间的耦合度并有利于未来的可拓展性和可维护性。 Spring MVC 的工作流程 ⚫ 用户向服务端发送一次请求这个请求会先到前端控制器 DispatcherServlet。 ⚫ DispatcherServlet 接收到请求后会调用 HandlerMapping 处理器映射器。由此得知该请求该由哪个 Controller 来处理并未调用 Controller只是得知 ⚫ DispatcherServlet 调用 HandlerAdapter 处理器适配器告诉处理器适配器应该要去执行哪个 Controller ⚫ HandlerAdapter 处理器适配器去执行 Controller 并得到 ModelAndView(数据和视图)并层层返回给 DispatcherServlet ⚫ DispatcherServlet 将 ModelAndView 交给 ViewResolver 视图解析器解析然后返回真正的视图。 ⚫ DispatcherServlet 将模型数据填充到视图中 ⚫ DispatcherServlet 将结果响应给用户 Spring Boot启动流程以及源码 创建 SpringApplication 实例。SpringApplication 是 Spring Boot 启动的入口类它是一个工厂类用于创建 Spring 应用上下文。在创建 SpringApplication 实例时会进行一些基本的配置如设置主程序类、设置 Web 应用类型等。加载配置文件。在创建 SpringApplication 实例时会加载配置文件包括 application.properties 和 application.yml 等文件。配置文件中可以设置一些基本配置信息如应用名称、端口号等。初始化 SpringApplication 实例。在创建 SpringApplication 实例后需要进行一些初始化操作如添加配置、设置属性等。执行 SpringApplication 实例的 run() 方法。在执行 run() 方法时Spring Boot 会根据配置情况创建应用上下文、注册 Bean 等。其中SpringApplication.run() 方法是 Spring Boot 启动的核心方法它会根据 Web 应用类型创建不同的应用上下文普通应用或web应用 SpringApplication(初始化启动类属性变量、推动web应用类型、加载类路径META-INF下的spring.factories文件实例初始化器和监听器、推断主启动类main) -- ConfigurableApplicationContext SpringBoot定时任务Scheduled 使用 Spring 中的 Scheduled 的方式主要通过 Scheduled 注解来实现。 使用 Quartz 则按照 Quartz 的方式定义 Job 和 Trigger 即可。 Spring 框架中用到了哪些设计模式 工厂设计模式 : Spring 使用工厂模式通过 BeanFactory、ApplicationContext 创建bean 对象。代理设计模式 : Spring AOP 功能的实现。单例设计模式 : Spring 中的 Bean 默认都是单例的。模板方法模式 : Spring 中 jdbcTemplate、hibernateTemplate 等以 Template 结尾的对数据库操作的类它们就使用到了模板模式。包装器设计模式 : 我们的项目需要连接多个数据库而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。观察者模式: Spring 事件驱动模型就是观察者模式很经典的一个应用。适配器模式 : Spring AOP 的增强或通知(Advice)使用到了适配器模式、spring MVC 中也是用到了适配器模式适配 Controller。 设计原则 ⚫ 遵循单一职责原则 ⚫ 开放-封闭原则 ⚫ 里氏代换原则LSP ⚫ 依赖倒置原则 ⚫ 接口隔离原则Interface Segregation Principle ⚫ 迪米特法则Law of Demeter 内存调优方面 排查 OOM 的经验写代码的时候也会注意内存性能了解 JVM 结构和 GC 流程的知识dump RPC 基础 ⚫ 需要有非常高效的网络通信比如一般选择 Netty 作为网络通信框架 ⚫ 需要有比较高效的序列化框架比如谷歌的 Protobuf 序列化框架 ⚫ 可靠的寻址方式主要是提供服务的发现比如可以使用 Zookeeper 来注册服务等等 ⚫ 如果是带会话状态的 RPC 调用还需要有会话和状态保持的功能 JAVA ⚫ 编码 utf-8变长编码技术不同类型的字符可以由1~6个字节组成。int型占4个字节utf-16世界上所有语言都可通过这本字典Unicode 来相互翻译而 UTF-16 定义了 Unicode 字符在计算机中存取方法用两个字节来表 示 Unicode 转化格式。不论什么字符都可用两字节表示即 16bit。GB2312:双字节编码总编码范围是 A1-A7,A1-A9 是符号区包含 682 个字符B0-B7是 汉字区包含 6763 个汉字GBK 为了扩展 GB2312,加入了更多的汉字编码范围是 8140~FEFE有 23940 个码位能 表示 21003 个汉字ASCII 码总共 128 个用一个字节的低 7 位表示0〜31 控制字符如换回车删除等32~126 是打印字符可通过键盘输入并显示出来ISO-8859-1,用来扩展 ASCII 编码256 个字符涵盖了大多数西欧语言字符。 I/O模型有哪几种 阻塞非阻塞复用信号驱动异步。 I/O多路复用实现方式select、poll、epoll 区别 select它仅仅知道了有 I/O 事件发生了却并不知道是哪那几个流可能有一个多个甚至全部我们只能无差别轮询所有流找出能读出数据或者写入数据的流对他们进行操作。所以 select 具有 O(n)的无差别轮询复杂度同时处理的流越多无差别轮询时间就越长。pollpoll 本质上和 select 没有区别它将用户传入的数组拷贝到内核空间然后查询每个 fd 对应的设备状态 但是它没有最大连接数的限制原因是它是基于链表来存储的.。epollepoll 可以理解为 event poll不同于忙轮询和无差别轮询epoll 会把哪个流发生了怎样的 I/O 事件通知我们。所以我们说 epoll 实际上是事件驱动每个事件关联上 fd的此时我们对这些流的操作都是有意义的。复杂度降低到了 O(1)通过红黑树和双链表数据结构并结合回调机制造就了 epoll 的高效epoll_create()epoll_ctl()和 epoll_wait()系统调用 浏览器打开一个链接的时候计算机做了哪些工作步骤 域名解析– 发起 TCP 的 3 次握手 – 建立 TCP 连接后发起 http 请求 – 服务器响应 http请求–浏览器得到 html 代码【加载】 – 浏览器解析 html 代码并请求 html 代码中的资源如js、css、图片等【解析】 – 浏览器对页面进行渲染呈现给用户【初始化】 线程生命周期切换 线程的生命周期包含 5 个阶段包括新建、就绪、运行、阻塞、销毁。 ⚫ 新建NEW就是刚使用 new 方法new 出来的线程 ⚫ 就绪RUNNABLE就是调用的线程的 start()方法后这时候线程处于等待 CPU 分配资源阶段谁先抢的 CPU 资源谁开始执行; ⚫ 运行RUNNING当就绪的线程被调度并获得 CPU 资源时便进入运行状态run 方法定义了线程的操作和功能; ⚫ 阻塞BLOCKED在运行状态的时候可能因为某些原因导致运行状态的线程变成了阻塞状态比如 sleep()、wait()之后线程就处于了阻塞状态这个时候需要其他机制将处于阻塞状态的线程唤醒比如调用 notify 或者 notifyAll()方法。唤醒的线程不会立刻执行run 方法它们要再次等待 CPU 分配资源进入运行状态; ⚫ Waiting无限等待一个线程在等待另一个线程执行一个唤醒动作时该线程进入Waiting 状态。进入这个状态后不能自动唤醒必须等待另一个线程调用 notify 方法或者 notifyAll 方法时才能够被唤醒。 ⚫ 销毁TERMINATED如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束那么线程就要被销毁释放资源; 线程进程 操作系统中可以拥有多个进程一个进程里可以拥有多个线程线程在进程内执行 容易创建新线程。创建新进程需要重复父进程线程可以控制同一进程的其他线程。进程无法控制兄弟进程只能控制其子进程进程拥有自己的内存空间。线程使用进程的内存空间且要和该进程的其他线程共享这个空间而不是在进程中给每个线程单独划分一点空间。同一进程中的线程在共享内存空间中运行而进程在不同的内存空间中运行线程可以使用 waitnotifynotifyAll等方法直接与其他线程同一进程通信而进程需要使用“进程间通信”IPC来与操作系统中的其他进程通信。 join 和 deamon 的区别 join当子线程调用 join 时主线程会被阻塞当子线程结束后主线程才能继续执行。deamon当子进程被设置为守护进程时主进程结束不管子进程是否执行完毕都会随着主进程的结束而结束。 关于创建守护进程fork两次的原因如下为了创建一个新的会话组防止获得终端 第一次fork前在开始运行且fork之前拥有一个会话两个进程组shell拥有一个进程组你运行的程序拥有一个进程组且他们都是组长进程shell还是会话首进程 第一次fork后父进程的退出带来的变化是告知shell命令已完成新创建的子进程不可能是组长进程(因为父进程在它之前创建)。这为接下来调用setsid做准备(因为调用setsid()的进程不能是组长进程) 第二次fork原因在调用setsid()后原来的子进程现在在一个新会话里面新会话里有一个进程组并且它就是组长进程。而且如果原来有终端现在也分离了。然而问题就处在这里组长进程是有可能获得终端的为了防止它以后获得终端所以再次fork来消除潜在问题 父进程宕掉子进程情况 如果父进程是会话首进程如shell那么父进程退出后子进程也会退出反之如果父进程不是会话首进程那么父进程退出后子进程不会退出而它的一个或多个子进程还在运行那么这些子进程就成为孤儿进程。 一个公司创业首先是一个人然后慢慢加了几个人做大以后有了部门每个部门各司其职。这里公司就是一个会话创业者就是会话首进程公司名就是会话id各个部门就是进程组各个部门的名字就是组id员工就是进程工号就是进程id(当员工辞职后工号会留下来给新任员工对比进程id) 孤儿进程和僵尸进程的区别 孤儿进程父进程结束了而它的一个或多个子进程还在运行那么这些子进程就成为孤儿进程(father died)。子进程的资源由 init 进程(进程号 PID 1)回收。 僵尸进程子进程退出了但是父进程没有用 wait 或 waitpid 去获取子进程的状态信息那么子进程的进程描述符仍然保存在系统中这种进程称为僵死进程。 内核态与用户态(❤️ ω ❤️) 假设没有这种内核态和用户态之分程序随随便便就能访问硬件资源比如说分配内存程序能随意的读写所有的内存空间如果程序员一不小心将不适当的内容写到了不该写的地方就很可能导致系统崩溃。用户程序是不可信的不管程序员是有意的还是无意的都很容易将系统干到崩溃。 正因为如此Intel 就发明了 ring0-ring3 这些访问控制级别来保护硬件资源ring0 的就是我们所说的内核级别,要想使用硬件资源就必须获取相应的权限设置 PSW 寄存器这个操作只能由操作系统设置。操作系统对内核级别的指令进行封装统一管理硬件资源然后向用户程序提供系统服务用户程序进行系统调用后操作系统执行一系列的检查验证确保这次调用是安全的再进行相应的资源访问操作。**内核态能有效保护硬件资源的安全。 进程间的几种通讯方式 管道( pipe )管道是一种半双工的通信方式数据只能单向流动而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。有名管道 (named pipe) 有名管道也是半双工的通信方式但是它允许无亲缘关系进程间的通信。信号量( semophore ) 信号量是一个计数器可以用来控制多个进程对共享资源的访问。它常作为一种锁机制防止某进程正在访问共享资源时其他进程也访问该资源。因此主要作为进程间以及同一进程内不同线程之间的同步手段。信号 ( sinal ) 信号是一种比较复杂的通信方式用于通知接收进程某个事件已经发生。共享内存( shared memory ) 共享内存就是映射一段能被其他进程所访问的内存这段共享内存由一个进程创建但多个进程都可以访问。共享内存是最快的 IPC 方式它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制如信号量配合使用来实现进程间的同步和通信.消息队列( message queue ) 消息队列是由消息的链表存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。套接字( socket ) 套解口也是一种进程间通信机制与其他通信机制不同的是它可用于不同及其间的进程通信。 管道是只读不回消息队列是写 、读然后写、读2个内存地址共享内存是一个内存地址。 名词解释 管道所谓的管道就是内核里面的一串缓存。管道传输的数据是无格式的流且大小受限。创建的子进程会复制父进程的文件描述符可以各自的fd读写同一个管道文件实现进程通信。在shell里通过「|」匿名管道将多个命令连接在一起实际上也就是创建了多个子进程那么在我们编写 shell 脚本时能使用一个管道搞定的事情就不要多用一个管道这样可以减少创建子进程的系统开销。 命名管道因为命令管道提前创建了一个类型为管道的设备文件在进程里只要使用这个设备文件就可以相互通信。管道因为匿名在没有实体也就没有管道文件 只读且阻塞方式 open(const char *pathname, O_RDONLY); 对应写 open(…, O_WRONLY);只读且非阻塞方式 open(const char *pathname, O_RDONLY | O_NONBLOCK); 共享内存在Linux中每个进程都有属于自己的进程控制块PCB和地址空间Addr Space并且都有一个与之对应的页表负责将进程的虚拟地址与物理地址进行映射通过内存管理单元MMU进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域它们所指向的这块区域即共享内存。PCB进程控制块–Addr Space地址空间–页表–物理地址对于一个共享内存实现采用的是引用计数的原理当进程脱离共享存储区后计数器减一挂架成功时计数器加一只有当计数器变为零时才能被删除。当进程终止时它所附加的共享存储区都会自动脱离。 信号信号就是由用户、系统或进程发送给目标进程的信息以通知目标进程中某个状态的改变或是异常。信号产生分为硬中段和软中段如系统异常指令和状态变化-定时器。信号处理分为忽略【kill、stop指令不能忽略】、捕捉【处理信号】和默认动作【默认处理方式一般杀死】。 信号量semaphore与已经介绍过的 IPC 结构不同它是一个计数器。信号量用于实现进程间的互斥与同步而不是用于存储进程间通信数据。信号量是一种特殊的变量对信号量的访问必须是原子操作信号量的操作只有两种P操作-1申请资源和V操作1释放资源 select 和 epoll 了解一下 select支持阻塞操作的设备驱动通常会实现一组自身的等待队列如读/写等待队列用于支持上层(用户层)所需的 BLOCK 或 NONBLOCK 操作。当应用程序通过设备驱动访问该设备时(默认为 BLOCK 操作)若该设备当前没有数据可读或写则将该用户进程插入到该设备驱动对应的读/写等待队列让其睡眠一段时间等到有数据可读/写时再将该进程唤醒。 select 就是巧妙的利用等待队列机制让用户进程适当在没有资源可读/写时睡眠有资源可读/写时唤醒。epollepoll 由三个系统调用组成分别是 epoll_createepoll_ctl 和 epoll_wait。epoll_create 用于创建和初始化一些内部使用的数据结构epoll_ctl 用于添加删除或者修改指定的 fd 及其期待的事件epoll_wait 就是用于等待任何先前指定的 fd 事件 Java的异常体系 Java 中 Throwable 是所有异常和错误的超类两个直接子类是 Error错误和 Exception Error是程序无法处理的错误由JVM产生和抛出。如OOM、ThreadDeath等一般会终止程序Exception是程序本身可以处理的异常分为运行异常NullPoint、IndexOutOf非运行异常IO、SQL、FileNotFount以及自定义的Exception final、finally、finallizefinally 是在 return 之前执行还是之后finally 块里的代码一定会执行吗 final 可以用来修饰类、方法、变量分别有不同的意义final 修饰的 class 代表不可以继承扩展final 的变量是不可以修改的而 final 的方法也是不可以重写的override。finally 是 Java 保证重点代码一定要被执行的一种机制。可以使用 try-finally 或者 try-catch-finally 来进行类似关闭 JDBC 连接、保证 unlock 锁等动作。finalize 是基础类 java.lang.Object 的一个方法设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用并且在 JDK 9 开始被标记为deprecated。 finally 块的语句在 try 或 catch 中的 return 语句执行之后返回之前执行且 finally 里的修改语句可能影响也可能不影响 try 或 catch 中 return 已经确定的返回值若 finally 里也有 return语句则覆盖 try 或 catch 中的 return 语句直接返回。 finally 块里的代码不一定会执行。比如 ⚫ try 语句没有被执行到如在 try 语句之前就返回了这样 finally 语句就不会执行这也说明了 finally 语句被执行的必要而非充分条件是相应的 try 语句一定被执行到。 ⚫ 在 try 块中有 System.exit(0** 解析与分派(❤️ ω ❤️) 解析指方法在运行前即编译期间就可知的有一个确定的版本运行期间也不会改变。解析是静态的在类加载的解析阶段就可将符号引用转变成直接引用。 分派可分为静态分派和动态分派重载属于静态分派覆盖属于动态分派。静态分派是指在重载时通过参数的静态类型而非实际类型作为判断依据在编译阶段编译器可根据参数的静态类型决定使用哪一个重载版本。动态分派则需要根据实际类型来调用相应的方法。 Java中实现多态的机制 多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时不确定在运行期间才确定一个引用变量到底会指向哪个类的实例。这样就可以不用 修改源程序就可以让引用变量绑定到各种不同的类实现上。Java 实现多态有三个必要条件 继承、重定、向上转型在多态中需要将子类的引用赋值给父类对象只有这样该引用才能 够具备调用父类方法和子类的方法。 Java反射 在运行状态中对任意一个类都能知道这个类的所有属性和方法对任意一个对象都能调用它的任意一个方法和属性。这种能动态获取信息及动态调用对象方法的功能 称为 java 语言的反射机制。 Class.forName(“全限类名”) //创建对象 对象.getClass(); //获取class//获取构造器对象通过构造器 new 出一个对象 Clazz.getConstructor([String.class]); Con.newInstance([参数]);//通过 class 对象创建一个实例对象 new Class Cls.newInstance()//通过 class 对象获得一个属性对象 Field ccls.getFields(); //获得某个类的所有的公共public的字段包括父类中的字段。 Field ccls.getDeclaredFields(); //获得某个类的所有声明的字段即包括 public、private和 proteced但是不包括父类的声明字段//通过 class 对象获得一个方法对象 Cls.getMethod(“方法名”,class……parameaType); //只能获取公共的 Cls.getDeclareMethod(“方法名”); //获取任意修饰的方法不能执行私有 M.setAccessible(true);//让私有的方法可以执行//让方法执行 Method.invoke(obj 实例对象,obj 可变参数); //-----是有返回值的Java注解(❤️ ω ❤️) 注解是通过interface关键字来进行定义的形式和接口差不多只是前面多了一个 public interface TestAnnotation{ } 要使注解能正常工作还需要使用元注解5种它是可以注解到注解上的注解。 Retention说明注解的存活时间取值有 RetentionPolicy.SOURCE 注解只在源码阶段保留在编译器进行编译时被丢弃RetentionPolicy.CLASS 注解只保留到编译进行的时候并不会被加载到 JVM 中。RetentionPolicy.RUNTIME 可以留到程序运行的时候它会被加载进入到 JVM 中所以在程序运行时可以获取到它们。Document 注解中的元素包含到javadoc中去。Target 限定注解的应用场景ElementType.FIELD 给属性进行注解ElementType.LOCAL_VARIABLE 可以给局部变量进行注解ElementType.METHOD 可以给方法进行注解ElementType.PACKAGE 可以给一个包进行注解 ElementType.TYPE 可以给一个类型进行注解如类、接口、枚举。Inherited 若一个超类被Inherited 注解过的注解进行注解它的子类没有被任何注解应用 的话该子类就可继承超类的注解 注解的作用 ⚫ 提供信息给编译器编译器可利用注解来探测错误和警告信息 ⚫ 编译阶段软件工具可以利用注解信息来生成代码、html 文档或做其它相应处理 ⚫ 运行阶段程序运行时可利用注解提取代码注解是通过反射获取的可以通过 Class 对象的 isAnnotationPresent()方法判断它是否应用了某个注解再通过 getAnnotation()方法获取 Annotation 对象 泛型原理 Java 中的泛型有 3 种形式泛型方法泛型类泛型接口。Java 通过在编译时类型擦除的方式来实现泛型。擦除时使用 Object 或者界定类型替代泛型同时在要调用具体类型方法或者成员变量的时候插入强转代码为了保证多态特性Java 编译器还会为泛型类的子类生成桥接方法。类型信息在编译阶段被擦除之后程序在运行期间无法获取类型参数所对应的具体类型。 泛型就是将类型变成参数传入使得可以使用的类型多样化从而实现解耦。 为保持对以前版本的兼容使用了擦除的方法实现泛型。擦除是指在 一定程度无视类型参数 T直接从 T 所在的类开始向上 T 的父类去擦除如调用泛型方法 传入类型参数 T 进入方法内部若没在声明时做类似publicTmethodName(T extends Fathert){}Java 就进行了向上类型的擦除直接把参数 t当做 Object 类来处理而不是传进去的 T。 即在有泛型的任何类和方法内部它都无法知道自己的泛型参数擦除和转型都是在边界上发生即传进去的参在进入类或方法时被擦除掉但传出来的时候又被转成了我们设置的 T。在泛型类或方法内任何涉及到具体类型即擦除后的类型的子类操作都不能进行如 newT()或者 T.play()play 为某子类的方法而不是擦除后的类的方法 擦除是向上类型所以子类的方法不能调用。如果入参没有extends就当Object处理。 String【注意new String()】 String 类是 final 型固 String 类不能被继承它的成员方法也都默认为 final 方法。因此任何改变都会生成新的String对象。 String类是通过char数组来保存字符串的String对equals方法进行了重定比较的是值相等。 String a“test”; String b“test”; String cnewString(“test”); a、b 和字面上的 test 都是指向 JVM 字符串常量池中的test对象他们指向同一个对象。 new 关键字一定会产生一个对象 test该对象存储在堆中。在堆中的 test 应该是引用字符串常量池中的 test。ctest是falsec是堆中句柄池对象而“test”是堆中常量池中对象。运行时都在方法区 常用集合 【List、Vector、Map、Set】 Vector 底层数据结构是数组查询快增删慢线程安全效率低默认长度为 10超过会 100%延长变成 20浪费空间ListArrayList 基于数组便于按 index 访问超过数组需要扩容扩容成本较高。LinkedList使用链表实现无需扩容。SetHashSet底层数据结构是哈希表无序唯一通过 hashcode()和 equals()保证元素唯一。LinkedHashSet 底层数据结构是链表和哈希表FIFO 插入有序唯一由链表保证元素有序由哈希表保证元素唯一。TreeSet底层数据结构是红黑树唯一有序通过自然排序和比较器排序保证元素有序根据比较返回值是否是 0 来保证元素唯一性。MapHashMap 空间换时间哈希冲突不大的情况下查找数据性能很高。LinkedHashMap 基本特点继承自 HashMap对 Entry 集合添加了一个双向链表before、after用于维护Entry插入的先后顺序next用于维护HashMap各个桶中Entry的连接顺序。TreeMap 是有序的。 Lambda lambda 表达式也被称为闭包。lambda 允许把函数作为一个方法的参数函数作为参数传递进方法中使用 Lambda 表达式可以使代码变的更加简洁紧凑。简洁、不易维护、可读性差。 作用域 访问局部变量我们可以直接在 lambda 表达式中访问外部的局部变量但是和匿名对象不同的是这里的变量可以不用声明为 final该代码同样正确不过这里的变量必须不可被后面的代码修改即隐性的具有 final 的语义访问字段和静态变量与局部变量相比我们对 lambda 表达式中的实例字段和静态变量都有读写访问权限。该行为和匿名对象是一致的。 虚拟内存 当每个进程创建的时候内核会为每个进程分配虚拟内存这个时候数据和代码还在磁盘上当运行到对应的程序时进程去寻找页表如果发现页表中地址没有存放在物理内存上而是在磁盘上于是发生缺页异常于是将磁盘上的数据拷贝到物理内存中并更新页表下次再访问该虚拟地址时就能命中了。 ⚫ 虚拟内存地址空间是连续的没有碎片。 ⚫ 虚拟内存的最大空间就是 cup 的最大寻址空间不受内存大小的限制能提供比内存更大的地址空间 栈会溢出吗什么时候溢出方法区会溢出吗 栈是线程私有的它的生命周期与线程相同每个方法在执行的时候都会创建一个栈帧用来存储局部变量表操作数栈动态链接方法出口等信息。局部变量表又包含基本数据类型对象引用类型。如果线程请求的栈深度大于虚拟机所允许的最大深度将抛出StackOverflowError 异常方法递归调用产生这种结果。 方法区用于存放 Class 的相关信息如类名、访问修饰符、常量池、字段描述、方法描述等如果动态生成大量的 Class 文件也会产生内存溢出。常见的场景还有大量 JSP 或动态产生JSP 文件的应用JSP 第一次运行时需要编译为 java 类、基于 OSGi 的应用即使是同一个类文件被不同的类加载器加载也会视为不同的类 java 程序诊断 ⚫ top 找出进程 CPU 比较高 PID ⚫ top -Hp PID 打印 该 PID 进程下哪条线程的 CPU 占用比较高 tid ⚫ printf “%x\n” tid 将该 id 进行 16 进制转换 tidhex ⚫ jstack PID |grep tidhex 打印线程的堆栈信息 自带工具 ⚫ jstat虚拟机进程状况工具 ⚫ jinfoJava 配置信息工具 ⚫ jmapJava 内存映像工具 ⚫ jhat虚拟机堆转储快照分析工具 ⚫ jstackJava 堆栈跟踪工具 ⚫ JConsole: Java 监视与管理控制台 ⚫ VisualVM: 多合一故障处理工具 JVM JVM 类加载机制分为五个部分加载Loading验证Verification准备Preparation解析Resolution初始化Initialization。 类再加上 使用Using 、卸载Unloading。 Java 虚拟机设计团队把类加载阶段中“通过一个类的全限定名来获取描述该类的二进制流”这个动作放到 Java 虚拟机外部去实现。比便让程序应用自己决定如何取获取所需的类。实现这个动作的代码被称为“类加载器”Class Loader。 ⚫ 方法区(method)被所有的线程共享。方法区包含所有的类信息和静态变量。运行时常量池 ⚫ 堆(heap)被所有的线程共享存放对象实例以及数组Java 堆是 GC 的主要区域。 ⚫ 栈(stack)每个线程包含一个栈区栈中保存一些局部变量等。本地局部变量、操作数栈、动态链接、返回地址 ⚫ 程序计数器是当前线程执行的字节码的行指示器。 ⚫ 本地方法栈 CMSG1垃圾回收中三色标记 三色黑、灰、白标记法是一种垃圾回收法它可以让 JVM 不发生或仅短时间发生 STW(Stop The World)从而达到清除 JVM 内存垃圾的目的。 黑色该对象已经被标记过了且该对象下的属性也全部都被标记过了。程序所需要的对象灰色对象已经被垃圾收集器扫描过了但是对象中还存在没有扫描的引用GC 需要从此对象中去寻找垃圾白色表示对象没有被垃圾收集器访问过即表示不可达 CMS 漏标问题增量更新 在应对漏标问题时CMS 使用了增量更新(Increment Update)方法来做在一个未被标记的对象白色对象被重新引用后引用它的对象若为黑色则要变成灰色在下次二次标记时让GC 线程继续标记它的属性对象但还是存在漏标的问题 内存碎片使用Mark-Sweep-Compact算法减少垃圾碎片。压缩、阈值 CMS 采用了 Mark-Sweep 算法最后会产生许多内存碎片当到一定数量时CMS 无法清理这些碎片了CMS 会让 Serial Old 垃圾处理器来清理这些垃圾碎片而 Serial Old 垃圾处理器是单线程操作进行清理垃圾的效率很低。所以使用 CMS 就会出现一种情况硬件升级了却越来越卡顿其原因就是因为进行 Serial Old GC 时效率过低。 调优参数配套使用 -XX:UseCMSCompactAtFullCollection 开启 CMS 的压缩 -XX:CMSFullGCsBeforeCompaction 默认为 0指经过多少次 CMS FullGC 才进行压缩当 JVM 认为内存不够再使用 CMS 进行并发清理内存可能会发生 OOM 的问题而不得不进行 Serial Old GCSerial Old 是单线程垃圾回收效率低。 解决方案降低触发 CMS GC 的阈值让浮动垃圾不那么容易占满老年代 调优参数 -XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值让老年代占用率达到该值就 进行 CMS GC G1 漏标问题SATB 在开始标记的时候生成一个快照图标记存活对象在一个引用断开后要将此引用推到 GC 的堆栈里保证白色对象垃圾还能被 GC 线程扫描到(在**write barrier(写屏障)**里把所有旧的引用所指向的对象都变成非白的)配合 Rset去扫描哪些 Region 引用到当前的白色对象若没有引用到当前对象则回收 Full GC且 JDK10 之前的 Full GC为单线程的。加大内存提高CPU性能增加速度小于回收速度降低阈值让 Mixed GC 提早发生默认 45% TCP/UDP 应用层 传输层 互连网络层 网络接口层 区别 TCP 基于连接消耗资源多保证数据顺序与正确性UDP 基于无连接消耗小结构简单无顺序丢包。 TCP原理 因为 TCP 是全双工每个方向都必须进行单独关闭。关闭连接时当 Server 端收到 FIN 报文时很可能并不会立即关闭 SOCKET所以只能先回复一个 ACK 报文告诉 Client 端”你发的 FIN 报文我收到了”。只有等到 Server 端所有的报文都发送完了我才能发送 FIN 报文因此不能一起发送。故需要四步握手。 Hash算法解决冲突 开放寻址法链地址法再哈希法公共溢出区 HashMap的底层数据结构 线程不安全多个线程同时操作两个线程遇到12的倍数会出现old转移new时候值存储异常。 扰动函数右移16位正好为32bit的一半hashcode低位与高位异或。是为了混合原始哈希吗的高位和低位来加大低位的随机性。而且混合后的低位掺杂了高位的部分特征使高位的信息也被保留下来。return (key null) ? 0 : (h key.hashCode()) ^ (h 16); 扩容【空参】实例化map内部数组为null第一次调用put方法开始第一次初始化扩容长度16【有参】不小于指定容量2的幂数将值赋给阈值threshold。第一次put阈值给容量然后阈值容量*负载因子。以后就是容量和阈值为原来2倍。 jdk1.8之前采用数组和链表结合在一起使用的链表散列。通过n-1hash分桶一个桶里用拉链法解决冲突。hash采用扰动函数处理减少碰撞。 jdk1.8以后当链表长度大于阈值默认8会调用treeifyBin()方法根据map数组来决定是否转换为红黑树。只有数组长度大于或者等于64的情况下才会执行转换红黑树操作以减少搜索时间。否则就是执行resize()对数组扩容。扩展后 Node 对象的位置要么在原位置要么移动到原偏移量两倍的位置。 ConcurrentHashMap 的存储结构 (HashTable) java7使用的分段锁每一段Segment上同时只有一个线程可以操作每个段 HashEntry 数组 链表类似HashMap数据的结构可以扩容。但是Segment的个数一旦初始化就不能改变默认16个。java8使用Synchronized锁加CAS的机制。Node 数组 链表 / 红黑树结构类似HashMap。冲突再达到一定大小时会转化成红黑树在冲突小于一定数量时又退回链表。对每个数组元素加锁 HashTable:使用Synchronized锁实现线程安全读操作不加锁由于HashEntry的value变量是volatile ArrayList 和 LinkedList 的区别 需要频繁读取集合中的元素时更推荐使用 Arrayist而在增删操作较多时更推荐使用 LinkedList。 数据结构实现ArrayList 基于数组便于按 index 访问超过数组需要扩容扩容成本较高。LinkedList使用链表实现无需扩容。数组静态分配内存链表动态分配内存数组是事先定义长度当数据少的时候造成内存浪费。数组需要内存连续。随机访问效率ArrayList 比 LinkedList 在随机访问的时候效率要高因为 LinkedList 是线性的数据存储方式所以需要移动指针从前往后依次查找。增加和删除效率在非首尾的增删操作LinkedList 要比 ArrayList 效率要高因为ArrayList 增删操作要影响数组内的其他数据的下标。内存空间占用LinkedList 比 ArrayList 更占内存因为 LinkedList 的节点除了存储数据还存储了两个引用一个指向前一个元素一个指向后一个元素。当数组内存过大时会出现什么问题堆内存溢出链表增删过多会出现的什么问题大量内存碎片 多台服务器同时对一个数据定时任务怎么处理 对于一个定时任务如果当前任务已经被某一个服务器处理后另外一个服务器就不需要执行这个任务了。总结就是在阈值时间没有执行就执行。任务ID分布式锁。 在定时任务里加锁机制等某台服务器获取权限其他服务器将不再执行此次定时任务。在数据库的创建定时任务控制表 job_controller创建 updated_by 字段用来存放执行代码的服务器生成的序列号。创建 updateTime 字段用于记录标记更新 update_by 的时间戳也可以理解为上一次任务执行的时间戳。在代码层面在执行任务的时候首先生成一个序列号然后将序列号存储在当前任务的记录上。然后再从数据库里查询当前记录的序列号在做标记前首先检查当前任务的上一次执行时间离当前时间超过阈值自己定义如果超过则表明还没有其他节点执行该任务然后为 task 保存标签和当前运行时间。当然如果上一次运行时间为空的情况下也是允许标记的如果数据库中的序列号与当前节点生成序列号相匹配则执行任务的具体逻辑反之则什么都不做处理。 多线程 线程的生命周期开销非常高消耗过多CPU资源线程切换。 安全性问题并发活跃性问题死锁性能问题线程切换 分布式锁 基于数据库实现分布式锁基于缓存实现分布式锁基于ZK实现分布式锁 Redis缓存分布式锁实现 set px nx守护线程进行 renewRedis 分布式锁实现 先拿 setnx 来争抢锁抢到之后再用 expire(过期)给锁加一个过期时间防止锁忘记了释放。如果在 setnx 之后执行 expire 之前进程意外 crash 或者要重启维护了那会怎么样set 指令有非常复杂的参数这个应该是可以同时把 setnx 和 expire 合成一条指令来用的 ThreadLocal线程本地变量 ThreadLocal 是一个解决线程并发问题的一个类用于创建线程的本地变量我们知道一个对象的所有线程会共享它的全局变量所以这些变量不是线程安全的我们可以使用同步技术。但是当我们不想使用同步的时候我们可以选择 ThreadLocal 变量。例如由于 JDBC 的连接对象不是线程安全的因此当多线程应用程序在没有协同的情况下使用全局变量时就不是线程安全的。通过将 JDBC 的连接对象保存到 ThreadLocal 中每个线程都会拥有属于自己的连接对象副本。 实现原理 ⚫ 每个 Thread 线程内部都有一个 ThreadLocalMap;以线程作为 key泛型作为 value可以理解为线程级别的缓存。每一个线程都会获得一个单独的 map。 ⚫ 提供了 set 和 get 等访问方法这些方法为每个使用该变量的线程都存有一份独立的副本因此 get 方法总是返回由当前执行线程在调用 set 时设置的最新值。 应用场景 ⚫ JDBC 连接⚫ Session 管理⚫ Spring 事务管理⚫ 调用链参数传递⚫ AOP 多线程同步 使用 synchronized 关键字⚫ wait 和 notify⚫ 使用特殊域变量 volatile 实现线程同步⚫ 使用重入锁实现线程同步⚫ 使用局部变量来实现线程同步⚫ 使用阻塞队列实现线程同步⚫ 使用原子变量实现线程同步 创建线程三个方法 ⚫ 通过继承 Thread 类创建线程类。并且运行其 start()方法 ⚫ 实现 Runnable 接口创建线程类。运行 Thread 对象的 start()方法 ⚫ 通过 Callable 和 Future 接口创建线程。运行 Thread 对象的 start()方法 获取多线程返回值 ⚫ 主线程等待。⚫ 使用 Thread 的 join 阻塞当前线程等待。⚫ 实现 Callable 接口通过 FutureTask 或线程池的 Future。 乐观锁和悲观锁 悲观锁总是假设最坏的情况每次去拿数据的时候都认为别人会修改所以每次在拿数据的时候都会上锁这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制比如行锁表锁等读锁写锁等都是在做操作之前先上锁。再比如 Java 里面的同步原语 synchronized 关键字的实现也是悲观锁。 乐观锁顾名思义就是很乐观每次去拿数据的时候都认为别人不会修改所以不会上锁但是在更新的时候会判断一下在此期间别人有没有去更新这个数据可以使用版本号等机制。乐观锁适用于多读的应用类型这样可以提高吞吐量像数据库提供的类似于 write_condition 机制MVCC快照读其实都是提供的乐观锁。在 Java 中 java.util.concurrent.atomic 包下面的原子变量类就是使用了乐观锁的一种实现方式 CAS 实现的。 互斥锁mutex和自旋锁spinlock场景 在多核机器中如果锁住的“事务”很简单占用很少的时间就应该使用 spinlock这个时候 spinlock 的代价比 mutex 会小很多。”事务”很快执行完毕自旋的消耗远远小于陷入sleep 和 wake 的消耗。如果锁住“事务”粒度较大就应该使用 mutex因为如果用spinlock那么在“事务”执行过程中自旋很长时间还不如使得线程 sleep。 自旋锁会独占cpu时间片所以单核不能使用。 可重入锁ReentrantLock ⚫ synchronized 是关键字ReentrantLock 是 API 接口 ⚫ Lock 需要手动加锁手动释放锁 ⚫ synchronized 不可中断ReentrantLock 可中断、可超时 ⚫ synchronized 是非公平锁ReentrantLock 公平、非公平皆可 ⚫ ReentrantLock 支持 Condition多条件 //不可重入锁其实调用outer 的线程已经获取了 lock 锁但是不能在 inner 中重复利用已经获取的锁资源这种锁即称之为不可重入可重入就意味着 public class UnReentrant{Lock lock new Lock();public void outer(){lock.lock();inner();lock.unlock();}public void inner(){lock.lock();//do somethinglock.unlock();} }线程可以进入任何一个它已经拥有的锁所同步着的代码块。synchronized、ReentrantLock 都是可重入的锁。 线程池参数以及大小 ⚫ corePoolSize 核心线程大小最小可以同时运行的线程数量。 ⚫ maximumPoolSize 线程池最大线程数量当队列中存放的任务达到队列容量的时候当前可以同时运行的线程数量变为最大线程数。 ⚫ keepAliveTime 空闲线程存活时间。 ⚫ unit 空间线程存活时间单位。 ⚫ workQueue 工作队列达到核心线程数的话信任就会被存放在队列中。 ⚫ threadFactory 线程工厂。 ⚫ handler 拒绝策略。 ⚫ 如果当前线程数corePoolSize如果是则创建新的线程执行该任务 。 ⚫ 如果当前线程数corePoolSize则将任务存入 BlockingQueue 。 ⚫ 如果阻塞队列已满且当前线程数maximumPoolSize则新建线程执行该任务。 ⚫ 如果阻塞队列已满且当前线程数maximumPoolSize则抛出异常 。 ⚫ RejectedExecutionException告诉调用者无法再接受任务了(默认是丢弃任务并抛出异常) 注意4个拒绝策略默认丢弃不抛出异常丢弃最前面任务重新提交被拒绝任务callerRun由调用线程处理该任务。如果给用户发消息超出队列建议用callerRun或者消息队列。 协程是用户态的线程是对普通线程更细粒度的划分。它是在用户态运行的由用户自行调度所以也就避免了频繁的上下文切换问题。java不如GO语音成熟。 CPU密集型任务n11的目的是替补有其他异常不至于CPU处于空闲状态。I/O密集型任务200起步线程等待时间线程CPU时间/线程CPU时间 * CPU数目。虽然I/O线程数量增多会造成非常频繁的上下文切换进而影响效率。但在互联网应用中它却是一个优秀的解决方案 排查OOM的问题 增加参数 -XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/tmp/heapdump.hprof当 OOM 发生时自动 dump 堆内存信息到指定目录。jstat查看监控JVM的内存和GC情况先观察问题大概出在上面区域。使用MAT工具载入dump文件分析大对象占用情况。比如hashmap缓存可用软引用。 缓存穿透、缓存击穿和缓存雪崩 缓存穿透多个key 大量并发查询不存在的KEY同时给缓存和数据库带来压力ddos攻击、数据被误删。 Bloom 过滤或 RoaingBitmap 判断 KEY 是否存在如果布隆过滤器中没有查到这个数据就不去数据库中查。在处理请求前增加恶意请求检查如果检测到是恶意攻击则拒绝进行服务。 缓存击穿单一key 某个 KEY 失效的时候正好有大量并发请求访问这个 KEY。 KEY 的更新操作添加全局互斥锁。完全以缓存为准使用延迟异步加载的策略异步线程负责维护缓存的数据定期或根据条件触发更新这样就不会触发更新。 缓存雪崩 当某一时刻发生大规模的缓存失效的情况导致大量的请求无法获取数据从而将流量压力传导到数据库上导致数据库压力过大甚至宕机大量的数据同一个时间失效。 更新策略在时间上做到比较平均随机值。使用的热数据尽量分散到不同的机器上。多台机器做主从复制或者多副本实现高可用。实现熔断限流机制对系统进行负载能力控制。对于非核心功能的业务拒绝其请求只允许核心功能业务访问数据库获取数据。服务降级提供默认返回值或简单的提示信息。 LRU-最近最少使用策略 使用双向链表实现的队列队列的最大容量为缓存的大小。在使用过程中把最近使用的页面移动到队列头最近没有使用的页面将被放在队列尾的位置。使用一个哈希表把页号作为键把缓存在队列中的节点的地址作为值只需要把这个页对应的节点移动到队列的前面。 堆内存 用于分配对象的存储空间。-Xmx最大、-Xms最小、-Xmn、-XXjava8以后无效meta空间无限 数据库 ⚫ SQL注入攻击 者把 SQL 命令插入到 Web 表单的输入域或页面请求的查询字符串欺骗服务器执行恶意的 SQL 命令。 在利用表单输入的内容构造 SQL 命令之前把所有输入内容过滤一番就可以了。 对SQL慢查询优化❤️ 分析语句是否加载了不必要的字段/数据。尽量缩小数据量分析SQL执行计划思考可能的优化点是否命中索引等。 like以%开头索引无效当like以结尾索引有效or语句前后均为索引时索引生效组合索引左匹配规则 。尽量按照最左前缀原则来使用联合索引并且将区分度高的列放在前面数据类型出现隐式转换如varchar不加单引号的数字。索引列上使用IS NULL。索引不索引空值的所以索引列避免null索引列使用not、、!不会使用索引。索引字段进行计算操作函数操作时不会使用索引。5.7及以上可以建立函数或运算的索引当全表扫描比索引快的时候。数据少直接一次聚簇查询不用多次查询索引再聚簇。 复杂的语句查看有没有嵌套子查询。如果数据量太大考虑分表利用缓存减少查询次数sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。 limit 1000000 加载很慢处理 如果 id 是连续的可以这样返回上次查询的最大记录(偏移量)再往下 limit \qquad 【select idname from employee where id1000000 limit 10】在业务允许的情况下限制页数有没有必要查这么后的分页啦。因为绝大多数用户都不会往后翻太多页。order by 索引id 为索引select idname from employee order by id limit 100000010利用延迟关联或者子查询优化超多分页场景SELECT a.* FROM employee a, (select id from employee where 条件 LIMIT 1000000,10 ) b where a.idb.id节约反查询其它字段数据丢弃时间 Mysql索引分类 单列索引普通索引、唯一索引、主键索引组合索引多个字段遵循最左前缀集合。全文索引在 MyISAM 引擎上才能使用只能在 CHAR,VARCHAR,TEXT 类型字段上使用全文索引空间索引空间索引是对空间数据类型的字段建立的索引MySQL 中的空间数据类型有四种GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时使用 SPATIAL 关键字。要求引擎为 MyISAM创建空间索引的列必须将其声明为 NOT NULL。 什么是散列表 select * 和 select 1 哈希表Hash table也叫散列表是根据关键码值(Key value)而直接进行访问的数据结构。也就是说它通过把关键码值映射到表中一个位置来访问记录以加快查找的速度。这个映射函数叫做散列函数存放记录的数组叫做散列表。 有时候为了提高效率只是为了测试下某个表中是否存在记录就用 1 来代替。 索引分类区别 ⚫ 索引是一种特殊的文件(InnoDB 数据表上的索引是表空间的一个组成部分)它们包含着对数据表里所有记录的引用指针。 ⚫ 普通索引(由关键字 KEY 或 INDEX 定义的索引)的唯一任务是加快对数据的访问速度。 ⚫ 普通索引允许被索引的数据列包含重复的值。如果能确定某个数据列将只包含彼此各不相同的值在为这个数据列创建索引的时候就应该用关键字 UNIQUE 把它定义为一个唯一索引。也就是说唯一索引可以保证数据记录的唯一性。 ⚫ 主键是一种特殊的唯一索引在一张表中只能定义一个主键索引主键用于唯一标识一条记录使用关键字 PRIMARY KEY 来创建。 ⚫ 索引可以覆盖多个数据列如像 INDEX(columnA, columnB)索引这就是联合索引。 ⚫ 索引可以极大的提高数据的查询速度但是会降低插入、删除、更新表的速度因为在执行这些写操作时还要操作索引文件。 Mysql事务的特性 原子性即不可分割性事务要么全部被执行要么就全部不被执行一致性或可串性。事务的执行使得数据库从一种正确状态转换成另一种正确状态。隔离性。在事务正确提交之前不允许把该事务对数据的任何改变提供给任何其他事务。持久性。事务正确提交后其结果将永久保存在数据库中即使在事务提交后有了其他故障事务的处理结果也会得到保存。 Mysql聚簇索引、非聚簇索引的区别 聚簇索引InnoDB 数据库表行中数据的物理顺序与键值的逻辑索引顺序相同。一个表只能有一个聚簇索引因为一个表的物理顺序只有一种情况所以对应的聚簇索引只能有一个。 聚簇索引的叶子节点就是数据节点既存储索引值又在叶子节点存储行数据。 Innodb 创建表后生成的文件有 frm:创建表的语句 idb:表里面的数据索引文件 非聚集索引MyISAM 逻辑顺序与磁盘上行的物理存储顺序不同。非聚簇 索引的叶子节点仍然是索引节点只不过有指向对应数据块的指针。索引命中后需要回表查 询。 Myisam 创建表后生成的文件有 frm:创建表的语句 MYD:表里面的数据文件myisam data MYI:表里面的索引文件myisam index innodb 的次索引指向对主键的引用 (聚簇索引) myisam 的次索引和主索引都指向物理行 (非聚簇索引) Mysql默认InnoDB引擎B树范围查询 聚集索引是指数据库表行中数据的物理顺序与键值的逻辑索引顺序相同。聚簇索引的叶子节点就是数据节点既存储索引值又在叶子节点存储行数据。 哈希虽然能够提供 O(1) 的单数据行操作性能但是对于范围查询和排序却无法很好地支持最终导致全表扫描B 树能够在非叶节子点中存储数据但是这也导致在查询连续数据时可能会带来更多的随机 I/O而 B树的所有叶节点可以通过指针相互连接能够减少顺序遍历时产生的额外随机 I/OB树的叶子节点是数据阶段用了一个双向链表串联起来便于范围查找。 Mysql的锁 【表、行、页】 表级锁开销小加锁快不会出现死锁锁定粒度大发生锁冲突的概率最高并发度最低行级锁开销大加锁慢会出现死锁锁定粒度最小发生锁冲突的概率最低并发度也最高。页面锁开销和加锁时间界于表锁和行锁之间会出现死锁锁定粒度界于表锁和行锁之间并发度一般 MVCC多版本并发控制 脏读读取并发事务未提交的变化不可重复读如果并发事务更新同一行并提交则在重新读取行时获得不同的值。RR是可以重复读幻读如果另一个事务添加或删除范围内的某些行并提交则在重新执行范围查询后获取不同的行。RR可以解决幻读。 当前读select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读。它读取的是记录的最新版本读取时还要保证其他并发事务不能修改当前记录会对读取的记录进行加锁。 【悲观锁】 快照读不加锁的 select 操作就是快照读即不加锁的非阻塞读。快照读的实现是基于多版本并发控制即 MVCC ,可以认为 MVCC 是行锁的一个变种但它在很多情况下避免了加锁操作降低了开销既然是基于多版本即快照读可能读到的并不一定是数据的最新版本而有可能是之前的历史版本。【乐观锁】 MVCCMulti-Version Concurrency Control多版本并发控制,它是通过读取历史版本的数据来降低并发事务冲突从而提高并发性能的一种机制。在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能用更好的方式去处理读-写冲突做到即使有读写冲突时也能做到不加锁非阻塞并发读。写-写必须使用锁 事务版本号为事务分配单向增长的时间戳为每个修改保存一个版本版本与事务时间戳关联读操作只读该事务开始前的数据库的快照。表的隐藏列3个TRX_ID事务ID占6个字节ROLL_PTR回滚指针指向上一个版本存储rollback segment里占7个字节ROW_ID隐藏主键实际还有一个删除flag字段undo loginsert undo log-在回滚需要事务提交成功被立即丢弃update undo log-不仅在回滚在快照读时也需要。所以不能随便删除只有在快照或回滚不涉及该日志对应日志才被purge线程统一清除。read view就是事务进行快照读操作的时候生产的读视图。遵循一个可见性算法主要是将要被修改的数据的最新记录中的 DB_TRX_ID即当前事务 ID 取出来与系统当前其他活跃事务的 ID 去对比由 Read View 维护如果不可见用回滚指针取上一个版本直到满足条件就是当前事务可见的最新老版本。 undo 原理 在事务 1 修改该行(记录)数据时数据库会先对该行加排他锁然后把该行数据拷贝到 undo log 中作为旧记录既在 undo log 中有当前行的拷贝副本拷贝完毕后修改该行name为Tom并且修改隐藏字段的事务 ID 为当前事务 1 的 ID, 我们默认从 1 开始之后递增回滚指针指向拷贝到 undo log 的副本记录既表示我的上一个版本就是它事务提交后释放锁 read view流程 例4个事务13在活跃存在trx_list中4已经提交考察对象事务2执行快照读 先从undo log取出改行最新数据TRX_ID为4去跟Read View的 up_limit_id 比较4是否小于up_limit_id(1),不符合条件继续判断4是否大于等于 low_limit_id (5)也不符合最后判断4是否在活跃表trx_list中不在符合可见条件所以4对于事务2快照读是可见的。 RC隔离级别下,是每个快照读都会生成并获取最新的Read View而在RR默认隔离级别下则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View Mybatis 如何将对象转换成 SQL SQL 绑定是在加载 Mybatis 配置文件然后扫描到哪个 mapper 子节点再加载 mapper 映射文件扫描里面的 SQL 节点然后封装成对象MappedStatement在这个对象的SqlSource 封装着 sql 语句。所有的配置信息保存在 Configuration 类最后动态代理执行的时候取出来封装 sql 的对象执行 sql。 主从复制及延迟的原因 主库将变更写入 binlog 日志然后从库连接到主库之后从库有一个 IO 线程将主库的binlog 日志拷贝到自己本地写入一个 relay 中继日志中接着从库中有一个 SQL 线程会从中继日志读取 binlog然后执行 binlog 日志中的内容也就是在自己本地再次执行一遍 SQL。 主从延迟主库的从库太多从库硬件配置比主库差慢sql语句过多主从库之间网络延迟主库读写压力大。 Redis redis可以处理2^32 Redis 本质上是一个 Key-Value 类型的内存数据库很像 Memcached整个数据库统统加载在内存当中进行操作定期通过异步操作把数据库数据 flush 到硬盘上进行保存。 因为是纯内存操作Redis 的性能非常出色每秒可以处理超过 10 万次读写操作。 Redis 的出色之处不仅仅是性能Redis 最大的魅力是支持保存多种数据结构此外单个value 的最大限制是 1GB不像 Memcached 只能保存 1MB 的数据因此 Redis 可以用来实现很多有用的功能。 比方说用他的 List 来做 FIFO 双向链表实现一个轻量级的高性 能消息队列服务用他的 Set可以做高性能的 tag 系统等等。另外 Redis 也可以对存入的 Key-Value 设置 expire 时间因此也可以被当作一 个功能加强版的 Memcached 来用。 Redis 的主要缺点是数据库容量受到物理内存的限制不能用作海量数据的高性能读写因此Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。 数据结构 String简单的key-value类型 二进制安全的可以包含任何数据如jpg或序列化对象最大512M。需要计数的场景如访问次数、点赞与转发量等list 链表。发布于订阅或者说消息队列、慢查询。hashhashMap数组链表。对象数据的存储。如用户信息。setHashSet。数据不重复且交集、并集等sorted set增加了一个权重参数score元素能够按score进行有序排列。需要排序如直播系统中实时排行信息包含直播间在线用户列表各种礼物排行榜弹幕消息等。bitmap存储连续的二进制数字一个bit位来表示某个元素对应的值或者状态。需要保存状态信息如签到、登录并进一步分析的场景。如签到情况、活跃用户情况、用户行为统计。 数据结构压缩列表和跳跃表 压缩表(ziplist)本质上就是一个字节数组是 Redis 为了节约内存而设计的一种线性数据结构可以包含多个元素每个元素可以是一个字节数组或一个整数。跳跃表skiplist是一种有序数据结构它通过在每个节点中维持多个指向其他节点的指针从而达到快速访问节点的目的。跳跃表支持平均 OlogN、最坏 ON复杂度的节点查找还可以通过顺序性操作来批量处理节点。 Redis 中的数据都是热点数据 Redis 提供 6 种数据淘汰策略 volatile-lru从已设置过期时间的数据集server.db[i].expires中挑选最近最少使用的数据淘汰volatile-ttl从已设置过期时间的数据集server.db[i].expires中挑选将要过期的数据淘汰volatile-random从已设置过期时间的数据集server.db[i].expires中任意选择数据淘汰allkeys-lru从数据集server.db[i].dict中挑选最近最少使用的数据淘汰allkeys-random从数据集server.db[i].dict中任意选择数据淘汰no-enviction驱逐禁止驱逐数据 主从同步 redis 策略是无论如何首先会尝试进行增量同步如不成功要求从机进行全量同步。 全量同步 master 服务器会开启一个后台进程用于将 redis 中的数据生成一个 rdb 文件与此同时服务器会缓存所有接收到的来自客户端的写命令包含增、删、改当后台保存进程处理完毕后会将该 rdb 文件传递给 slave 服务器而 slave 服务器会将 rdb 文件保存在磁盘并通过读取该文件将数据加载到内存在此之后 master 服务器会将在此期间缓存的命令通过 redis 传输协议发送给 slave 服务器然后 slave 服务器将这些命令依次作用于自己本地的数据集上最终达到数据的一致性。 增量同步 部分同步的实现依赖于在 master 服务器内存中给每个 slave 服务器维护了一份同步日志和同步标识每个 slave 服务器在跟 master 服务器进行同步时都会携带自己的同步标识和上次同步的最后位置。当主从连接断掉之后slave 服务器隔断时间默认1s主动尝试和 master 服务器进行连接如果从服务器携带的偏移量标识还在 master 服务器上的同步备份日志中那么就从 slave 发送的偏移量开始继续上次的同步操作如果 slave发送的偏移量已经不再 master 的同步备份日志中可能由于主从之间断掉的时间比较长或者在断掉的短暂时间内 master 服务器接收到大量的写操作则必须进行一次全量更新。在部分同步过程中master 会将本地记录的同步备份日志中记录的指令依次发送给 slave 服务器从而达到数据一致。 持久化RDB和AOF优缺点 RDB RDB 持久化方式是将 Redis 某一时刻的数据持久化到磁盘中是一种快照式的持久化方法。 优点 ⚫ RDB 是一个非常紧凑有压缩的文件,它保存了某个时间点的数据,非常适用于数据的备份。 ⚫ RDB 作为一个非常紧凑有压缩的文件可以很方便传送到另一个远端数据中心 非常适用于灾难恢复。 ⚫ RDB 在保存 RDB 文件时父进程唯一需要做的就是 fork 出一个子进程,接下来的工作全部由子进程来做父进程不需要再做其他 IO 操作所以 RDB 持久化方式可以最大化 redis的性能。 ⚫ 与 AOF 相比在恢复大的数据集的时候RDB 方式会更快一些。 RDB 缺点 ⚫ Redis 意外宕机时会丢失部分数据。 ⚫ 当 Redis 数据量比较大时fork 的过程是非常耗时的fork 子进程时是会阻塞的在这期间 Redis 是不能响应客户端的请求的。 AOF AOF 方式是将执行过的写指令记录下来在数据恢复时按照从前到后的顺序再将指令都执行一遍。 优点 ⚫ 使用 AOF 会让你的 Redis 更加持久化。 ⚫ AOF 文件是一个只进行追加的日志文件不需要在写入时读取文件。 ⚫ Redis 可以在 AOF 文件体积变得过大时自动地在后台对 AOF 进行重写 。 ⚫ AOF 文件可读性高分析容易。 缺点 ⚫ 对于相同的数据来说AOF 文件大小通常要大于 RDB 文件。 ⚫ 根据所使用的 fsync 策略AOF 的速度可能会慢于 RDB。 混合持久化方式保证数据不丢失 Redis 4.0 之后新增的方式混合持久化是结合了 RDB 和 AOF 的优点在写入的时候先把当前的数据以 RDB 的形式写入文件的开头再将后续的操作命令以 AOF 的格式存入文件这样既能保证 Redis 重启时的速度又能减低数据丢失的风险。 在使用 RDB 进行快照时会通过子进程的方式进行实现 通过 fork 创建的子进程能够获得和父进程完全相同的内存空间父进程对内存的修改对于子进程是不可见的两者不会相互影响 进程间沟通可以用管道。通过 fork 创建子进程时不会立刻触发大量内存的拷贝内存在被修改时会以页为单位进行拷贝这也就避免了大量拷贝内存而带来的性能问题 Redis数据不丢失 ❤️ 使用 AOF 和 RDB 结合的方式RDB 做镜像全量持久化AOF 做增量持久化。因为 RDB 会耗费较长时间不够实时在停机的时候会导致大量丢失数据所以需要 AOF 来配合使用。 Redis 集群模式 master 节点持久化 如果采用了主从架构那么建议必须开启 master node 的持久化不建议用 slave node 作为 master node 的数据热备因为那样的话如果你关掉 master 的持久化可能在 master宕机重启的时候数据是空的然后可能一经过复制salve node 数据也丢了master 就会将空的数据集同步到 slave 上去所有 slave 的数据全部清空。 Redis 断点续传 从 redis 2.8 开始就支持主从复制的断点续传如果主从复制过程中网络连接断掉了那么可以接着上次复制的地方继续复制下去而不是从头开始复制一份。 主备切换的过程可能会导致数据丢失 解决异步复制和脑裂导致的数据丢失 redis.conf 中 min-slaves-to-write 1 min-slaves-max-lag 10 要求至少有 1 个 slave数据复制和同步的延迟不能超过 10 秒 如果说一旦所有的 slave数据复制和同步的延迟都超过了 10 秒钟那么这个时候master就不会再接收任何请求了 上面两个配置可以减少异步复制和脑裂导致的数据丢失。 消息队列MQ 消息队列使用场景 应用耦合多应用间通过消息队列对同一消息进行处理避免调用接口失败导致整个过程失败异步处理多应用对消息队列中同一消息进行处理应用间并发处理消息相比串行处理减少处理时间限流削峰广泛应用于秒杀或抢购活动中避免流量过大导致应用系统挂掉的情况消息驱动的系统系统分为消息队列、消息生产者、消息消费者生产者负责产生消息消费者(可能有多个)负责对消息进行处理 RabbitMQ保证消息不丢失 ❤️ 生产者开启事务同步性能差或者开启confirm模式异步性能好exchange持久化queue持久化、消息持久化。消费者关闭自动ACK。 Kafka Kafka 是一个分布式流式处理平台。整个架构中包含三个角色生产者Producer、代理Broker、消费者Consumer。 Kafka 给 Producer 和 Consumer 提供注册的接口数据从 Producer 发送到 BrokerBroker 承担一个中间缓存和分发的作用负责分发注册到系统中的 Consumer。 topic和patition PartitionKafka 可以将主题划分为多个分区Partition会根据分区规则选择把消息存储到哪个分区中只要分区规则设置的合理那么所有的消息将会被均匀的分布到不同的分区中这样就实现了负载均衡和水平扩展。Partition 的引入就是解决水平扩展问题的一个方案。 topicproducer 只需要关心消息发往哪个 topic而 consumer 只关心自己订阅哪个 topic并不关心每条消息存于整个集群的哪个 broker。 kafka偏移量保证顺序 消息在被追加到 Partition(分区)的时候都会分配一个特定的偏移量offset。Kafka 通过偏移量offset来保证消息在分区内的顺序性。发送消息的时候指定 key/Partition。 kafka信息完整性 ❤️ 生产者回调函数 Kafka 生产者(Producer) 使用 send方法发送消息实际上是异步的操作我们可以通过 get()方法获取调用结果但是这样也让它变为了同步操作可以采用为其添加回调函数的形式。 ListenableFutureSendResultString, Object future kafkaTemplate.send(topic, o); future.addCallback(result -logger.info(生产者成功发送消息到 topic:{} partition:{}的消息, result.getRecordMetadata().topic(), result.getRecordMetadata().partition()),ex - logger.error(生产者发送消失败原因{}, ex.getMessage()) );Producer 的 retries重试次数设置一个比较合理的值一般是 3 但是为了保证消息不丢失的话一般会设置比较大一点。设置完成之后当出现网络问题之后能够自动重试消息发送避免消息丢失。另外建议还要设置重试间隔因为间隔太小的话重试的效果就不明显了网络波动一次你 3 次一下子就重试完了。 消费者手动提交会出现重复消费幂等发送每条数据增加一个唯一标识 ID。 当消费者拉取到了分区的某个消息之后消费者会自动提交了 offset。自动提交的话会有一个问题试想一下当消费者刚拿到这个消息准备进行真正消费的时候突然挂掉了消息实际上并没有被消费但是 offset 却被自动提交了。 解决办法也比较粗暴我们手动关闭自动提交 offset每次在真正消费完消息之后再自己手动提交 offset 。 但是细心的朋友一定会发现这样会带来消息被重新消费的问题。比如你刚刚消费完消息之后还没提交 offset结果自己挂掉了那么这个消息理论上就会被消费两次。 生产者发送每条数据的时候里面加一个全局唯一的 id消费到了之后先根据这个 id去比如 Redis 里查一下之前消费过吗如果没有消费过就处理然后这个 id 写Redis。如果消费过就别处理了。基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了重复数据插入只会报错不会导致数据库中出现脏数据。 Broker只能降低 leader挂掉了follower没有完全同步leader中数据。 当我们配置了 unclean.leader.election.enable false 的话当 leader 副本发生故障时就不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader 这样降低了消息丢失的可能性。 Kafka、RabbitMQ、RocketMQ阿里 之间的区别 性能 消息中间件的性能主要衡量吞吐量Kafka 的吞吐量比 RabbitMQ 要高出 1~2 个数量级RabbitMQ 的单机 QPS 在万级别Kafka 的单机 QPS 能够达到百万级别。RocketMQ 单机写入 TPS 单实例约 7 万条/秒单机部署 3 个 Broker可以跑到最高 12 万条/秒消息大小10 个字节Kafka 如果开启幂等、事务等功能性能也会有所降低。 数据可靠性 Kafka 与 RabbitMQ 都具备多副本机制数据可靠性较高。RocketMQ 支持异步实时刷盘同步刷盘同步 Replication异步 Replication。 服务可用性 Kafka 采用集群部署分区与多副本的设计使得单节点宕机对服务无影响且支持消息容量的线性提升。RabbitMQ 支持集群部署集群节点数量有多种规格。RocketMQ 是分布式架构可用性高。 功能 Kafka 与 RabbitMQ 都是比较主流的两款消息中间件具备消息传递的基本功能但在一些特殊的功能方面存在差异RocketMQ 在阿里集团内部有大量的应用在使用。 算法 常见排序算法 1. 冒泡排序 o( n 2 n^2 n2) 【稳定】 o(1 通过重复走完数组的所有元素通过两两比较直到没有数可以交换的时候结束这个数再到下个数直到整个数组排好顺序。 2. 插入排序 o( n 2 n^2 n2) 【稳定】 o(1 每次从未排好序的数据堆中拿出一个数插入到已排好序的数据队列的正确位置。 3. 希尔排序 o( n 1.3 n^{1.3} n1.3) 【不稳定】 o(1插入排序改进版本 是插入排序的改进版。它将数组按照一定的步长分成若干个子序列对每个子序列进行插入排序然后逐步缩小步长最后对整个数组进行插入排序 4. 选择排序 o( n 2 n^2 n2) 【不稳定】o(1 每次从未排好序的数据堆中找到最小的数插入到已排好序的数据队列的头部。 5. 快速排序 o( N ∗ l o g N N*logN N∗logN) 【不稳定】o(logn) 以数据堆中的一个数为标准将数据堆分为小于等于和大于该数的两堆对于分割后的两堆数再分别利用上述方法进行分割以此类推直到堆中只有一个数为止。 6. 堆排序 O(N*logN) 【不稳定】o(1 将数据堆中的数两两组队排序对于排序好的这些子堆再两两组队排序以此类推直到只剩下一个堆。 7. 归并排序 O(N*logN) 【稳定】o(n 基于堆的排序算法分为最大堆和最小堆。排序分为两个过程堆的构造和堆的排序。 8. 基数排序 o(d(nr)) 【稳定】o® 9. 计数排序 o(nm) 【稳定】 o(m) 利用数组下标来确定元素的正确位置下标是值数组值是出现次数。 适用于一定范围内的整数排序。数组长度max-min1最小值做偏移量。 10. 桶排序 o(n) 【稳定】 o(n) 计数排序补充区间 计数排序补充用每一个桶代表一个区间范围里面承载一个或多个元素。 桶长度一般等于原元素长度区间跨度max-min/桶数量-1.元素放入桶numint((array[i]-min)*(bucketNum-1)/d)。类似hashmap数组链 1 亿个数据取出最大前 100 个有什么方法 快速排序最容易想到的方法是将数据全部排序然后在排序后的集合中进行查找最快的排序算法的时间复杂度一般为 Onlogn如快速排序。局部淘汰法-插入该方法与排序方法类似用一个容器保存前 10000 个数然后将剩余的所有数字——与容器内的最小数字相比如果所有后续的元素都比容器内的 10000 个数还小那么容器内这个 10000 个数就是最大 10000 个数。如果某一后续元素比容器内最小数字大则删掉容器内最小元素并将该元素插入容器最后遍历完这 1 亿个数得到的结果容器中保存的数即为最终结果了。此时的时间复杂度为 Onm^2其中 m 为容器的大小即 10000。分治法-桶将 1 亿个数据分成 100 份每份 100 万个数据找到每份数据中最大的 10000个最后在剩下的 10010000 个数据里面找出最大的 10000 个。如果 100 万数据选择足够理想那么可以过滤掉 1 亿数据里面 99%的数据。100 万个数据里面查找最大的10000 个数据的方法如下用快速排序的方法将数据分为 2 堆如果大的那堆个数 N大于 10000 个继续对大堆快速排序一次分成 2 堆如果大的那堆个数 N 大于 10000个继续对大堆快速排序一次分成 2 堆如果大堆个数 N 小于 10000 个就在小的那堆里面快速排序一次找第 10000-n 大的数字递归以上过程就可以找到第 1w 大的数。参考上面的找出第 1w 大数字就可以类似的方法找到前 10000 大数字了。此种方法需要每次的内存空间为 10^644MB一共需要 101 次这样的比较。最小堆法首先读入前 10000 个数来创建大小为 10000 的最小堆建堆的时间复杂度为 Omlogmm 为数组的大小即为 10000然后遍历后续的数字并于堆顶最小数字进行比较。如果比最小的数小则继续读取后续数字如果比堆顶数字大则替换堆顶元素并重新调整堆为最小堆。整个过程直至 1 亿个数全部遍历完为止。然后按照中序遍历的方式输出当前堆中的所有 10000 个数字。该算法的时间复杂度为 Onmlogm空间复杂度是 10000常数Hash 法如果这 1 亿个书里面有很多重复的数先通过 Hash 法把这 1 亿个数字去重复这样如果重复率很高的话会减少很大的内存用量从而缩小运算空间然后通过分治法或最小堆法查找最大的 10000 个数。 z x y z x y zxy a b a \qquad b ab a 1 a_1 a1​ ; a 1 a^1 a1 ; a 11 b 1 2 a_{11} b^{\frac{1}{2}} a11​b21​ ; z x ⋅ y z x \cdot y zx⋅y ; z x × y z x \times y zx×y; z x ÷ y z x \div y zx÷y
http://www.zqtcl.cn/news/506215/

相关文章:

  • 怎么寻找做有益做网站的客户大连网站推广
  • 湖南网站开发企业excel网站建设
  • 安康网站建设技巧腾讯建设网站视频下载
  • 如何能让企业做网站的打算中企动力做网站贵吗
  • wordpress 空间常州seo
  • 网站负责人备案采集照具体要求湛江网吧
  • 长春建站模板制作php网站空间购买
  • 网站域名到期怎么办食品包装设计的介绍
  • 建设网站专栏台州cms模板建站
  • 网站建设套餐方案湛江网站如何制作
  • wordpress网站怎么打开西安企业做网站多少钱
  • 电子商务网站建设的实训报告网页美工设计夏霍
  • 在一呼百应上做网站行吗江西省住房和城乡建设厅的网站
  • 对百度网站进行分析山水人家装饰公司
  • 接网站开发广州仿站定制模板建站
  • 资源网站源码下载制作软件的app有哪些
  • 免备案空间网站电子商务网站经营特色分析的主要内容包括
  • 遨游建站网站设计的基本知识
  • 延津县建设局网站景安网站上传完还要怎么做
  • 模板做网站达州住房和城乡建设部网站
  • 高端网站定做公司企业文化模板
  • iis7.5添加网站销售订单管理系统
  • 网站开发模板代码外贸流程知识
  • 免费网站有哪些邯郸去哪做网站改版
  • 商务网站开发的工作任务湖南专业网站建设
  • 怎样搭建免费网站什么网站做网页好
  • flash工作室网站模板天津seo培训班在哪里
  • 怎么做游戏推广网站扬中商城官网
  • html5 公众号 网站开发顺德手机网站建设
  • 上海医疗网站备案表千库网是什么