MySQL 同步 ES 流程如下:
通过定时任务的形式触发同步,比如间隔半天或一天的时间频率
同步的形式为增量同步,根据更新时间的机制,比如第一次同步查询 >= 1970-01-01 00:00:00.0
记录最大的更新时间进行存储,下次更新同步以此为条件
以分页的形式获取数据,当前页数量加一,循环到最后一页
在这里问题也就出现了,MySQL 查询分页 OFFSET 越深入,性能越差,初步估计线上 MCS_PROD 表中记录在 1000w 左右
如果按照每页 10 条,OFFSET 值会拖垮查询性能,进而形成一个 "性能深渊"
同步类代码针对此问题有两种优化方式:
采用游标、流式方案进行优化
优化深分页性能
MySQL 耗费了 大量随机 I/O 在回表查询聚簇索引的数据上,而这 100000 次随机 I/O 查询数据不会出现在结果集中
如果系统并发量稍微高一点,每次查询扫描超过 100000 行,性能肯定堪忧,另外 LIMIT 分页 OFFSET 越深,性能越差
Latest posts made by q464824151
-
MySQL数据库深分页查询优化
-
需要拥有什么才能达到丝滑的消息治理
-
强大的监控体系:服务端监控。MQ本身就是一个程序,那么这个程序本身的健康状况我们需要关注,假设我们的MQ是Java开发的,那么JVM指标也需要监控。同时发消息的耗时啊等等都需要监控。
-
服务器层面监控:无论你的MQ是部署在物理机上还是容器上,MQ都依赖它。所以对于服务器层面的监控也是必不可少。像CPU, 内存,磁盘IO,网络等等。
-
业务层面监控:消息堆积对在线业务场景有很大的影响,必须具备实时监控的能力。当有消息堆积时及时通过 告警机制通知业务团队进行处理。
-
QPS飙升:当整个集群或者某个Topic级别的消息发送量在1分钟之内极度飙升,那么需要及时关注。因为很有可能会超过MQ本身的承受范围,同时消费方也会产生堆积问题。
-
死信消息:当消费者消费某条消息一直失败的时候,已经超过了最大重试次数,这条消息会进入死信队列。这块也是需要及时监控,因为一旦有死信消息,也就是意味着你的消费逻辑存在问题,需要及时修复。
-
消费失败率:当消费方消息消息,频繁失败的时候,也需要有对应的监控告警。这个其实在MQ的client包里面就可以进行数据的埋点上报。
-
强大的运维体系:集群自动化部署:当规模到达一定的时候,一定要具备高度自动化的能力。就像云产品一样,今天业务团队需要一个新的集群,直接走工单审批,等审批通过后集群自动创建好。
-
集群弹性扩容:有了自动化部署后,弹性扩容也是水到渠成的事情。当集群的QPS超过了本身能够承载的量,必然会进行限流,但是一旦限流,也就意味着业务功能有损。所以具备弹性扩容的能力非常重要,最好是自动化的弹性扩容。
资源隔离也十分的非常重要,我们用MQ也是不同的业务场景,不同的场景对稳定性的要求也不一样。比如在线交易场景的稳定性肯定要高于一些后台操作类的场景。
比如订单会将订单的生命周期通过消息的形式发送出去,各个业务域按需进行订阅。如果订单的集群跟其他业务共用一套,当其他业务出现某些问题的时候,那么就会影响订单的消息,所以要针对业务场景进行资源的隔离。
-
强大的治理后台:Topic&Group管理:Topic和Group的管理是最基本的功能,每个迭代都会有新增的Topic和Group,所以能够通过后台快速创建Topic和Group至关重要。
同时也能查看Topic中的消息,Group的在线情况,消费情况等等信息。 -
账号权限体系:当公司组织规模大了后,严格的账号权限体系必不可少。无论是Topic和Group的创建,还是消息查询的权限,MQ吞吐量的数据等等,都需要严格控制权限,不能随意公开。其次,对于操作类的申请,要结合审批流进行多层审批,提高安全系数。
-
消息轨迹:消息轨迹在实际排查问题的时候非常有用,通过消息轨迹,我们可以查看某条消息到底有没有被消费?是谁消费了这条消息?消费耗时了多久?这个功能在很多MQ中都是支持的。
-
消费位重置:消费位重置指的是通过后台,我们可以将消费点进行倒退到某一个时间点,然后重新将这个时间点之后的消息进行投递,让消费者再消费一次的操作。一般情况下这个功能是用不到的,但是某些场景还是有可能用到。
其实最常用的就是某一条消息进行重新投递,比如我们在测试环境出了一个什么问题,然后想验证下,这个时候可以去后台将这个消息重新投递一次,然后再观察下日志,去排查问题。
-
-
模块依赖和启动模块
一个业务应用通常由多个模块组成,ABP 框架允许您声明模块之间的依赖关系。一个应用必须要有一个启动模块。启动模块可以依赖于其他模块,其他模块可以再依赖于其他模块,以此类推。
下图是一个简单的模块依赖关系图:
如果所示,如果模块 A 依赖于模块 B,则模块 B 总是在模块 A 之前初始化。这允许模块 A 使用、设置、更改或覆盖模块 B 定义的配置和服务。对于示例图,模块初始化的顺序应该是:G、F、E、D、B、C、A。模块生命周期:AbpModule中定义的生命周期方法,除了上面看到的ConfigureServices和OnApplicationInitialization,下面罗列其他生命周期相关方法:
- PreConfigureServices: 这个方法在ConfigureServices方法之前被调用。它允许您配置服务之前执行的代码。
- ConfigureServices:这是配置模块和注册服务的主要方法。
- PostConfigureServices: 该方法在ConfigureServices之后调用(包括依赖于您模块的模块),这里可以配置服务后执行的代码。
- OnPreApplicationInitialization: 这个方法在OnApplicationInitialization之前被调用。在这个阶段,您可以从依赖注入中解析服务,因为服务已经被初始化。
- OnApplicationInitalization:此方法用来配置 ASP.NET Core 请求管道并初始化您的服务。
- OnApplicationInitialization: 这个方法在初始化阶段后被调用。
- OnApplicationShutdown:您可以根据需要自己实现模块的关闭逻辑。
带Pre…和Post…前缀的方法与原始方法具有相同的目的。它们提供了一种在模块之前或之后执行的一些配置/初始化代码,一般情况下我们很少使用到。
-
一起创建并配置一个
创建 Filter 需要实现 javax.servlet.Filter 接口,或者继承实现了 Filter 接口的父类。Filter 接口中定义了三个方法:
init方法:用于Web 程序启动时被调用,用于初始化 Filter。
doFilter:在客户端的请求到达时被调用,doFilter 方法中定义了 Filter 的主要处理逻辑,同时该方法还负责将请求传递给下一个 Filter 或 Servlet。
destroy:在 Web 程序关闭时被调用,用于销毁一些资源。
init 方法的 filterConfig 参数封装了当前 Filter 的配置信息,在 Filter 初始化时,我们将 Filter 的名称打印在控制台。doFilter方法定义了 Filter 拦截到用户请求后的处理逻辑,filterChain.doFilter(servletRequest, servletResponse);指的是将请求传递给一下个 Filter 或 Servlet,如果不添加该语句,那么请求就不会向后传递,自然也不会被处理。在该语句之后,可以添加对响应的处理逻辑(如果要修改响应的 Header,可直接在该语句之前修改;如果要修改响应的内容,则需要在该语句之后,且需要自定义一个 response)。
destroy 方法中,我们输出 "Filter 被回收" 的提示信息。在下面的代码中,setFilter 方法用于设置 Filter 的类型;addUrlPatterns 方法用于设置拦截的规则;setName 方法用于设置 Filter 的名称;setOrder 方法用于设置 Filter 的优先级,数字越小优先级越高。
-
浅聊一下过滤器和拦截器
过滤器 Filter 是 Sun 公司在 Servlet 2.3 规范中添加的新功能,其作用是对客户端发送给 Servlet 的请求以及对 Servlet 返回给客户端的响应做一些定制化的处理,例如校验请求的参数、设置请求/响应的 Header、修改请求/响应的内容等。
Filter 引入了过滤链(Filter Chain)的概念,一个 Web 应用可以部署多个 Filter,这些 Filter 会组成一种链式结构,客户端的请求在到达 Servlet 之前会一直在这个链上传递,不同的 Filter 负责对请求/响应做不同的处理。 Filter 的处理流程如下图所示:
拦截器 Interceptor 是 Spring MVC 中的高级组件之一,其作用是拦截用户的请求,并在请求处理前后做一些自定义的处理,如校验权限、记录日志等。这一点和 Filter 非常相似,但不同的是,Filter 在请求到达 Servlet 之前对请求进行拦截,而 Interceptor 则是在请求到达 Controller 之前对请求进行拦截,响应也同理。
与 Filter 一样,Interceptor 也是 AOP 编程思想的体现,且 Interceptor 也具备链式结构,我们在项目中可以配置多个 Interceptor,当请求到达时,每个 Interceptor 根据其声明的顺序依次执行。
postHandle 方法和 afterCompletion 方法执行的前提条件是 preHandle 方法的返回值为 true。如果 Controller 抛出异常,那么 postHandle 方法将不会执行,afterCompletion 方法则一定执行。
-
哈希函数
哈希函数,或者叫散列函数,是一种从任何一种数据中创建一个数字指纹(摘要)的方法,散列函数把数据压缩压缩(或者放大)成一个长度固定的摘要。
哈希函数的输入空间(文本或者二进制数据)是无限大,但是输出空间(一个固定长度的摘要)却是有限的。将「无限」映射到「有限」,不可避免的会有概率不同的输入得到相同的输出,这种情况我们称为碰撞(collision)。
一个简单的哈希函数是直接对输入数据/文本的字节求和。它会导致大量的碰撞,例如 hello 和 ehllo 将具有相同的哈希值。
更好的哈希函数可以使用这样的方案:它将第一个字节作为状态,然后转换状态(例如,将它乘以像 31 这样的素数),然后将下一个字节添加到状态,然后再次转换状态并添加下一个字节等。这样的操作可以显着降低碰撞概率并产生更均匀的分布。
一个好的「加密哈希函数」必须满足抗碰撞(collision-resistant)和不可逆(irreversible)这两个条件。
抗碰撞是指通过统计学方法(彩虹表)很难或几乎不可能猜出哈希值对应的原始数据,而不可逆则是说攻击者很难或几乎不可能从算法层面通过哈希值逆向演算出原始数据。一个理想的加密哈希函数,应当具有如下属性:- 快速:计算速度要足够快;
- 确定性:对同样的输入,应该总是产生同样的输出;
- 难以分析:对输入的任何微小改动,都应该使输出完全发生变化;
- 不可逆:从其哈希值逆向演算出输入值应该是不可行的。这意味着没有比暴力破解更好的破解方法。
- 无碰撞:找到具有相同哈希值的两条不同消息应该非常困难(或几乎不可能)。
现代加密哈希函数(如 SHA2 和 SHA3)都具有上述几个属性,并被广泛应用在多个领域,各种现代编程语言和平台的标准库中基本都包含这些常用的哈希函数。
-
CoLAKE的模型方法
CoLAKE模型存在的目的是对结构化的、无标签的word-knowledge graphs的数据上对结合了上下文的语言和知识表征进行联合的、同步的预训练。其实现方法是先构造出输入句子对应的WK graphs,然后对模型结构和训练目标稍作改动。具体实现如下:
- 构造一个WK graphs:先对输入的句子中的mention进行识别,然后通过一个entity linker的功能,找到其在特定知识图谱中对应的entity。Mention结点被替换为相应的entity,这个操作被称作为anchor nodes。
以这个anchor node为中心,还可以提取到多种三元组关系来形成子图,提取到的这些子图和句子中的词语,以及anchor node一起拼接起来形成WK graph。
实际上,对于每个anchor node,作者随机玄奇最多15个相邻关系和实体来构建WK graph,并且只考虑anchor node在三元组中是head的情况。
-
模型结构改动:接下来构建好的WK graph进入Transformer Encoder,CoLAKE对embedding层和encoder层都做了相应的改造。
-
Embedding Layer:输入的embedding是token embedding,type embedding和position embedding的加和。
其中,输入的token embedding需要构建word、relation和entity三种类型的查找表。
对于word embedding而言,采用Roberta一样的BPE的分词方法,将词语切割为字词用以维护大规模的词典。相应的,对每一个entity和relation就沿用一般的知识嵌套方法一样来获取对应的embedding。
然后输入中token embedding则是由word embedding,entity embedding, relation embedding拼接起来,这三者是同样维度的向量。因为WK graph会将原本的输入以token为单位进行重组,因此输入的token序列会看起来像是一段错乱的序列,因此需要对应修正其type input和position input。其中对于每个token,其同一对应的type会用来表征该token对应的node的类型,比如是word,entity或者是relation;对应的position也是根据WK graph赋予的。下图给出了一个具体的例子进行说明:
-
VLIW
software和hardware之间总是存在tradeoff:要么是hardware结构复杂,software灵活。要么是hardware结构保持简洁清晰,software干一些脏活累活。VLIW就是属于后一种。
与Superscalar架构不同,VLIW将检查指令依赖关系的工作全部交给了编译器:编译器将没有依赖关系的指令打包成一个bundle,hardware不需要动态调度,只是负责取指、执行。VLIW创始人Josh Fisher于1983年发表的论文Very Long Instruction Word architectures and the ELI-512提出了VLIW的Trace Scheduling思想,优化代码中最经常执行的路径。不过当时很多科学家对这种方式持怀疑态度,也许从学术角度来看这种思想有点意思,但人们压根不相信,可以建造一台依靠software而不是hardware来提速的计算机。但是Josh Fisher一直相信VLIW架构远远超出了一个学术项目范畴,它有潜力改变所有的科学计算。
-
函数式编程
函数式编程关心数据的映射,命令式编程关心解决问题的步骤
函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
只用"表达式",不用"语句":"表达式"(expression)是一个单纯的运算过程,总是有返回值;"语句"(statement)是执行某种操作,没有返回值。函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
没有"副作用"。所谓"副作用"(side effect),指的是函数内部与外部互动(最典型的情况,就是修改全局变量的值),产生运算以外的其他结果。
函数式编程强调没有"副作用",意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值。
函数式编程不依赖、也不会改变外界的状态,只要给定输入参数,返回的结果必定相同。因此,每一个函数都可以被看做独立单元,很有利于进行单元测试(unit testing)和除错(debugging),以及模块化组合。
高阶函数:假如你写了一大堆程序而不考虑什么类结构设计,然后发现有一部分代码重复了几次,于是你就会把这部分代码独立出来作为一个函数以便多次调用。
如果你发现这个函数里有一部分逻辑需要在不同的情况下实现不同的行为,那么你可以把这部分逻辑独立出来作为一个高阶函数。
-
大数据批处理和流处理标准Apache Beam
Apache Beam 是 Apache 软件基金会越来越多的数据流项目中最新增添的成员,是 Google 在2016年2月份贡献给 Apache 基金会的孵化项目。
这个项目的名称表明了设计:结合了批处理Batch模式和数据流Stream处理模式。它基于一种统一模式,用于定义和执行数据并行处理管道pipeline,这些管理随带一套针对特定语言的SDK用于构建管道,以及针对特定运行时环境的Runner用于执行管道。
Apache Beam 的主要目标是统一批处理和流处理的编程范式,为无限、乱序、web-scale的数据集处理提供简单灵活,功能丰富以及表达能力十分强大的SDK。Apache Beam项目重点在于数据处理的编程范式和接口定义,但是这并不涉及具体执行引擎的实现,Apache Beam希望基于Beam开发的数据处理程序可以执行在任意的分布式计算引擎上。