• 关于

    主信息块什么意思

    的搜索结果

问题

游戏IP是什么意思?

游客bnlxddh3fwntw 2020-04-25 14:23:41 12 浏览量 回答数 1

问题

【Java学习全家桶】1460道Java热门问题,阿里百位技术专家答疑解惑

管理贝贝 2019-12-01 20:07:15 27612 浏览量 回答数 19

问题

初识Hadoop:报错

kun坤 2020-06-07 00:57:43 0 浏览量 回答数 1

万券齐发助力企业上云,爆款产品低至2.2折起!

限量神券最高减1000,抢完即止!云服务器ECS新用户首购低至0.95折!

回答

一般都是调用现成的命令,解析命令输出######回复 @Hyacinthus_M : 几乎所有编程语言都有执行命令获取输出的方法的,获取完之后,按行正则表达式匹配提取信息即可######回复 @Mallon : 那你能给点这方面解析的资料吗?我的GREP等用得不好,首先得有思路了,不然我会觉得还没有改代码来得可行。######回复 @Mallon : 意思是先iwlist eth0 scan | grep "Target"吗,那怎么样设置密码呢?在这方面我很迷惑######回复 @Hyacinthus_M : 你在程序里调用命令行,Linux下基本就是这个套路######可是要求是写程序。 这个程序是面对客户的,我总不能要求客户还能用命令行吧。我只想知道有没有代码量小点的方法。###### 是这样的。我开始的时候也想过通过用system调用通过shell的工具来完成任务,可是shell功底有些不足。 我所需要的只是iwlist eth0 scan 结果中的ESSID, TXPOWER,还有就是加密相关的内容。 而又要求将这些内容以每个网络块的方式输出,而通过grep 后,所有的相同的项都在一起了。 那么要通过样才能实现我的需求呢,shell我真的很基本。 ###### 引用来自“Hyacinthus_M”的答案 是这样的。我开始的时候也想过通过用system调用通过shell的工具来完成任务,可是shell功底有些不足。 我所需要的只是iwlist eth0 scan 结果中的ESSID, TXPOWER,还有就是加密相关的内容。 而又要求将这些内容以每个网络块的方式输出,而通过grep 后,所有的相同的项都在一起了。 那么要通过样才能实现我的需求呢,shell我真的很基本。 shell作为程序设计语言功能太弱了,上perl python吧,推荐后者,呵呵 ######回复 @Hyacinthus_M : 纯C麻烦,我没研究过...######这个程序只能C做,纯的。python,呵呵。说重点好吗,######grep是shell用的,其它语言都内置正则模块的###### http://stackoverflow.com/questions/646241/c-run-a-system-command-and-get-output######大哥,你没有把握我的意思啊。###### 正则就用PCRE吧 http://www.pcre.org/ ###### 按照楼主的描述,我猜公司是让你在一个嵌入式设备上实现该功能吧。 wireless-tools需要跟底层的硬件配合使用,它本身无非就是通过socket从内核获取到信息。设备的wifi驱动也是你们自己搞的?如果从驱动着手的话,可能还更简单点。方便的话,可以说一下你们所使用的平台。 ######我也是刚去上班不久的,对硬件了解也少,驱动好像不是他们写的。 我已经改了不少了,这个星期应该能完成吧。 iw_get_ext()这个接口?######是用的baspberrypi的。系统基本和debian的一样。只是只有基本的一些功能。######回复 @txgcwm : 大哥。真是谢谢你啊。 我真的很需要这样的交流。 你也早点睡啊。我先睡了。######回复 @Hyacinthus_M : 大哥。真是谢谢你啊。 我真的很需要这样的交流。 你也早点睡啊。我先睡了。######回复 @txgcwm : 你好。我对你说的“楼主可以使用fopen来执行“iwlist eth0 scan”指令,将获取到的结果逐个解析出来”的意思不是太理解? 是先将结果定向到一个文件中,然后再解析? 好像你不是这个意思啊,有些地方我不是很懂,可以请你说具体点吗######回复 @txgcwm : 是啊,我也想通过调用system()来运行iwlist eth0 scan来做,然后解析输出,包括无线网络的配置,同样也可以用同样的方法来做,但是我的问题出在解析部分。 希望你给点具体点的建议。######如果是那样的话,预计你执行那些指令是没有多少问题的。 楼主可以使用fopen来执行“iwlist eth0 scan”指令,将获取到的结果逐个解析出来(这样比你修改代码好多了,字符串解析应该相对简单一些嘛)。###### 引用来自“txgcwm”的答案 按照楼主的描述,我猜公司是让你在一个嵌入式设备上实现该功能吧。 wireless-tools需要跟底层的硬件配合使用,它本身无非就是通过socket从内核获取到信息。设备的wifi驱动也是你们自己搞的?如果从驱动着手的话,可能还更简单点。方便的话,可以说一下你们所使用的平台。 是的呢。我不是科班出身的,一直在自己摸索,有很多东西都不太懂,很多时候都是在用笨方法来解决。      开始的时候,我的确是想用SHELL来实现的,然后解析。但是我的头说要用程序写, 开始真是一点头绪都没有,我只能想到去修改wireless-tools的源程序了。 我觉得自己可以解决的。 不过,还是希望你能给点建议。 ######楼主,现在这个问题肯定解决了吧!能不能分享一下最后的结果啊?我现在也遇到了和你同样的问题,可是我没有楼主那么厉害,可以直接更改源代码。能不能帮一下小弟,谢谢先!!!######回复 @txgcwm : 你好,我已经使用解析的方法完成了基本功能。最近在使用基于wireless.h,iwlib.h iwlib.c三个文件(丢弃iwlist.c iwconfig.c)来重写该程序,在main.c中,包括了#include "iwlib.h", 并且在iwlib.h声明了所有的函数原型,所有的函数实现均在iwlib.c中,######回复 @txgcwm : 恩。我已经在baspberrypi的板子上运行过wireless-tools的工具了,iwlist scan结果正常。######回复 @txgcwm : 比如,把scan的结果定向到一个文件中,然后再通过字符串的操作来实现,还是scan | grep 的方法来实现呢? 后者我不太熟悉,我的功底还没能满足需求,但是前者,对多个关健字的解析方面的算法实现,思路不是很明确。######回复 @Hyacinthus_M : 而且我建议你将这个工具在没有修改前先编译进去,看看是否能够输出你想要的数据(没有的话可能底层不支持,那样的话你上层改了也没有用)。###### #include <sys/wait.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <stdio.h> #define MAXLINE 1024 int main(int argc, char **argv) { char buf[MAXLINE]; int rc = 0; FILE *fp = NULL; fp = popen("iwlist wlan0 scan", "r"); if(fp == NULL) { perror("popen error!"); return -1; } while(fgets(buf, MAXLINE, fp) != NULL) { int len = strlen(buf); if(buf[len-1] == '\n') buf[len-1] = '\0'; printf("%s\r\n", buf); } if((rc = pclose(fp)) < 0) { perror("pclose error!"); return -1; } return 0; }######回复 @txgcwm : 但是在主函数中,调用相关函数经常出现“undefined reference to”的错误,这些错误我有些不能理解。我在网上也看了一些相关的资料,但是是没能解决,希望你给点建议。######回复 @Hyacinthus_M : 给你一个大概的思路: 1、从数据上看,每个wifi信息都是以cell开头的,以此为标记; 2、使用isspace函数将前面的所有空格清除; 3、通过strcmp对比你要获取的数据项,然后将其后的数据保存。 你自己尝试解析,后续有问题,下班了再说。######回复 @txgcwm : 就是。我觉得他个人有点偏执。同样是用上层。他是把这个任务交给我的。我的公司小,只有十个人,其中几个还是实习的。我先用解析的方法做出来,如果有什么不懂的,还要请教你啊。 对了,设置关键字字符串数组,每一行一行的解析,是不是用strstr()简单来对每一行的内容与关键字进行配对来过滤内容就可以了?######回复 @Hyacinthus_M : 这个我也不太清楚哪里有这些资料,你自己去找找吧。无论你是去修改wireless tools还是按照解析输出数据的方法,也只是上层应用。如果要从内核的层次去了解这些东西,预计你一周的时间根本不够。 不太清楚你的主管需要你用什么样的方法实现?他固执的要用自己的方法(他自身是否对这些可能用到的方法作过分析?),他自己为什么不去实现。######回复 @txgcwm : 我把自己的想法和他说了,他还是让我熟悉底层的API(意思还是让我去修改wireless-tools)。先把用这个实现方法做出来吧。用wireless-tools修改程序的方法,和解析输出的方法没有本质区别吧。 对了,基于无线网络开发的API在哪能找到资料?难道要去看kernel?

kun坤 2020-06-06 13:38:52 0 浏览量 回答数 0

问题

【精品问答】python技术1000问(1)

问问小秘 2019-12-01 21:57:48 454222 浏览量 回答数 19

回答

MQTT协议 MQTT(Message Queuing Telemetry Transport,消息队列遥测传输)最早是IBM开发的一个即时通讯协议,MQTT协议是为大量计算能力有限且工作在低带宽、不可靠网络的远程传感器和控制设备通讯而设计的一种协议。 MQTT协议的优势是可以支持所有平台,它几乎可以把所有的联网物品和互联网连接起来。 它具有以下主要的几项特性:1、使用发布/订阅消息模式,提供一对多的消息发布和应用程序之间的解耦;2、消息传输不需要知道负载内容;3、使用 TCP/IP 提供网络连接;4、有三种消息发布的服务质量:QoS 0:“最多一次”,消息发布完全依赖底层 TCP/IP 网络。分发的消息可能丢失或重复。例如,这个等级可用于环境传感器数据,单次的数据丢失没关系,因为不久后还会有第二次发送。QoS 1:“至少一次”,确保消息可以到达,但消息可能会重复。QoS 2:“只有一次”,确保消息只到达一次。例如,这个等级可用在一个计费系统中,这里如果消息重复或丢失会导致不正确的收费。5、小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;6、使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制;在MQTT协议中,一个MQTT数据包由:固定头(Fixed header)、 可变头(Variable header)、 消息体(payload)三部分构成。MQTT的传输格式非常精小,最小的数据包只有2个bit,且无应用消息头。下图是MQTT为可靠传递消息的三种消息发布服务质量 发布/订阅模型允许MQTT客户端以一对一、一对多和多对一方式进行通讯。 下图是MQTT的发布/订阅消息模式 CoAP协议 CoAP是受限制的应用协议(Constrained Application Protocol)的代名词。由于目前物联网中的很多设备都是资源受限型的,所以只有少量的内存空间和有限的计算能力,传统的HTTP协议在物联网应用中就会显得过于庞大而不适用。因此,IETF的CoRE工作组提出了一种基于REST架构、传输层为UDP、网络层为6LowPAN(面向低功耗无线局域网的IPv6)的CoAP协议。 CoAP采用与HTTP协议相同的请求响应工作模式。CoAP协议共有4中不同的消息类型。CON——需要被确认的请求,如果CON请求被发送,那么对方必须做出响应。NON——不需要被确认的请求,如果NON请求被发送,那么对方不必做出回应。ACK——应答消息,接受到CON消息的响应。RST——复位消息,当接收者接受到的消息包含一个错误,接受者解析消息或者不再关心发送者发送的内容,那么复位消息将会被发送。 CoAP消息格式使用简单的二进制格式,最小为4个字节。 一个消息=固定长度的头部header + 可选个数的option + 负载payload。Payload的长度根据数据报长度来计算。 主要是一对一的协议 举个例子: 比如某个设备需要从服务器端查询当前温度信息。 请求消息(CON): GET /temperature , 请求内容会被包在CON消息里面响应消息 (ACK): 2.05 Content “22.5 C” ,响应内容会被放在ACK消息里面 CoAP与MQTT的区别 MQTT和CoAP都是行之有效的物联网协议,但两者还是有很大区别的,比如MQTT协议是基于TCP,而CoAP协议是基于UDP。从应用方向来分析,主要区别有以下几点: 1、MQTT协议不支持带有类型或者其它帮助Clients理解的标签信息,也就是说所有MQTT Clients必须要知道消息格式。而CoAP协议则相反,因为CoAP内置发现支持和内容协商,这样便能允许设备相互窥测以找到数据交换的方式。 2、MQTT是长连接而CoAP是无连接。MQTT Clients与Broker之间保持TCP长连接,这种情形在NAT环境中也不会产生问题。如果在NAT环境下使用CoAP的话,那就需要采取一些NAT穿透性手段。 3、MQTT是多个客户端通过中央代理进行消息传递的多对多协议。它主要通过让客户端发布消息、代理决定消息路由和复制来解耦消费者和生产者。MQTT就是相当于消息传递的实时通讯总线。CoAP基本上就是一个在Server和Client之间传递状态信息的单对单协议。 HTTP协议http的全称是HyperText Transfer Protocol,超文本传输协议,这个协议的提出就是为了提供和接收HTML界面,通过这个协议在互联网上面传出web的界面信息。 HTTP协议的两个过程,Request和Response,两个都有各自的语言格式,我们看下是什么。请求报文格式:(注意这里有个换行) 响应报文格式:(注意这里有个换行) 方法method:       这个很重要,比如说GET和POST方法,这两个是很常用的,GET就是获取什么内容,而POST就是向服务器发送什么数据。当然还有其他的,比如HTTP 1.1中还有:DELETE、PUT、CONNECT、HEAD、OPTIONS、TRACE等一共8个方法(HTTP Method历史:HTTP 0.9 只有GET方法;HTTP 1.0 有GET、POST、HEAD三个方法)。请求URL:       这里填写的URL是不包含IP地址或者域名的,是主机本地文件对应的目录地址,所以我们一般看到的就是“/”。版本version:       格式是HTTP/.这样的格式,比如说HTTP/1.1.这个版本代表的就是我们使用的HTTP协议的版本,现在使用的一般是HTTP/1.1状态码status:       状态码是三个数字,代表的是请求过程中所发生的情况,比如说200代表的是成功,404代表的是找不到文件。原因短语reason-phrase:       是状态码的可读版本,状态码就是一个数字,如果你事先不知道这个数字什么意思,可以先查看一下原因短语。首部header:       注意这里的header我们不是叫做头,而是叫做首部。可能有零个首部也可能有多个首部,每个首部包含一个名字后面跟着一个冒号,然后是一个可选的空格,接着是一个值,然后换行。实体的主体部分entity-body:       实体的主体部分包含一个任意数据组成的数据块,并不是所有的报文都包含实体的主体部分,有时候只是一个空行加换行就结束了。 下面我们举个简单的例子: 请求报文:GET /index.html HTTP/1.1    Accept: text/*Host: www.myweb.com 响应报文:HTTP/1.1 200 OKContent-type: text/plainContent-length: 3  HTTP与CoAP的区别 CoAP是6LowPAN协议栈中的应用层协议,基于REST(表述性状态传递)架构风格,支持与REST进行交互。通常用户可以像使用HTTP协议一样用CoAP协议来访问物联网设备。而且CoAP消息格式使用简单的二进制格式,最小为4个字节。HTTP使用报文格式对于嵌入式设备来说需要传输数据太多,太重,不够灵活。 XMPP协议 XMPP(可扩展通讯和表示协议)是一种基于可扩展标记语言(XML)的协议, 它继承了在XML环境中灵活的发展性。可用于服务类实时通讯、表示和需求响应服务中的XML数据元流式传输。XMPP以Jabber协议为基础,而Jabber是即时通讯中常用的开放式协议。   基本网络结构 XMPP中定义了三个角色,客户端,服务器,网关。通信能够在这三者的任意两个之间双向发生。 服务器同时承担了客户端信息记录,连接管理和信息的路由功能。网关承担着与异构即时通信系统 的互联互通,异构系统可以包括SMS(短信),MSN,ICQ等。基本的网络形式是单客户端通过 TCP/IP连接到单服务器,然后在之上传输XML。 功能 传输的是与即时通讯相关的指令。在以前这些命令要么用2进制的形式发送(比如QQ),要么用纯文本指令加空格加参数加换行符的方式发送(比如MSN)。而XMPP传输的即时通讯指令的逻辑与以往相仿,只是协议的形式变成了XML格式的纯文本。举个例子看看所谓的XML(标准通用标记语言的子集)流是什么样子的?客户端:123456<?xmlversion='1.0'?>to='example_com'xmlns='jabber:client'xmlns:stream='http_etherx_jabber_org/streams'version='1.0'>服务器:1234567<?xmlversion='1.0'?>from='example_com'id='someid'xmlns='jabber:client'xmlns:stream='http_etherx_jabber_org/streams'version='1.0'>工作原理XMPP核心协议通信的基本模式就是先建立一个stream,然后协商一堆安全之类的东西, 中间通信过程就是客户端发送XML Stanza,一个接一个的。服务器根据客户端发送的信息 以及程序的逻辑,发送XML Stanza给客户端。但是这个过程并不是一问一答的,任何时候 都有可能从一方发信给另外一方。通信的最后阶段是关闭流,关闭TCP/IP连接。  网络通信过程中数据冗余率非常高,网络流量中70% 都消耗在 XMPP 协议层了。对于物联网来说,大量计算能力有限且工作在低带宽、不可靠网络的远程传感器和控制设备,省电、省流量是所有底层服务的一个关键技术指标,XMPP协议看起来已经落后了。 SoAP协议 SoAP(简单对象访问协议)是交换数据的一种协议规范,是一种轻量的、简单的、 基于可扩展标记语言(XML)的协议,它被设计成在WEB上交换结构化的和固化的信息。  SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP), 简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到 远程过程调用(RPC)等大量的应用程序。SOAP使用基于XML的数据结构和超文本传输协议 (HTTP)的组合定义了一个标准的方法来使用Internet上各种不同操作环境中的分布式对象。 总结: 从当前物联网应用发展趋势来分析,MQTT协议具有一定的优势。因为目前国内外主要的云计算服务商,比如阿里云、AWS、百度云、Azure以及腾讯云都一概支持MQTT协议。还有一个原因就是MQTT协议比CoAP成熟的要早,所以MQTT具有一定的先发优势。但随着物联网的智能化和多变化的发展,后续物联网应用平台肯定会兼容更多的物联网应用层协议。 作者:HFK_Frank 来源:CSDN 原文:https://blog.csdn.net/acongge2010/article/details/79142380 版权声明:本文为博主原创文章,转载请附上博文链接!

auto_answer 2019-12-02 01:55:21 0 浏览量 回答数 0

回答

tl; dr:您可能应该使用一维方法。 注意:在不填充书本的情况下比较动态1d或动态2d存储模式时,无法深入研究影响性能的细节,因为代码的性能取决于很多参数。如有可能,进行配置文件。 1.什么更快? 对于密集矩阵,一维方法可能更快,因为它提供了更好的内存局部性以及更少的分配和释放开销。 2.较小的是? 与2D方法相比,Dynamic-1D消耗的内存更少。后者还需要更多分配。 备注 我出于以下几个原因给出了一个很长的答案,但我想首先对您的假设做一些评论。 我可以想象,重新计算1D数组(y + x * n)的索引可能比使用2D数组(x,y)慢 让我们比较这两个函数: int get_2d (int **p, int r, int c) { return p[r][c]; } int get_1d (int *p, int r, int c) { return p[c + C*r]; } Visual Studio 2015 RC为这些功能(启用了优化功能)生成的(非内联)程序集是: ?get_1d@@YAHPAHII@Z PROC push ebp mov ebp, esp mov eax, DWORD PTR _c$[ebp] lea eax, DWORD PTR [eax+edx*4] mov eax, DWORD PTR [ecx+eax*4] pop ebp ret 0 ?get_2d@@YAHPAPAHII@Z PROC push ebp mov ebp, esp mov ecx, DWORD PTR [ecx+edx*4] mov eax, DWORD PTR _c$[ebp] mov eax, DWORD PTR [ecx+eax*4] pop ebp ret 0 区别是mov(2d)与lea(1d)。前者的延迟为3个周期,最大吞吐量为每个周期2个,而后者的延迟为2个周期,最大吞吐量为每个周期3个。(根据指令表-Agner Fog, 由于差异很小,我认为索引重新计算不会产生很大的性能差异。我希望几乎不可能将这种差异本身确定为任何程序的瓶颈。 这将我们带到下一个(也是更有趣的)点: ...但是我可以想象一维可能在CPU缓存中... 是的,但是2d也可能在CPU缓存中。有关为什么1d仍然更好的说明,请参见缺点:内存局部性。 长答案,或者为什么对于简单 /小的矩阵,动态二维数据存储(指针到指针或向量矢量)是“不好的” 。 注意:这是关于动态数组/分配方案[malloc / new / vector等]。静态2D数组是一个连续的内存块,因此不受我将在此处介绍的不利影响。 问题 为了能够理解为什么动态数组的动态数组或向量的矢量最有可能不是选择的数据存储模式,您需要了解此类结构的内存布局。 使用指针语法的示例案例 int main (void) { // allocate memory for 4x4 integers; quick & dirty int ** p = new int*[4]; for (size_t i=0; i<4; ++i) p[i] = new int[4]; // do some stuff here, using p[x][y] // deallocate memory for (size_t i=0; i<4; ++i) delete[] p[i]; delete[] p; } 缺点 内存位置 对于此“矩阵”,您分配一个包含四个指针的块和四个包含四个整数的块。所有分配都不相关,因此可以导致任意存储位置。 下图将使您了解内存的外观。 对于真正的二维情况: 紫色正方形是其p自身占据的存储位置。 绿色方块将存储区域p点组装为(4 x int*)。 4个连续的蓝色方块的4个区域是每个int*绿色区域所指向的区域 对于在1d情况下映射的2d: 绿色方块是唯一需要的指针 int * 蓝色方块组合了所有矩阵元素的存储区域(16 x int)。 实际2D与映射2D内存布局 这意味着(例如,使用左侧布局时)(例如,使用缓存),与连续存储模式(如右侧所示)相比,您可能会发现性能较差。 假设高速缓存行是“一次传输到高速缓存中的数据量”,并想象一个程序一个接一个地访问整个矩阵。 如果您具有正确对齐的32位值的4 4矩阵,则具有64字节高速缓存行(典型值)的处理器能够“一次性”读取数据(4 * 4 * 4 = 64字节)。如果您开始处理而缓存中还没有数据,则将面临缓存未命中,并且将从主内存中获取数据。由于且仅当连续存储(并正确对齐)时,此负载才能装入整个缓存行,因此可以立即读取整个矩阵。处理该数据时可能不会再有任何遗漏。 在动态的“真实二维”系统中,每行/列的位置都不相关,处理器需要分别加载每个内存位置。即使只需要64个字节,在最坏的情况下,为4个不相关的内存位置加载4条高速缓存行实际上会传输256个字节并浪费75%的吞吐量带宽。如果使用2d方案处理数据,您将再次在第一个元素上遇到缓存未命中(如果尚未缓存)。但是现在,从主内存中第一次加载后,只有第一行/列会在缓存中,因为所有其他行都位于内存中的其他位置,并且不与第一行/列相邻。一旦到达新的行/列,就会再次出现高速缓存未命中,并从主内存执行下一次加载。 长话短说:2d模式具有较高的缓存未命中率,而1d方案由于数据的局部性而具有更好的性能潜力。 频繁分配/取消分配 N + 1创建所需的NxM(4×4)矩阵需要多达(4 + 1 = 5)个分配(使用new,malloc,allocator :: allocate或其他方法)。 也必须应用相同数量的适当的各自的重新分配操作。 因此,与单个分配方案相比,创建/复制此类矩阵的成本更高。 随着行数的增加,情况变得更加糟糕。 内存消耗开销 我假设int的大小为32位,指针的大小为32位。(注意:系统依赖性。) 让我们记住:我们要存储一个4×4 int矩阵,表示64个字节。 对于NxM矩阵,使用提出的指针对指针方案存储,我们消耗了 NMsizeof(int) [实际的蓝色数据] + Nsizeof(int) [绿色指针] + sizeof(int**) [紫罗兰色变量p]字节。 444 + 44 + 4 = 84在本示例的情况下,这会使字节变多,使用时甚至会变得更糟std::vector<std::vector >。对于4 x 4 int ,它将需要N * M * sizeof(int)+ N * sizeof(vector )+ sizeof(vector<vector >)字节,即4 44 + 416 + 16 = 144总共字节,共64个字节。 另外-根据所使用的分配器-每个单独的分配可能(并且很可能会)还有16个字节的内存开销。(一些“信息字节”用于存储已分配的字节数,以进行适当的重新分配。) 这意味着最坏的情况是: N*(16+Msizeof(int)) + 16+Nsizeof(int*) + sizeof(int**) = 4*(16+44) + 16+44 + 4 = 164 bytes ! Overhead: 156% 开销的份额将随着矩阵大小的增加而减少,但仍然存在。 内存泄漏的风险 一堆分配需要适当的异常处理,以避免在其中一个分配失败的情况下发生内存泄漏!您需要跟踪分配的内存块,并且在释放内存时一定不要忘记它们。 如果new无法运行内存并且无法分配下一行(特别是在矩阵很大时),std::bad_alloc则抛出a new。 例: 在上面提到的new / delete示例中,如果要避免发生bad_alloc异常时的泄漏,我们将面临更多代码。 // allocate memory for 4x4 integers; quick & dirty size_t const N = 4; // we don't need try for this allocation // if it fails there is no leak int ** p = new int*[N]; size_t allocs(0U); try { // try block doing further allocations for (size_t i=0; i<N; ++i) { p[i] = new int[4]; // allocate ++allocs; // advance counter if no exception occured } } catch (std::bad_alloc & be) { // if an exception occurs we need to free out memory for (size_t i=0; i<allocs; ++i) delete[] p[i]; // free all alloced p[i]s delete[] p; // free p throw; // rethrow bad_alloc } /* do some stuff here, using p[x][y] */ // deallocate memory accoding to the number of allocations for (size_t i=0; i<allocs; ++i) delete[] p[i]; delete[] p; 摘要 在某些情况下,“真实的2d”内存布局适合并且有意义(即,如果每行的列数不是恒定的),但是在最简单和常见的2D数据存储情况下,它们只会使代码的复杂性膨胀,并降低性能和程序的内存效率。 另类 您应该使用连续的内存块,并将行映射到该内存块。 做到这一点的“ C ++方式”可能是编写一个类来管理您的内存,同时考虑诸如 什么是三法则? 资源获取是什么意思初始化(RAII)? C ++概念:容器(在cppreference.com上) 例 为了提供这样一个类的外观的想法,下面是一个具有一些基本功能的简单示例: 二维尺寸可构造 2d可调整大小 operator(size_t, size_t) 用于2行主要元素访问 at(size_t, size_t) 用于检查的第二行主要元素访问 满足容器的概念要求 资源: #include #include #include #include namespace matrices { template class simple { public: // misc types using data_type = std::vector ; using value_type = typename std::vector ::value_type; using size_type = typename std::vector ::size_type; // ref using reference = typename std::vector ::reference; using const_reference = typename std::vector ::const_reference; // iter using iterator = typename std::vector ::iterator; using const_iterator = typename std::vector ::const_iterator; // reverse iter using reverse_iterator = typename std::vector ::reverse_iterator; using const_reverse_iterator = typename std::vector ::const_reverse_iterator; // empty construction simple() = default; // default-insert rows*cols values simple(size_type rows, size_type cols) : m_rows(rows), m_cols(cols), m_data(rows*cols) {} // copy initialized matrix rows*cols simple(size_type rows, size_type cols, const_reference val) : m_rows(rows), m_cols(cols), m_data(rows*cols, val) {} // 1d-iterators iterator begin() { return m_data.begin(); } iterator end() { return m_data.end(); } const_iterator begin() const { return m_data.begin(); } const_iterator end() const { return m_data.end(); } const_iterator cbegin() const { return m_data.cbegin(); } const_iterator cend() const { return m_data.cend(); } reverse_iterator rbegin() { return m_data.rbegin(); } reverse_iterator rend() { return m_data.rend(); } const_reverse_iterator rbegin() const { return m_data.rbegin(); } const_reverse_iterator rend() const { return m_data.rend(); } const_reverse_iterator crbegin() const { return m_data.crbegin(); } const_reverse_iterator crend() const { return m_data.crend(); } // element access (row major indexation) reference operator() (size_type const row, size_type const column) { return m_data[m_cols*row + column]; } const_reference operator() (size_type const row, size_type const column) const { return m_data[m_cols*row + column]; } reference at() (size_type const row, size_type const column) { return m_data.at(m_cols*row + column); } const_reference at() (size_type const row, size_type const column) const { return m_data.at(m_cols*row + column); } // resizing void resize(size_type new_rows, size_type new_cols) { // new matrix new_rows times new_cols simple tmp(new_rows, new_cols); // select smaller row and col size auto mc = std::min(m_cols, new_cols); auto mr = std::min(m_rows, new_rows); for (size_type i(0U); i < mr; ++i) { // iterators to begin of rows auto row = begin() + i*m_cols; auto tmp_row = tmp.begin() + i*new_cols; // move mc elements to tmp std::move(row, row + mc, tmp_row); } // move assignment to this *this = std::move(tmp); } // size and capacity size_type size() const { return m_data.size(); } size_type max_size() const { return m_data.max_size(); } bool empty() const { return m_data.empty(); } // dimensionality size_type rows() const { return m_rows; } size_type cols() const { return m_cols; } // data swapping void swap(simple &rhs) { using std::swap; m_data.swap(rhs.m_data); swap(m_rows, rhs.m_rows); swap(m_cols, rhs.m_cols); } private: // content size_type m_rows{ 0u }; size_type m_cols{ 0u }; data_type m_data{}; }; template void swap(simple & lhs, simple & rhs) { lhs.swap(rhs); } template bool operator== (simple const &a, simple const &b) { if (a.rows() != b.rows() || a.cols() != b.cols()) { return false; } return std::equal(a.begin(), a.end(), b.begin(), b.end()); } template bool operator!= (simple const &a, simple const &b) { return !(a == b); } } 请注意以下几点: T需要满足使用的std::vector成员函数的要求 operator() 不执行任何“范围”检查 无需自己管理数据 不需要析构函数,复制构造函数或赋值运算符 因此,您不必费心为每个应用程序进行适当的内存处理,而只需为编写的类一次即可。 限制条件 在某些情况下,动态“真实”二维结构是有利的。例如,如果 矩阵非常大且稀疏(如果甚至不需要分配任何行,但可以使用nullptr对其进行处理),或者 这些行没有相同数量的列(也就是说,如果您根本没有矩阵,而只有另一个二维结构)。

保持可爱mmm 2020-02-09 13:47:55 0 浏览量 回答数 0

回答

作为一个javaer,我以前写过很多关于Linux的文章。但经过多年的观察,发现其实对于大部分人,有些东西压根就用不着。用的最多的,就是到线上排查个问题而已,这让人很是苦恼。那么,我们就将范围再缩小一下。 Linux生产环境上,最常用的一套“Sed“技巧 Linux生产环境上,最常用的一套“AWK“技巧 Linux生产环境上,最常用的一套“vim“技巧 Linux命令好像还真不少,根本原因就是软件多,也有像ag这样的命令想替代grep,但大多数命令古老而坚挺。不是因为这些软件设计的有多好,原因是一些软件最开始入驻了系统,时间久了,就变成了一种约定,这种习惯改变代价太大,就像把所有键盘的L和F换一下一样。 这片文章假定你已经了解大多数Linux命令,并了解操作系统的基本元素。如果你现在了解的命令还不足10个,下面的内容就不用看了。除了最基本的东西,本文列出一些对你的面试最常见的最能加分的地方,有些组合可能是你没见过的技巧。但本文仅仅是给出一个大致的轮廓和印象,为以后的专题性考察点作一个序。 本文中出现的所有命令,应该熟记并熟练使用。 几种比较典型的Linux系统 首先对目前的Linux版本有个大体的印象,大体分Desktop版和Server版,已经是百花齐放。 Ubuntu 最常见的Linux个人发行版,一位有情怀的南非富豪,有了钱你也可以这么做 CentOS 最常用Linux服务器发新版,RHEL的开放版本,因版权而生的轮子 Arch 滚动升级,海量二进制包,社区活跃,个人最爱 Gentoo 安装软件需要从源码开始编译,稳定,但用起来会很痛 LFS 从零构建Linux,跟着做一遍,Linux每根毛都看的清清楚楚 Kali 专做渗透用的,代表了发行版的一个发展路径,就是领域 首先要了解的概念 KISS Keep it Simple and Stupid,据说是哲学 一切皆文件 通常是文件的东西叫文件,进程、磁盘等也被抽象成了文件,比较离谱的管道、设备、socket等,也是文件。 这是Linux最重要的组织方式。 管道 | 分隔,前面命令的输出作为后面命令的输入,可以串联多个 重定向 < 将文件做为命令的输入 将命令的输出输出到文件 将命令的输出追加到文件 SHELL 首先确认你的shell,一般最常用的是bash,也有不少用csh,zsh等的,通过echo $SHELL可以看到当前用户的shell,对应的配置文件也要相应改变。 比如.zshrc,.bashrc 四大元素 进入linux,我们首先关注的是四个元素: 内存,cpu,存储,网络。 Linux提供了足够的命令,让你窥探它的每个角落。 接下来的命令都是些最常用的,不管精通不精通,想不起来要打屁股。 CPU 使用top查看cpu的load,使用shift+p按照cpu排序。 需要了解wa,us等都是什么意思 使用uptime查看系统启动时间和load,load是什么意思呢? 什么算是系统过载? 这是个高频问题,别怪我没告诉你 ps命令勃大茎深,除了查进程号外,你还需要知道R、S、D、T、Z、<、N状态位的含义 top和ps很多功能是相通的,比如watch "ps -mo %cpu,%mem,pid,ppid,command ax" 相当于top的进程列表; top -n 1 -bc 和ps -ef的结果相似。 有生就有死,可以用kill杀死进程。 对java来说,需要关注kill -9、kill -15、kill -3的含义,kill的信号太多了,可以用kill -l查看,搞懂大多数信号大有裨益。 如果暂时不想死,可以通过&符号在后台执行,比如tail -f a.log &。 jobs命令可以查看当前后台的列表,想恢复的话,使用fg回到幕前。 这都是终端作业,当你把term关了你的后台命令也会跟着消失,所以想让你的程序继续执行的话, 需要nohup命令,此命令需要牢记 mpstat 显示了系统中 CPU 的各种统计信 了解cpu亲和性 内存 free -m 命令,了解free、used、cached、swap各项的含义 cat /proc/meminfo 查看更详细的内存信息 细心的同学可能注意到,CPU和内存的信息,通过top等不同的命令显示的数值是一样的。 slabtop 用来显示内核缓存占用情况,比如遍历大量文件造成缓存目录项。 曾在生产环境中遇到因执行find /造成dentry_cache耗尽服务器内存。 vmstat 命令是我最喜欢也最常用的命令之一,可以以最快的速度了解系统的运行状况。 每个参数的意义都要搞懂。 swapon、swapoff 开启,关闭交换空间 sar 又一统计类轮子,一般用作采样工具 存储 使用df -h查看系统磁盘使用概况 lsblk 列出块设备信息 du 查看目录或者文件大小 网络 rsync 强大的同步工具,可以增量哦 netstat 查看Linux中网络系统状态信息,各种 ss 它能够显示更多更详细的有关TCP和连接状态的信息,而且比netstat更快速更高效。 curl、wget 模拟请求工具、下载工具。 如wget -r http://site 将下载整个站点 ab Apache服务器的性能测试工具 ifstat 统计网络接口流量状态 nslookup 查询域名DNS信息的工具,在内网根据ip查询域名是爽爆了 nc 网络工具中的瑞士军刀,不会用真是太可惜了 arp 可以显示和修改IP到MAC转换表 traceroute 显示数据包到主机间的路径,俗称几跳,跳的越少越快 tcpdump 不多说了,去下载wireshark了 wall 向当前所有打开的终端上输出信息。 使用who命令发现女神正在终端上,可以求爱 网络方面推荐安装体验一下kaliLinux,上面的工具会让你high到极点。 如何组织起来 linux的命令很有意思,除了各种stat来监控状态,也有各种trace来进行深入的跟踪,也有各种top来统计资源消耗者,也有各种ls来查看系统硬件如lsblk、lsusb、lscpi。基本上跟着你的感觉走,就能找到相应的工具,因为约定是系统中最强大的导向。 Linux有个比较另类的目录/proc,承载了每个命令的蹂躏。像sysctl命令,就是修改的/proc/sys目录下的映射项。不信看看find /proc/sys -type f | wc -l和sysctl -a| wc -l的结果是不是很像? /proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。只不过以文件系统的方式为访问系统内核数据的操作提供接口。系统的所有状态都逃不过它的火眼金睛。例如: cat /proc/vmstat 看一下,是不是和vmstat命令的输出很像? cat /proc/meminfo 是不是最全的内存信息 cat /proc/slabinfo 这不就是slabtop的信息么 cat /proc/devices 已经加载对设备们 cat /proc/loadavg load avg原来就躺在这里啊 cat /proc/stat 所有的CPU活动信息 ls /proc/$pid/fd 静静地躺着lsof的结果 一般排查问题的方法 一般排查问题也是围绕着内存cpu等几个元素去排查。下图是一张大体的排查故障或者性能问题的过程,看图,不多说。 应用场景举例 下面举例从具体应用场景来说明各种命令的组合应用,此类场景数不胜数,需要个人积累。但强烈建议将sed和awk练的熟练一些。 怎么查看某个Java进程里面占用CPU最高的一个线程具体信息? 获取进程中占用CPU最高的线程,计为n。 使用top top -H -p pid,肉眼观察之 使用ps ps -mo spid,lwp,stime,time,%cpu -p pid 将线程号转化成十六进制printf 0x%x n 使用jstack找到相应进程,打印线程后的100行信息 jstack -l pid| grep spid -A 100 统计每种网络状态的数量 netstat -ant | awk '{print $6}' | sort | uniq -c | sort -n -k 1 -r![5.jpg](https://ucc.alicdn.com/pic/developer-ecology/655b656daf0344d58dbfd798fe1460b8.jpg) 首先使用netstat查看列表,使用’awk’截取第六列,使用uniq进行统计,并对统计结果排序。当然,也可以这样。 netstat -ant | awk '{arr[$6]++}END{for(i in arr){print arr[i]" "i }}' | sort -n -k 1 -r 这和“分析apache日志,给出当日访问ip的降序列表”是一样的问题。 怎么查看哪个进程在用swap 首先要了解/proc/$pid/smaps里有我们所需要的各种信息,其中Swap字段即是我们所需要的。只要循环遍历一下即可。 for i in `cd /proc;ls |grep "^[0-9]"|awk ' $0 >100'` ;do awk '/Swap:/{a=a+$2}END{print '"$i"',a/1024"M"}' /proc/$i/smaps ;done |sort -k2nr End 软件领域有两种人才,一种是工程型的,一种是研究型的。在Linux领域里,相对于搞内核研究的来说,搞命令行的就属于工程型。工程型也有他自己的苦衷,比如,背诵命令就挺痛苦的,一般来说不太推荐背诵,第一覆盖的面不广,第二记的快忘的也快,浪费脑细胞。牛逼的记法就是用,用时间来冲淡烟云,见微知著,并体验其中的喜悦。爱她并天天抱她上床,真爱才成。 原创:小姐姐味道。

剑曼红尘 2020-04-01 11:01:13 0 浏览量 回答数 0

问题

Logtail 机器无心跳是什么意思?怎么解决?

轩墨 2019-12-01 22:04:03 1597 浏览量 回答数 0

回答

12月17日更新 请问下同时消费多个topic的情况下,在richmap里面可以获取到当前消息所属的topic吗? 各位大佬,你们实时都是怎样重跑数据的? 有木有大神知道Flink能否消费多个kafka集群的数据? 这个问题有人遇到吗? 你们实时读取广业务库到kafka是通过什么读的?kafka connector 的原理是定时去轮询,这样如果表多了,会不会影响业务库的性能?甚至把业务库搞挂? 有没有flink 1.9 连接 hive的例子啊?官网文档试了,没成功 请问各位是怎么解决实时流数据倾斜的? 请问一下,对于有状态的任务,如果任务做代码升级的时候,可否修改BoundedOutOfOrdernessTimestampExtractor的maxOutOfOrderness呢?是否会有影响数据逻辑的地方呢? 老哥们有做过统计从0点开始截止到现在时刻的累计用户数吗? 比如五分钟输出一次,就是7点输出0点到7点的累计用户,7:05输出0点到7:05的累计用户。 但是我这里有多个维度,现在用redis来做的。 想知道有没有更好的姿势? 实时数仓用什么存储介质来存储维表,维表有大有小,大的大概5千万左右。 各位大神有什么建议和经验分享吗? 请教个问题,就是flink的窗口触发必须是有数据才会触发吗?我现在有个这样的需求,就是存在窗口内没有流数据进入,但是窗口结束是要触发去外部系统获取上一个窗口的结果值作为本次窗口的结果值!现在没有流数据进入窗口结束时如何触发? kafkaSource.setStartFromTimestamp(timestamp); 发现kafkasource从指定时间开始消费,有些topic有效,有效topic无效,大佬们有遇到过吗? 各位大佬,flink两个table join的时候,为什么打印不出来数据,已经赋了关联条件了,但是也不报错 各位大佬 请教一下 一个faile的任务 会在这里面存储展示多久啊? 各位大佬,我的程序每五分钟一个窗口做了基础指标的统计,同时还想统计全天的Uv,这个是用State就能实现吗? 大佬们,flink的redis sink是不是只适用redis2.8.5版本? 有CEP 源码中文注释的发出来学习一下吗? 有没有拿flink和tensorflow集成的? 那位大神,给一个java版的flink1.7 读取kafka数据,做实时监控和统计的功能的代码案例。 请问下风控大佬,flink为风控引擎做数据支撑的时候,怎么应对风控规则的不断变化,比如说登录场景需要实时计算近十分钟内登录次数超过20次用户,这个规则可能会变成计算近五分钟内登录次数超过20次的。 想了解一下大家线上Flink作业一般开始的时候都分配多少内存?广播没办法改CEP flink支持多流(大于2流)join吗? 谁能帮忙提供一下flink的多并行度的情况下,怎么保证数据有序 例如map并行度为2 那就可能出现数据乱序的情况啊 请教下现在从哪里可以可以看单任务的运行状况和内存占用情况,flink页面上能看单个任务的内存、cpu 大佬们 flink1.9 停止任务手动保存savepoint的命令是啥? flink 一个流计算多个任务和 还是一个流一个任务好? flink 1.9 on yarn, 自定义个connector里面用了jni, failover以后 就起不来了, 报错重复load so的问题。 我想问一下 这个,怎么解决。 难道flink 里面不能用jni吗。 ide里面调试没有问题,部署到集群就会报错了,可能什么问题? 请教一下对于长时间耗内存很大的任务,大家都是开checkpoint机制,采用rocksdb做状态后端吗? 请问下大佬,flink jdbc读取mysql,tinyin字段类型自动转化为Boolean有没有好的解决方法 Flink 1.9版本的Blink查询优化器,Hive集成,Python API这几个功能好像都是预览版,请问群里有大佬生产环境中使用这些功能了吗? 想做一个监控或数据分析的功能,如果我flink 的datastreaming实现消费Kafka的数据,但是我监控的规则数据会增加或修改,但是不想停这个正在运行的flink程序,要如何传递这个动态变化的规则数据,大神给个思路,是用ConnectedStream这个吗?还是用Broadcast ?还有一个,比如我的规则数据是存放在Mysql表中,用什么事件隔30秒去触发读取mysql规则表呢?谢谢! 想做一个监控或数据分析的功能,如果我flink 的datastreaming实现消费Kafka的数据,但是我监控的规则数据会增加或修改,但是不想停这个正在运行的flink程序,要如何传递这个动态变化的规则数据,大神给个思路,是用ConnectedStream这个吗?还是用Broadcast ?还有一个,比如我的规则数据是存放在Mysql表中,用什么事件隔30秒去触发读取mysql规则表呢?谢谢! 各位大佬,在一个 Job 计算过程中,查询 MySQL 来补全额外数据,是一个好的实践嘛?还是说流处理过程中应该尽量避免查询额外的数据? Flink web UI是jquery写的吗? 12月9日更新 成功做完一次checkpoint后,会覆盖上一次的checkpoint吗? 数据量较大时,flink实时写入hbase能够异步写入吗? flink的异步io,是不是只是适合异步读取,并不适合异步写入呀? 请问一下,flink将结果sink到redis里面会不会对存储的IO造成很大的压力,如何批量的输出结果呢? 大佬们,flink 1.9.0版本里DataStream api,若从kafka里加载完数据以后,从这一个流中获取数据进行两条业务线的操作,是可以的吗? flink 中的rocksdb状态怎么样能可视化的查看有大佬知道吗? 感觉flink 并不怎么适合做hive 中的计算引擎来提升hive 表的查询速度 大佬们,task端rocksdb状态 保存路径默认是在哪里的啊?我想挂载个新磁盘 把状态存到那里去 flink 的state 在窗口滑动到下一个窗口时候 上一个窗口销毁时候 state会自己清除吗? 求助各位大佬,一个sql里面包含有几个大的hop滑动窗口,如15个小时和24个小时,滑动步长为5分钟,这样就会产生很多overlap 数据,导致状态会很快就达到几百g,然后作业内存也很快达到瓶颈就oom了,然后作业就不断重启,很不稳定,请问这个业务场景有什么有效的解决方案么? 使用jdbcsink的时候,如果连接长时间不使用 就会被关掉,有人遇到过吗?使用的是ddl的方式 如何向云邪大佬咨询FLink相关技术问题? 请问各位公司有专门开发自己的实时计算平台的吗? 请问各位公司有专门开发自己的实时计算平台的吗? 有哪位大佬有cdh集成安装flink的文档或者手册? 有哪位大佬有cdh集成安装flink的文档或者手册? 想问下老哥们都是怎么统计一段时间的UV的? 是直接用window然后count嘛? Flink是不是也是这样的? 请问现在如有个实时程序,根据一个mysql的维表来清洗,但是我这个mysql表里面就只有几条信息且可能会变。 我想同一个定时器去读mysql,然后存在对象中,流清洗的时候读取这个数据,这个想法可行吗?我目前在主类里面定义一个对象,然后往里面更新,发现下面的map方法之类的读不到我更新进去的值 有大佬做过flink—sql的血缘分析吗? 12月3日更新 请教一下,为什么我flume已经登录成功了keytab认证的kafka集群,但是就是消费不到数据呢? flink 写入mysql 很长一段时间没有写入,报错怎么解决呢? flink timestamp转换为date类型,有什么函数吗 Run a single Flink job on YARN 我采用这种模式提交任务,出现无法找到 开启 HA 的ResourceManager Failed to connect to server: xxxxx:8032: retries get failed due to exceeded maximum allowed retries number: 0 有大佬遇到过吗 ? 各位大佬,请问有Flink写S3的方案吗? flink 连接hbase 只支持1.4.3版本? onnector: type: hbase version: "1.4.3" 请问 flink1.9能跑在hadoop3集群上吗? 滑动窗口 排序 报错这个是什么原因呢? 这个pravega和kafka有啥区别? flink 开发里数据源配置了RDS,但是在RDS里没有看到创建的表,是为什么呢? Tumbling Window里的数据,是等窗口期内的数据到齐之后一次性处理,还是到了一条就处理一条啊 双流join后再做time window grouping. 但是双流join会丢失时间属性,请问大家如何解决 stream processing with apache flink,这本书的中译版 现在可以买吗? flink on yarn时,jm和tm占用的内存最小是600M,这个可以修改吗? 各位大佬,使用默认的窗口Trigger,在什么情况下会触发两次啊?窗口关闭后,然后还来了这个窗口期内的数据,并且开了allowedLateness么? flink web里可以像storm那样 看每条数据在该算子中的平均耗时吗? 各位大佬,flink任务的并发数调大到160+以后,每隔几十分钟就会出现一次TM节点连接丢失的异常,导致任务重启。并发在100时运行比较稳定,哪位大佬可以提供下排查的思路? 感觉stateful function 是下一个要发力的点,这个现在有应用案例吗? 我有2个子网(a子网,b子网)用vpn联通,vpn几周可能会断一次。a子网有一个kafka集群,b子网运行我自己的flink集群和应用,b子网的flink应用连接到a子网的kafka集群接收消息来处理入库到数仓去。我的问题是,如果vpn断开,flink consumer会异常整个作业退出吗?如果作业退出,我重连vpn后,能从auto checkpoint再把flink应用恢复到出错时flink kafka consumer应该读取的partition/offset位置吗?flink的checkpoint除了保存自己开发的算子里的state,kafkaconsumer里的partition/offset也会保存和恢复吗? flink的反压为什么不加入metrics呢 hdfs是不是和flink共用一个集群? flink消费kafka,可以从指定时间消费的吗?目前提供的接口只是根据offset消费?有人知道怎么处理? flink 的Keyby是不是只是repartition而已?没有将key相同的数据放到一个组合里面 电商大屏 大家推荐用什么来做吗? 我比较倾向用数据库,因为有些数据需要join其他表,flink充当了什么角色,对这个有点迷,比如统计当天订单量,卖了多少钱,各个省的销量,销售金额,各个品类的销售量销售金额 开源1.9的sql中怎么把watermark给用起来,有大神知道吗? 有没有人能有一些flink的教程 代码之类的分享啊 采用了checkpoint,程序停止了之后,什么都不改,直接重启,还是能接着继续运行吗?如果可以的话,savepoint的意义又是什么呢? 有人做过flink 的tpc-ds测试吗,能不能分享一下操作的流程方法 checkpoint是有时间间隔的,也就可以理解为checkpoint是以批量操作的,那如果还没进行ckecnpoint就挂了,下次从最新的一次checkpoint重启,不是重复消费了? kafka是可以批量读取数据,但是flink是一条一条处理的,应该也可以一条一条提交吧。 各位大佬,flink sql目前是不是不支持tumbling window join,有人了解吗? 你们的HDFS是装在taskmanager上还是完全分开的,请问大佬们有遇到这种情况吗? 大佬们flink检查点存hdfs的话怎么自动清理文件啊 一个128M很快磁盘就满了 有谁遇到过这个问题? 请教一下各位,这段代码里面,我想加一个trigger,实现每次有数据进window时候,就输出,而不是等到window结束再输出,应该怎么加? 麻烦问下 flink on yarn 执行 客户端启动时 报上面错,是什么原因造成的 求大佬指点 ERROR org.apache.flink.client.program.rest.RestClusterClient - Error while shutting down cluster java.util.concurrent.ExecutionException: org.apache.flink.runtime.concurrent.FutureUtils$RetryException: Could not complete the operation. Number of retries has been exhausted. 大家怎么能动态的改变 flink WindowFunction 窗口数据时间 flink on yarn之后。yarn的日志目录被写满,大家如配置的? Flink1.9 启动 yarn-session报这个错误 怎么破? yarn 模式下,checkpoint 是存在 JobManager的,提交任务也是提交给 JobManager 的吧? heckpoint机制,会不会把window里面的数据全部放checkpoint里面? Flink On Yarn的模式下,如果通过REST API 停止Job,并触发savepiont呢 jenkins自动化部署flink的job,一般用什么方案?shell脚本还是api的方式? 各位大佬,开启增量checkpoint 情况下,这个state size 是总的checkpoint 大小,还是增量上传的大小? 想用状态表作为子表 外面嵌套窗口 如何实现呢 因为状态表group by之后 ctime会失去时间属性,有哪位大佬知道的? 你们有试过在同样的3台机器上部署两套kafka吗? 大家有没有比较好的sql解析 组件(支持嵌套sql)? richmapfuntion的open/close方法,和处理数据的map方法,是在同一个线程,还是不同线程调用的? flink on yarn 提交 参数 -p 20 -yn 5 -ys 3 ,我不是只启动了5个container么? Flink的乱序问题怎么解决? 我对数据流先进行了keyBy,print的时候是有数据的,一旦进行了timeWindow滑动窗口就没有数据了,请问是什么情况呢? 搭建flinksql平台的时候,怎么处理udf的呀? 怎么查看sentry元数据里哪些角色有哪些权限? 用java api写的kafka consumer能消费到的消息,但是Flink消费不到,这是为啥? 我state大小如果为2G左右 每次checkpoint会不会有压力? link-table中的udaf能用deltaTrigger么? flink1.7.2,场景是一分钟为窗口计算每分钟传感器的最高温度,同时计算当前分钟与上一分钟最高温 001 Flink集群支持kerberos认证吗?也就是说flink客户端需要向Flink集群进行kerberos认证,认证通过之后客户端才能提交作业到Flink集群运行002 Flink支持多租户吗? 如果要对客户端提交作业到flink进行访问控制,你们有类似的这种使用场景吗? flink可以同时读取多个topic的数据吗? Flink能够做实时ETL(oracle端到oracle端或者多端)么? Flink是否适合普通的关系型数据库呢? Flink是否适合普通的关系型数据库呢? 流窗口关联mysql中的维度表大佬们都是怎么做的啊? 怎么保证整个链路的exactly one episode精准一次,从source 到flink到sink? 在SQL的TUMBLE窗口的统计中,如果没数据进来的,如何让他也定期执行,比如进行count计算,让他输出0? new FlinkKafkaConsumer010[String]("PREWARNING",new JSONKeyValueDeserializationSchema(true), kafkaProps).setStartFromGroupOffsets() ) 我这样new 它说要我传个KeyedDeserializationSchema接口进去 flink里面broadcast state想定时reload怎么做?我用kafka里的stream flink独立模式高可用搭建必需要hadoop吗? 有人用增量cleanupIncrementally的方式来清理状态的嘛,感觉性能很差。 flink sink to hbase继承 RichOutputFormat运行就报错 kafka 只有低级 api 才拿得到 offset 吗? 有个问题咨询下大家,我的flinksql中有一些参数是要从mysql中获取的,比如我flink的sql是select * from aa where cc=?,这个问号的参数需要从mysql中获取,我用普通的jdbc进行连接可以获的,但是有一个问题,就是我mysql的数据改了之后必须重启flink程序才能解决这个问题,但这肯定不符合要求,请问大家有什么好的办法吗? flink里怎样实现多表关联制作宽表 flink写es,因为半夜es集群做路由,导致写入容易失败,会引起source的反压,然后导致checkpoint超时任务卡死,请问有没有办法在下游es处理慢的时候暂停上游的导入来缓解反压? flink 写parquet 文件,使用StreamingFileSink streamingFileSink = StreamingFileSink.forBulkFormat( new Path(path), ParquetAvroWriters.forReflectRecord(BuyerviewcarListLog.class)). withBucketAssigner(bucketAssigner).build(); 报错 java.lang.UnsupportedOperationException: Recoverable writers on Hadoop are only supported for HDFS and for Hadoop version 2.7 or newer 1.7.2 NoWindowInnerJoin这个实现,我看实现了CleanupState可更新过期时间删除当前key状态的接口,是不是这个1.7.2版本即使有个流的key一直没有被匹配到他的状态也会被清理掉,就不会存在内存泄漏的问题了? flink1.7.2 想在Table的UDAF中使用State,但是发现UDAF的open函数的FunctionContext中对于RuntimeContext是一个private,无法使用,大佬,如何在Table的UDAF中使用State啊? Flink有什么性能测试工具吗? 项目里用到了了KafkaTableSourceSinkFactory和JDBCTableSourceSinkFactory。maven打包后,META-INF里只会保留第一个 标签的org.apache.flink.table.factories.TableFactory内容。然后执行时就会有找不到合适factory的报错,请问有什么解决办法吗? 为什么这个这段逻辑 debug的时候 是直接跳过的 各位大佬,以天为单位的窗口有没有遇到过在八点钟的时候会生成一条昨天的记录? 想问一下,我要做一个规则引擎,需要动态改变规则,如何在flink里面执行? flink-1.9.1/bin/yarn-session.sh: line 32: construc 我要用sql做一个规则引擎,需要动态改变规则,如何在flink里面执行? 我要用sql做一个规则引擎,需要动态改变规则,如何在flink里面执行? 一般公司的flink job有没有进程进行守护?有专门的工具或者是自己写脚本?这种情况针对flink kafka能不能通过java获取topic的消息所占空间大小? Flink container was removed这个咋解决的。我有时候没有数据的时候也出现这 大家有没有这种场景,数据从binlog消费,这个信息是订单信息,同一个订单id,会有不同状态的变更 问大家个Hive问题,新建的hive外部分区表, 怎么把HDFS数据一次性全部导入hive里 ? flink里面的broadcast state值,会出现broad流的数据还没put进mapstat Flink SQL DDL 创建表时,如何定义字段的类型为proctime? 请问下窗口计算能对历史数据进行处理吗?比如kafka里的写数据没停,窗口计算的应用停掉一段时间再开起 请问下,想统计未退费的订单数量,如果一个订单退费了(发过来一个update流),flink能做到对结果进行-1吗,这样的需求sql支持吗? 使用Flink sql时,对table使用了group by操作。然后将结果转换为流时是不是只能使用的toRetractStream方法不能使用toAppendStream方法。 百亿数据实时去重,有哪位同学实践过吗? 你们的去重容许有误差?因为bloom filter其实只能给出【肯定不存在】和【可能存在】两种结果。对于可能存在这种结果,你们会认为是同一条记录? 我就运行了一个自带的示例,一运行就报错然后web页面就崩了 flink定时加载外部数据有人做过吗? NoSuchMethodError: org.apache.flink.api.java.Utils.resolveFactory(Ljava/lang/ThreadLocal;Ljava/lang/Object;)Ljava/util/Optional 各位知道这个是那个包吗? flink 可以把大量数据写入mysql吗?比如10g flink sql 解析复杂的json可以吗? 在页面上写规则,用flink执行,怎么传递给flink? 使用cep时,如何动态添加规则? 如何基于flink 实现两个很大的数据集的交集 并集 差集? flink的应用场景是?除了实时 各位好,请教一下,滑动窗口,每次滑动都全量输出结果,外部存储系统压力大,是否有办法,只输出变化的key? RichSinkFunction close只有任务结束时候才会去调用,但是数据库连接一直拿着,最后成了数据库连接超时了,大佬们有什么好的建议去处理吗?? 为啥我的自定义函数注册,然后sql中使用不了? 请问一下各位老师,flink flapmap 中的collector.collect经常出现Buffer pool is destroyed可能是什么原因呢? 用asyncIO比直接在map里实现读hbase还慢,在和hbase交互这块儿,每个算子都加了时间统计 请教一下,在yarn上运行,会找不到 org.apache.flink.streaming.util 请问下大佬,flink1.7.2对于sql的支持是不是不怎么好啊 ,跑的数据一大就会报错。 各位大佬,都用什么来监控flink集群? flink 有那种把多条消息聚合成一条的操作吗,比如说每五十条聚合成一条 如何可以让checkpoint 跳过对齐呢? 请问 阿里云实时计算(Blink)支持这4个源数据表吗?DataHub Kafka MQ MaxCompute? 为啥checkpoint时间会越来越长,请问哪位大佬知道是因为啥呢? 请问Flink的最大并行度跟kafka partition数量有关系吗? source的并行度应该最好是跟partition数量一致吧,那剩下的算子并行度呢? Flink有 MLIB库吗,为什么1.9中没有了啊? 请教一下,有没有flink ui的文章呢?在这块内存配置,我给 TM 配置的内存只有 4096 M,但是这里为什么对不上呢?请问哪里可以看 TM 内存使用了多少呢? 请教个问题,fink RichSinkFunction的invoke方法是什么时候被调用的? 请教一下,flink的window的触发条件 watermark 小于 window 的 end_time。这个 watermark 为什么是针对所有数据的呢?没有设计为一个 key 一个 watermark 呢? 就比如说有 key1、key2、key3,有3个 watermark,有 3个 window interval不支持left join那怎么可以实现把窗口内左表的数据也写到下游呢? 各位 1、sink如何只得到最终的结果而不是也输出过程结果 ;2、不同的运算如何不借助外部系统的存储作为另外一个运算的source 请教各位一个问题,flink中设置什么配置可以取消Generic这个泛型,如图报错: 有大佬在吗,线上遇到个问题,但是明明内存还有200多G,然后呢任务cancel不了,台也取消不了程序 flink遇到The assigned slot container_1540803405745_0094_01_000008_1 was removed. 有木有大佬遇到过。在flink on yarn上跑 这个报错是什么意思呢?我使用滑动窗口的时候出现报错 flink 双流union状态过期不清理有遇到的吗? 大家有没有这种场景,数据从binlog消费,这个信息是订单信息,同一个订单id,会有不同状态的变更,如果订单表与商品明细join查询,就会出现n条重复数据,这样数据就不准了,flink 这块有没有比较好的实战经验的。 大佬们、有没有人遇到过使用一分钟的TumblingEventTimeWindows,但是没有按时触发窗口、而是一直等到下一条消息进来之后才会把这个窗口的数据发送出去的? flink 有办法 读取 pytorch的 模型文件吗? 大佬们、有没有人遇到过使用一分钟的TumblingEventTimeWindows,但是没有按时触发窗口、而是一直等到下一条消息进来之后才会把这个窗口的数据发送出去的? flink timestamp转换为date类型,有什么函数吗 flink 写入mysql 很长一段时间没有写入,报错怎么解决呢? flink 有办法 读取 pytorch的 模型文件吗? 有没有大佬知道实时报表怎么做?就是统计的结果要实时更新,热数据。 刚接触flink 1.9 求问flink run脚本中怎么没有相关提交到yarn的命令了 请教一下,flink里怎么实现batch sink的操作而不导致数据丢失

问问小秘 2019-12-02 03:19:17 0 浏览量 回答数 0

回答

怎么 没人来呀 @中山野鬼###### 1、如果想去掉while(true),可以考虑通知实现; 2、关于自动重连的问题,可以考虑重发送逻辑中抽离出来,采用心跳检测完成; 3、另外发送速率统计部分也应该抽离出来。 4、上多通道要考虑资源使用可控。 5、实在不行按照业务拆分成多模块,用redis 或mq类的扩展一下架构设计; ######回复 @OS小小小 : map =(Map)JSONObject.parse(SendMsgCMPP2ThredPoolByDB.ZhangYi.take()); 换成take,阻塞线程,试试。######回复 @OS小小小 : 1、通知只是告知队列里有新的数据需要处理了; 5、内存队列换成redis队列 实现成本增加,但是可扩展性增加;######1、通知实现的话 ,岂不是 无法保证 最少发送么,又会陷入另一个问题中 是吗? 或者是我的想法不对么? 2、嗯,这一块可以这样做。谢谢你 3、速率统计这里 我目前想不到怎么抽离、既可以控制到位,又可以保证不影响。。。 5、redis 是有的 但是 redis的队列的话 跟我这个 没啥区别吧,可能速度更快一点。######while(true) 里面 没数据最起码要休眠啊,不停死循环操作,又没有休眠cpu不高才怪######回复 @OS小小小 : 休眠是必须的,只是前面有数据进来,可以用wait notify 的思路通知,思路就是这样,CountDownLatch 之类多线程通讯也可以实现有数据来就能立即处理的功能######嗯,目前在测试 排除没有数据的情况,所以这一块没有去让他休眠,后面会加进去。 就针对于目前这种情况,有啥好办法吗###### 我的思路是:一个主线程,多个任务子线程。 主线程有一层while(true),这个循环是不断的扫描LinkedBlockingQueue是否有数据,有则交个任务子线程(也就是你这里定义的线程池)处理,而不是像你这样每个子任务线程都有一个while(true) ######这才是对的做法######嗯,这思路可以。谢谢哈###### 引用来自“K袁”的评论 我的思路是:一个主线程,多个任务子线程。 主线程有一层while(true),这个循环是不断的扫描LinkedBlockingQueue是否有数据,有则交个任务子线程(也就是你这里定义的线程池)处理,而不是像你这样每个子任务线程都有一个while(true) 正确做法. 还有就是 LinkedBlockingQueue 本身阻塞的,while(true)没问题,主要在于不需要每个发送线程都去block######while(true)不加休眠就会这样###### java 的线程数量大致要和cpu数量一致,并不是越多越快,线程调度是很消耗时间的。要用好多线程,就需要设计出好的多线程业务模型,不恰当的sleep和block是性能的噩梦。利用好LinkedBlockingQueue,队列空闲时读队列的线程会释放cpu。利用消息触发后续线程工作,就没必要使用while(true)来不停的扫描。 ######@蓝水晶飞机 看到你要比牛逼,我就没有兴趣跟你说话了######回复 @不日小鸡 : 我就是装逼怎么啦,特么的装逼装出样子来的,起码也比你牛逼啊。######回复 @蓝水晶飞机 : 你说这话不能掩盖你没有回复我的问题又来回复我导致装逼失败的事实。 那你不是楼主你回复我干什么,还不是回答我的问题。 不要装逼了好么,装多就成傻逼了######回复 @不日小鸡 : 此贴楼主不是你,装什么逼。######回复 @王斌_ : 这些我都知道,我的意思是你这样回复可能会误导其他看帖子的人或者新手,让他们以为线程数就等于CPU数###### 引用来自“OS小小小”的评论 怎么 没人来呀 @中山野鬼 抬举我了。c++ 我还敢对不知深浅的人说,“权当我不懂”,java真心只是学过,没有实际工程上的经验。哈。而且我是c的思维,面对c适合的应用开发,是反对使用线程的。基本思维是,执行模块的生命周期不以任务为决定,同类的执行模块,可根据物理硬核数量,形成对应独立多个进程,但绝对不会同类的任务独立对应多个线程。哈。所以java这类面向线程的设计,没办法参与讨论。设计应用目标不同,系统组织策略自然有异。 唯一的建议是:永远不要依赖工具,特别是所谓的垃圾资源处理回收机制,无论它做的再好,一旦你依赖,必然你的代码,在不久的将来会因为系统设计规模的变大,而变的垃圾。哈。 听不懂的随便喷,希望听懂的,能记得这个观点,这不是我一个人的观点。 ######给100万像素做插值运算进行染色特效,请问单线程怎么做比多线程快?###### @乌龟壳 : 几种方法都可以,第一是按照计算步骤,每个进程处理一个步骤,然后切换共享空间(这没有数据传递逻辑上的额外开销),就是流水思维。第二个是block的思维,同样的几个进程负责相同计算,但负责不同片区。同时存在另一类的进程是对前期并发处理完的工作进行边界处理。 你这个例子体现不出进程和线程的差异的。 如果非要考虑进程和线程在片内cache的差异,如果没记错(错了大家纠正哈),进程之间的共享是在二级缓存之间吧。即便线程能做到一级缓存之间的共享,但对于这种大批量像素的计算,用进程仍然是使用 dma,将数据成块载入一级缓存区域进行处理,而这个载入工作和计算工作是同步的。不会有额外太多的延迟。 你举的这个例子,还真好是我以前的老本行。再说了。像素计算,如今都用专用计算处理器了吧。还用x86或arm来处理,不累死啊。哈。 而且这种东西java不适合,同样的处理器,用c写,基本可以比java快1到2倍。因为c可以直接根据硬件特性和计算逻辑特点有效调度底层硬件驱动方式。而java即便你用了底层优化的官方库,仍然不能保证硬件与计算目标特性的高度整合。 ######回复 @中山野鬼 : 简单来说,你的多个进程处理结果进行汇总的时候,是不是要做内存复制操作?如果是多线程天然就不用,多进程用系统的共享内存机制也不用,问题是既然用了共享内存,和多线程就没区别了。######回复 @乌龟壳 : 两回事哦。共享空间是独立的,而线程如果我没记错,全局变量,包括文件内的(静态变量)是共享的。不同线程共享同一个进程内的变量嘛。这些和业务逻辑相关的东西,每个线程又是独立一套业务逻辑,针对c语言,这样去设计,不是没事找事嘛。面向对象语言,这块都帮你处理好了,自然没有关系。######既然有共享空间了,那你所说的进程和线程实际就是一回事了。###### @乌龟壳   ,数据分两种,一种和算法或处理相关的。一种是待处理的数据。 前者,不应该共享,后者属于数据加工流程,必然存在数据传递或流动,最低成本的传递/流动方式就是共享内存,交替使用权限的思路。 但这仅仅针对待加工的数据和辅助信息,而不针对程序本身。 进程不会搞混乱这些东西特别是(待加工数据的辅助信息),而线程,就各种乱吧。哈。 进程之间,虽然用共享空间,但它本质是数据传递/流动,当你采用多机(物理机器)并发处理时,进程移动到另外一个物理主机,则共享空间就是不能选择的传递/流动方式了。但线程就没有这些概念。 ######回复 @中山野鬼 : 是啊,java天然就不是像C一样对汇编的包装。######@乌龟壳 面向企业级的各种业务,java这些没问题的。而且更有优势,面向计算设备特性的设计开发,就不行了。哈。######回复 @中山野鬼 : 也算各有场景吧,java同样可以多进程可以分布式来降低多线程的风险。java也可以静态编译成目标机器码。总之事在人为。######回复 @乌龟壳 : 高手,啥都可以,低手,依赖这些,就是各种想当然。哈哈。######回复 @中山野鬼 : 那针对java的垃圾回收,这个东西是可以调节它算法的,不算依赖工具吧,哈。不然依赖C语言语法也算依赖工具咯。哈。;-p

kun坤 2020-05-31 13:04:51 0 浏览量 回答数 0

问题

不搞清这8大算法思想,刷再多题效果也不好的 7月23日 【今日算法】

游客ih62co2qqq5ww 2020-07-29 11:10:09 3 浏览量 回答数 1

问题

【精品问答】python技术1000问(2)

问问小秘 2019-12-01 22:03:02 3129 浏览量 回答数 1

回答

*1、查询SQL尽量不要使用select ,而是select具体字段。 反例子: select * from employee; 正例子: select id,name from employee; 理由: 只取需要的字段,节省资源、减少网络开销。select * 进行查询时,很可能就不会使用到覆盖索引了,就会造成回表查询。 2、如果知道查询结果只有一条或者只要最大/最小一条记录,建议用limit 1 假设现在有employee员工表,要找出一个名字叫jay的人. CREATE TABLE `employee` ( `id` int(11) NOT NULL, `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, `date` datetime DEFAULT NULL, `sex` int(1) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 反例: select id,name from employee where name='jay' 正例 select id,name from employee where name='jay' limit 1; 理由: 加上limit 1后,只要找到了对应的一条记录,就不会继续向下扫描了,效率将会大大提高。当然,如果name是唯一索引的话,是不必要加上limit 1了,因为limit的存在主要就是为了防止全表扫描,从而提高性能,如果一个语句本身可以预知不用全表扫描,有没有limit ,性能的差别并不大。 3、应尽量避免在where子句中使用or来连接条件 新建一个user表,它有一个普通索引userId,表结构如下: CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` int(11) NOT NULL, `age` int(11) NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_userId` (`userId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 假设现在需要查询userid为1或者年龄为18岁的用户,很容易有以下sql 反例: select * from user where userid=1 or age =18 正例: //使用union all select * from user where userid=1 union all select * from user where age = 18 //或者分开两条sql写: select * from user where userid=1 select * from user where age = 18 理由: 使用or可能会使索引失效,从而全表扫描。 对于or+没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程: 全表扫描+索引扫描+合并 如果它一开始就走全表扫描,直接一遍扫描就完事。 mysql是有优化器的,处于效率与成本考虑,遇到or条件,索引可能失效,看起来也合情合理。 4、优化limit分页 我们日常做分页需求时,一般会用 limit 实现,但是当偏移量特别大的时候,查询效率就变得低下。 反例: select id,name,age from employee limit 10000,10 正例: //方案一 :返回上次查询的最大记录(偏移量) select id,name from employee where id>10000 limit 10. //方案二:order by + 索引 select id,name from employee order by id limit 10000,10 //方案三:在业务允许的情况下限制页数: 理由: 当偏移量最大的时候,查询效率就会越低,因为Mysql并非是跳过偏移量直接去取后面的数据,而是先把偏移量+要取的条数,然后再把前面偏移量这一段的数据抛弃掉再返回的。 如果使用优化方案一,返回上次最大查询记录(偏移量),这样可以跳过偏移量,效率提升不少。 方案二使用order by+索引,也是可以提高查询效率的。 方案三的话,建议跟业务讨论,有没有必要查这么后的分页啦。因为绝大多数用户都不会往后翻太多页。 5、优化你的like语句 日常开发中,如果用到模糊关键字查询,很容易想到like,但是like很可能让你的索引失效。 反例: select userId,name from user where userId like '%123'; 正例: select userId,name from user where userId like '123%'; 理由: 把%放前面,并不走索引,如下: 把% 放关键字后面,还是会走索引的。如下: 6、使用where条件限定要查询的数据,避免返回多余的行 假设业务场景是这样:查询某个用户是否是会员。曾经看过老的实现代码是这样。。。 反例: List<Long> userIds = sqlMap.queryList("select userId from user where isVip=1"); boolean isVip = userIds.contains(userId); 正例: Long userId = sqlMap.queryObject("select userId from user where userId='userId' and isVip='1' ") boolean isVip = userId!=null; 理由: 需要什么数据,就去查什么数据,避免返回不必要的数据,节省开销。 7、尽量避免在索引列上使用mysql的内置函数 业务需求:查询最近七天内登陆过的用户(假设loginTime加了索引) 反例: select userId,loginTime from loginuser where Date_ADD(loginTime,Interval 7 DAY) >=now(); 正例: explain select userId,loginTime from loginuser where loginTime >= Date_ADD(NOW(),INTERVAL - 7 DAY); 理由: 索引列上使用mysql的内置函数,索引失效 8、应尽量避免在 where 子句中对字段进行表达式操作,这将导致系统放弃使用索引而进行全表扫 反例: select * from user where age-1 =10; 正例: select * from user where age =11; 理由: 9、Inner join 、left join、right join,优先使用Inner join,如果是left join,左边表结果尽量小 Inner join 内连接,在两张表进行连接查询时,只保留两张表中完全匹配的结果集 left join 在两张表进行连接查询时,会返回左表所有的行,即使在右表中没有匹配的记录。 right join 在两张表进行连接查询时,会返回右表所有的行,即使在左表中没有匹配的记录。 都满足SQL需求的前提下,推荐优先使用Inner join(内连接),如果要使用left join,左边表数据结果尽量小,如果有条件的尽量放到左边处理。 反例: select * from tab1 t1 left join tab2 t2 on t1.size = t2.size where t1.id>2; 正例: select * from (select * from tab1 where id >2) t1 left join tab2 t2 on t1.size = t2.size; 理由: 如果inner join是等值连接,或许返回的行数比较少,所以性能相对会好一点。 同理,使用了左连接,左边表数据结果尽量小,条件尽量放到左边处理,意味着返回的行数可能比较少。 10、应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。 反例: select age,name from user where age <>18; 正例: //可以考虑分开两条sql写 select age,name from user where age <18; select age,name from user where age >18; 理由: 使用!=和<>很可能会让索引失效 11、使用联合索引时,注意索引列的顺序,一般遵循最左匹配原则。 表结构:(有一个联合索引idx_userid_age,userId在前,age在后) CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `userId` int(11) NOT NULL, `age` int(11) DEFAULT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`), KEY `idx_userid_age` (`userId`,`age`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; 反例: select * from user where age = 10; 正例: //符合最左匹配原则 select * from user where userid=10 and age =10; //符合最左匹配原则 select * from user where userid =10; 理由: 当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。 联合索引不满足最左原则,索引一般会失效,但是这个还跟Mysql优化器有关的。 12、对查询进行优化,应考虑在 where 及 order by 涉及的列上建立索引,尽量避免全表扫描。 反例: select * from user where address ='深圳' order by age ; 正例: 添加索引 alter table user add index idx_address_age (address,age) 13、如果插入数据过多,考虑批量插入。 反例: for(User u :list){ INSERT into user(name,age) values(#name#,#age#) } 正例: //一次500批量插入,分批进行 insert into user(name,age) values <foreach collection="list" item="item" index="index" separator=","> (#{item.name},#{item.age}) </foreach> 理由: 批量插入性能好,更加省时间 打个比喻:假如你需要搬一万块砖到楼顶,你有一个电梯,电梯一次可以放适量的砖(最多放500),你可以选择一次运送一块砖,也可以一次运送500,你觉得哪个时间消耗大? 14、在适当的时候,使用覆盖索引。 覆盖索引能够使得你的SQL语句不需要回表,仅仅访问索引就能够得到所有需要的数据,大大提高了查询效率。 反例: // like模糊查询,不走索引了 select * from user where userid like '%123%' 正例: //id为主键,那么为普通索引,即覆盖索引登场了。 select id,name from user where userid like '%123%'; 15、慎用distinct关键字 distinct 关键字一般用来过滤重复记录,以返回不重复的记录。在查询一个字段或者很少字段的情况下使用时,给查询带来优化效果。但是在字段很多的时候使用,却会大大降低查询效率。 反例: SELECT DISTINCT * from user; 正例: select DISTINCT name from user; 理由: 带distinct的语句cpu时间和占用时间都高于不带distinct的语句。因为当查询很多字段时,如果使用distinct,数据库引擎就会对数据进行比较,过滤掉重复数据,然而这个比较,过滤的过程会占用系统资源,cpu时间。 16、删除冗余和重复索引 反例: KEY `idx_userId` (`userId`) KEY `idx_userId_age` (`userId`,`age`) 正例: //删除userId索引,因为组合索引(A,B)相当于创建了(A)和(A,B)索引 KEY `idx_userId_age` (`userId`,`age`) 理由: 重复的索引需要维护,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能的。 17、如果数据量较大,优化你的修改/删除语句。 避免同时修改或删除过多数据,因为会造成cpu利用率过高,从而影响别人对数据库的访问。 反例: //一次删除10万或者100万+? delete from user where id <100000; //或者采用单一循环操作,效率低,时间漫长 for(User user:list){ delete from user; } 正例: //分批进行删除,如每次500 delete user where id<500 delete product where id>=500 and id<1000; 理由: 一次性删除太多数据,可能会有lock wait timeout exceed的错误,所以建议分批操作。 18、where子句中考虑使用默认值代替null。 反例: select * from user where age is not null; 正例: //设置0为默认值 select * from user where age>0; 理由: 并不是说使用了is null 或者 is not null 就会不走索引了,这个跟mysql版本以及查询成本都有关。 如果mysql优化器发现,走索引比不走索引成本还要高,肯定会放弃索引,这些条件!=,>is null,is not null经常被认为让索引失效,其实是因为一般情况下,查询的成本高,优化器自动放弃的。 如果把null值,换成默认值,很多时候让走索引成为可能,同时,表达意思会相对清晰一点。 19、不要有超过5个以上的表连接 连表越多,编译的时间和开销也就越大。 把连接表拆开成较小的几个执行,可读性更高。 如果一定需要连接很多表才能得到数据,那么意味着糟糕的设计了。 20、exist & in的合理利用 假设表A表示某企业的员工表,表B表示部门表,查询所有部门的所有员工,很容易有以下SQL: select * from A where deptId in (select deptId from B); 这样写等价于: 先查询部门表B select deptId from B 再由部门deptId,查询A的员工 select * from A where A.deptId = B.deptId 可以抽象成这样的一个循环: List<> resultSet ; for(int i=0;i<B.length;i++) { for(int j=0;j<A.length;j++) { if(A[i].id==B[j].id) { resultSet.add(A[i]); break; } } } 显然,除了使用in,我们也可以用exists实现一样的查询功能,如下: select * from A where exists (select 1 from B where A.deptId = B.deptId); 因为exists查询的理解就是,先执行主查询,获得数据后,再放到子查询中做条件验证,根据验证结果(true或者false),来决定主查询的数据结果是否得意保留。 那么,这样写就等价于: select * from A,先从A表做循环 select * from B where A.deptId = B.deptId,再从B表做循环. 同理,可以抽象成这样一个循环: List<> resultSet ; for(int i=0;i<A.length;i++) { for(int j=0;j<B.length;j++) { if(A[i].deptId==B[j].deptId) { resultSet.add(A[i]); break; } } } 数据库最费劲的就是跟程序链接释放。假设链接了两次,每次做上百万次的数据集查询,查完就走,这样就只做了两次;相反建立了上百万次链接,申请链接释放反复重复,这样系统就受不了了。即mysql优化原则,就是小表驱动大表,小的数据集驱动大的数据集,从而让性能更优。 因此,我们要选择最外层循环小的,也就是,如果B的数据量小于A,适合使用in,如果B的数据量大于A,即适合选择exist。 21、尽量用 union all 替换 union 如果检索结果中不会有重复的记录,推荐union all 替换 union。 反例: select * from user where userid=1 union select * from user where age = 10 正例: select * from user where userid=1 union all select * from user where age = 10 理由: 如果使用union,不管检索结果有没有重复,都会尝试进行合并,然后在输出最终结果前进行排序。如果已知检索结果没有重复记录,使用union all 代替union,这样会提高效率。 22、索引不宜太多,一般5个以内。 索引并不是越多越好,索引虽然提高了查询的效率,但是也降低了插入和更新的效率。 insert或update时有可能会重建索引,所以建索引需要慎重考虑,视具体情况来定。 一个表的索引数最好不要超过5个,若太多需要考虑一些索引是否没有存在的必要。 23、尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型 反例: king_id` varchar(20) NOT NULL COMMENT '守护者Id' 正例: `king_id` int(11) NOT NULL COMMENT '守护者Id'` 理由: 相对于数字型字段,字符型会降低查询和连接的性能,并会增加存储开销。 24、索引不适合建在有大量重复数据的字段上,如性别这类型数据库字段。 因为SQL优化器是根据表中数据量来进行查询优化的,如果索引列有大量重复数据,Mysql查询优化器推算发现不走索引的成本更低,很可能就放弃索引了。 25、尽量避免向客户端返回过多数据量。 假设业务需求是,用户请求查看自己最近一年观看过的直播数据。 反例: //一次性查询所有数据回来 select * from LivingInfo where watchId =useId and watchTime >= Date_sub(now(),Interval 1 Y) 正例: //分页查询 select * from LivingInfo where watchId =useId and watchTime>= Date_sub(now(),Interval 1 Y) limit offset,pageSize //如果是前端分页,可以先查询前两百条记录,因为一般用户应该也不会往下翻太多页, select * from LivingInfo where watchId =useId and watchTime>= Date_sub(now(),Interval 1 Y) limit 200 ; 26、当在SQL语句中连接多个表时,请使用表的别名,并把别名前缀于每一列上,这样语义更加清晰。 反例: select * from A inner join B on A.deptId = B.deptId; 正例: select memeber.name,deptment.deptName from A member inner join B deptment on member.deptId = deptment.deptId; 27、尽可能使用varchar/nvarchar 代替 char/nchar。 反例: `deptName` char(100) DEFAULT NULL COMMENT '部门名称' 正例: `deptName` varchar(100) DEFAULT NULL COMMENT '部门名称' 理由: 因为首先变长字段存储空间小,可以节省存储空间。 其次对于查询来说,在一个相对较小的字段内搜索,效率更高。 28、为了提高group by 语句的效率,可以在执行到该语句前,把不需要的记录过滤掉。 反例: select job,avg(salary) from employee group by job having job ='president' or job = 'managent' 正例: select job,avg(salary) from employee where job ='president' or job = 'managent' group by job; 29、如何字段类型是字符串,where时一定用引号括起来,否则索引失效 反例: select * from user where userid =123; 正例: select * from user where userid ='123'; 理由: 为什么第一条语句未加单引号就不走索引了呢? 这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。 30、使用explain 分析你SQL的计划 日常开发写SQL的时候,尽量养成一个习惯吧。用explain分析一下你写的SQL,尤其是走不走索引这一块。 explain select * from user where userid =10086 or age =18;

剑曼红尘 2020-04-21 14:01:32 0 浏览量 回答数 0

回答

初识 MyBatis MyBatis 是第一个支持自定义 SQL、存储过程和高级映射的类持久框架。MyBatis 消除了大部分 JDBC 的样板代码、手动设置参数以及检索结果。MyBatis 能够支持简单的 XML 和注解配置规则。使 Map 接口和 POJO 类映射到数据库字段和记录。 MyBatis 的特点 那么 MyBatis 具有什么特点呢?或许我们可以从如下几个方面来描述 MyBatis 中的 SQL 语句和主要业务代码分离,我们一般会把 MyBatis 中的 SQL 语句统一放在 XML 配置文件中,便于统一维护。 解除 SQL 与程序代码的耦合,通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。SQL 和代码的分离,提高了可维护性。 MyBatis 比较简单和轻量 本身就很小且简单。没有任何第三方依赖,只要通过配置 jar 包,或者如果你使用 Maven 项目的话只需要配置 Maven 以来就可以。易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。 屏蔽样板代码 MyBatis 回屏蔽原始的 JDBC 样板代码,让你把更多的精力专注于 SQL 的书写和属性-字段映射上。 编写原生 SQL,支持多表关联 MyBatis 最主要的特点就是你可以手动编写 SQL 语句,能够支持多表关联查询。 提供映射标签,支持对象与数据库的 ORM 字段关系映射 ORM 是什么?对象关系映射(Object Relational Mapping,简称ORM) ,是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中。本质上就是将数据从一种形式转换到另外一种形式。 提供 XML 标签,支持编写动态 SQL。 你可以使用 MyBatis XML 标签,起到 SQL 模版的效果,减少繁杂的 SQL 语句,便于维护。 MyBatis 整体架构 MyBatis 最上面是接口层,接口层就是开发人员在 Mapper 或者是 Dao 接口中的接口定义,是查询、新增、更新还是删除操作;中间层是数据处理层,主要是配置 Mapper -> XML 层级之间的参数映射,SQL 解析,SQL 执行,结果映射的过程。上述两种流程都由基础支持层来提供功能支撑,基础支持层包括连接管理,事务管理,配置加载,缓存处理等。 接口层 在不与Spring 集成的情况下,使用 MyBatis 执行数据库的操作主要如下: InputStream is = Resources.getResourceAsStream("myBatis-config.xml"); SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder.build(is); sqlSession = factory.openSession(); 其中的SqlSessionFactory,SqlSession是 MyBatis 接口的核心类,尤其是 SqlSession,这个接口是MyBatis 中最重要的接口,这个接口能够让你执行命令,获取映射,管理事务。 数据处理层 配置解析 在 Mybatis 初始化过程中,会加载 mybatis-config.xml 配置文件、映射配置文件以及 Mapper 接口中的注解信息,解析后的配置信息会形成相应的对象并保存到 Configration 对象中。之后,根据该对象创建SqlSessionFactory 对象。待 Mybatis 初始化完成后,可以通过 SqlSessionFactory 创建 SqlSession 对象并开始数据库操作。 SQL 解析与 scripting 模块 Mybatis 实现的动态 SQL 语句,几乎可以编写出所有满足需要的 SQL。 Mybatis 中 scripting 模块会根据用户传入的参数,解析映射文件中定义的动态 SQL 节点,形成数据库能执行的SQL 语句。 SQL 执行 SQL 语句的执行涉及多个组件,包括 MyBatis 的四大核心,它们是: Executor、StatementHandler、ParameterHandler、ResultSetHandler。SQL 的执行过程可以用下面这幅图来表示 MyBatis 层级结构各个组件的介绍(这里只是简单介绍,具体介绍在后面): SqlSession: ,它是 MyBatis 核心 API,主要用来执行命令,获取映射,管理事务。接收开发人员提供 Statement Id 和参数。并返回操作结果。Executor :执行器,是 MyBatis 调度的核心,负责 SQL 语句的生成以及查询缓存的维护。StatementHandler : 封装了JDBC Statement 操作,负责对 JDBC Statement 的操作,如设置参数、将Statement 结果集转换成 List 集合。ParameterHandler : 负责对用户传递的参数转换成 JDBC Statement 所需要的参数。ResultSetHandler : 负责将 JDBC 返回的 ResultSet 结果集对象转换成 List 类型的集合。TypeHandler : 用于 Java 类型和 JDBC 类型之间的转换。MappedStatement : 动态 SQL 的封装SqlSource : 表示从 XML 文件或注释读取的映射语句的内容,它创建将从用户接收的输入参数传递给数据库的 SQL。Configuration: MyBatis 所有的配置信息都维持在 Configuration 对象之中。 基础支持层 反射模块 Mybatis 中的反射模块,对 Java 反射进行了很好的封装,提供了简易的 API,方便上层调用,并且对反射操作进行了一系列的优化,比如,缓存了类的 元数据(MetaClass)和对象的元数据(MetaObject),提高了反射操作的性能。 类型转换模块 Mybatis 的别名机制,能够简化配置文件,该机制是类型转换模块的主要功能之一。类型转换模块的另一个功能是实现 JDBC 类型与 Java 类型的转换。在 SQL 语句绑定参数时,会将数据由 Java 类型转换成 JDBC 类型;在映射结果集时,会将数据由 JDBC 类型转换成 Java 类型。 日志模块 在 Java 中,有很多优秀的日志框架,如 Log4j、Log4j2、slf4j 等。Mybatis 除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架。 资源加载模块 该模块主要封装了类加载器,确定了类加载器的使用顺序,并提供了加载类文件和其它资源文件的功能。 解析器模块 该模块有两个主要功能:一个是封装了 XPath,为 Mybatis 初始化时解析 mybatis-config.xml配置文件以及映射配置文件提供支持;另一个为处理动态 SQL 语句中的占位符提供支持。 数据源模块 Mybatis 自身提供了相应的数据源实现,也提供了与第三方数据源集成的接口。数据源是开发中的常用组件之一,很多开源的数据源都提供了丰富的功能,如连接池、检测连接状态等,选择性能优秀的数据源组件,对于提供ORM 框架以及整个应用的性能都是非常重要的。 事务管理模块 一般地,Mybatis 与 Spring 框架集成,由 Spring 框架管理事务。但 Mybatis 自身对数据库事务进行了抽象,提供了相应的事务接口和简单实现。 缓存模块 Mybatis 中有一级缓存和二级缓存,这两级缓存都依赖于缓存模块中的实现。但是需要注意,这两级缓存与Mybatis 以及整个应用是运行在同一个 JVM 中的,共享同一块内存,如果这两级缓存中的数据量较大,则可能影响系统中其它功能,所以需要缓存大量数据时,优先考虑使用 Redis、Memcache 等缓存产品。 Binding 模块 在调用 SqlSession 相应方法执行数据库操作时,需要制定映射文件中定义的 SQL 节点,如果 SQL 中出现了拼写错误,那就只能在运行时才能发现。为了能尽早发现这种错误,Mybatis 通过 Binding 模块将用户自定义的Mapper 接口与映射文件关联起来,系统可以通过调用自定义 Mapper 接口中的方法执行相应的 SQL 语句完成数据库操作,从而避免上述问题。注意,在开发中,我们只是创建了 Mapper 接口,而并没有编写实现类,这是因为 Mybatis 自动为 Mapper 接口创建了动态代理对象。 MyBatis 核心组件 在认识了 MyBatis 并了解其基础架构之后,下面我们来看一下 MyBatis 的核心组件,就是这些组件实现了从 SQL 语句到映射到 JDBC 再到数据库字段之间的转换,执行 SQL 语句并输出结果集。首先来认识 MyBatis 的第一个核心组件 SqlSessionFactory 对于任何框架而言,在使用该框架之前都要经历过一系列的初始化流程,MyBatis 也不例外。MyBatis 的初始化流程如下 String resource = "org/mybatis/example/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); sqlSessionFactory.openSession(); 上述流程中比较重要的一个对象就是SqlSessionFactory,SqlSessionFactory 是 MyBatis 框架中的一个接口,它主要负责的是 MyBatis 框架初始化操作 为开发人员提供SqlSession 对象 SqlSessionFactory 有两个实现类,一个是 SqlSessionManager 类,一个是 DefaultSqlSessionFactory 类 DefaultSqlSessionFactory : SqlSessionFactory 的默认实现类,是真正生产会话的工厂类,这个类的实例的生命周期是全局的,它只会在首次调用时生成一个实例(单例模式),就一直存在直到服务器关闭。 SqlSessionManager : 已被废弃,原因大概是: SqlSessionManager 中需要维护一个自己的线程池,而使用MyBatis 更多的是要与 Spring 进行集成,并不会单独使用,所以维护自己的 ThreadLocal 并没有什么意义,所以 SqlSessionManager 已经不再使用。 ####SqlSessionFactory 的执行流程 下面来对 SqlSessionFactory 的执行流程来做一个分析 首先第一步是 SqlSessionFactory 的创建 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 1 从这行代码入手,首先创建了一个 SqlSessionFactoryBuilder 工厂,这是一个建造者模式的设计思想,由 builder 建造者来创建 SqlSessionFactory 工厂 然后调用 SqlSessionFactoryBuilder 中的 build 方法传递一个InputStream 输入流,Inputstream 输入流中就是你传过来的配置文件 mybatis-config.xml,SqlSessionFactoryBuilder 根据传入的 InputStream 输入流和environment、properties属性创建一个XMLConfigBuilder对象。SqlSessionFactoryBuilder 对象调用XMLConfigBuilder 的parse()方法,流程如下。 XMLConfigBuilder 会解析/configuration标签,configuration 是 MyBatis 中最重要的一个标签,下面流程会介绍 Configuration 标签。 MyBatis 默认使用 XPath 来解析标签,关于 XPath 的使用,参见 https://www.w3school.com.cn/xpath/index.asp 在 parseConfiguration 方法中,会对各个在 /configuration 中的标签进行解析 重要配置 说一下这些标签都是什么意思吧 properties,外部属性,这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。 <properties> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="root" /> </properties> 一般用来给 environment 标签中的 dataSource 赋值 <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driver}" /> <property name="url" value="${url}" /> <property name="username" value="${username}" /> <property name="password" value="${password}" /> </dataSource> </environment> 还可以通过外部属性进行配置,但是我们这篇文章以原理为主,不会介绍太多应用层面的操作。 settings ,MyBatis 中极其重要的配置,它们会改变 MyBatis 的运行时行为。 settings 中配置有很多,具体可以参考 https://mybatis.org/mybatis-3/zh/configuration.html#settings 详细了解。这里介绍几个平常使用过程中比较重要的配置 一般使用如下配置 <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> </settings> typeAliases,类型别名,类型别名是为 Java 类型设置的一个名字。 它只和 XML 配置有关。 <typeAliases> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases> 当这样配置时,Blog 可以用在任何使用 domain.blog.Blog 的地方。 typeHandlers,类型处理器,无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。 在 org.apache.ibatis.type 包下有很多已经实现好的 TypeHandler,可以参考如下 你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很方便的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个 JDBC 类型。 objectFactory,对象工厂,MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。 public class ExampleObjectFactory extends DefaultObjectFactory { public Object create(Class type) { return super.create(type); } public Object create(Class type, List constructorArgTypes, List constructorArgs) { return super.create(type, constructorArgTypes, constructorArgs); } public void setProperties(Properties properties) { super.setProperties(properties); } public boolean isCollection(Class type) { return Collection.class.isAssignableFrom(type); } } 然后需要在 XML 中配置此对象工厂 <objectFactory type="org.mybatis.example.ExampleObjectFactory"> <property name="someProperty" value="100"/> </objectFactory> plugins,插件开发,插件开发是 MyBatis 设计人员给开发人员留给自行开发的接口,MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。MyBatis 允许使用插件来拦截的方法调用包括:Executor、ParameterHandler、ResultSetHandler、StatementHandler 接口,这几个接口也是 MyBatis 中非常重要的接口,我们下面会详细介绍这几个接口。 environments,MyBatis 环境配置,MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中 使用相同的 SQL 映射。 这里注意一点,虽然 environments 可以指定多个环境,但是 SqlSessionFactory 只能有一个,为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties); databaseIdProvider ,数据库厂商标示,MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 <databaseIdProvider type="DB_VENDOR"> <property name="SQL Server" value="sqlserver"/> <property name="DB2" value="db2"/> <property name="Oracle" value="oracle" /> </databaseIdProvider> mappers,映射器,这是告诉 MyBatis 去哪里找到这些 SQL 语句,mappers 映射配置有四种方式 上面的一个个属性都对应着一个解析方法,都是使用 XPath 把标签进行解析,解析完成后返回一个 DefaultSqlSessionFactory 对象,它是 SqlSessionFactory 的默认实现类。这就是 SqlSessionFactoryBuilder 的初始化流程,通过流程我们可以看到,初始化流程就是对一个个 /configuration 标签下子标签的解析过程。 SqlSession 在 MyBatis 初始化流程结束,也就是 SqlSessionFactoryBuilder -> SqlSessionFactory 的获取流程后,我们就可以通过 SqlSessionFactory 对象得到 SqlSession 然后执行 SQL 语句了。具体来看一下这个过程‘ 在 SqlSessionFactory.openSession 过程中我们可以看到,会调用到 DefaultSqlSessionFactory 中的 openSessionFromDataSource 方法,这个方法主要创建了两个与我们分析执行流程重要的对象,一个是 Executor 执行器对象,一个是 SqlSession 对象。执行器我们下面会说,现在来说一下 SqlSession 对象 SqlSession 对象是 MyBatis 中最重要的一个对象,这个接口能够让你执行命令,获取映射,管理事务。SqlSession 中定义了一系列模版方法,让你能够执行简单的 CRUD 操作,也可以通过 getMapper 获取 Mapper 层,执行自定义 SQL 语句,因为 SqlSession 在执行 SQL 语句之前是需要先开启一个会话,涉及到事务操作,所以还会有 commit、 rollback、close 等方法。这也是模版设计模式的一种应用。 MapperProxy MapperProxy 是 Mapper 映射 SQL 语句的关键对象,我们写的 Dao 层或者 Mapper 层都是通过 MapperProxy 来和对应的 SQL 语句进行绑定的。下面我们就来解释一下绑定过程 这就是 MyBatis 的核心绑定流程,我们可以看到 SqlSession 首先调用 getMapper 方法,我们刚才说到 SqlSession 是大哥级别的人物,只定义标准(有一句话是怎么说的来着,一流的企业做标准,二流的企业做品牌,三流的企业做产品)。 SqlSession 不愿意做的事情交给 Configuration 这个手下去做,但是 Configuration 也是有小弟的,它不愿意做的事情直接甩给小弟去做,这个小弟是谁呢?它就是 MapperRegistry,马上就到核心部分了。MapperRegistry 相当于项目经理,项目经理只从大面上把握项目进度,不需要知道手下的小弟是如何工作的,把任务完成了就好。最终真正干活的还是 MapperProxyFactory。看到这段代码 Proxy.newProxyInstance ,你是不是有一种恍然大悟的感觉,如果你没有的话,建议查阅一下动态代理的文章,这里推荐一篇 (https://www.jianshu.com/p/95970b089360) 也就是说,MyBatis 中 Mapper 和 SQL 语句的绑定正是通过动态代理来完成的。 通过动态代理,我们就可以方便的在 Dao 层或者 Mapper 层定义接口,实现自定义的增删改查操作了。那么具体的执行过程是怎么样呢?上面只是绑定过程,别着急,下面就来探讨一下 SQL 语句的执行过程。 MapperProxyFactory 会生成代理对象,这个对象就是 MapperProxy,最终会调用到 mapperMethod.execute 方法,execute 方法比较长,其实逻辑比较简单,就是判断是 插入、更新、删除 还是 查询 语句,其中如果是查询的话,还会判断返回值的类型,我们可以点进去看一下都是怎么设计的。 很多代码其实可以忽略,只看我标出来的重点就好了,我们可以看到,不管你前面经过多少道关卡处理,最终都逃不过 SqlSession 这个老大制定的标准。 我们以 selectList 为例,来看一下下面的执行过程。 这是 DefaultSqlSession 中 selectList 的代码,我们可以看到出现了 executor,这是什么呢?我们下面来解释。 Executor 还记得我们之前的流程中提到了 Executor(执行器) 这个概念吗?我们来回顾一下它第一次出现的位置。 由 Configuration 对象创建了一个 Executor 对象,这个 Executor 是干嘛的呢?下面我们就来认识一下 Executor 的继承结构 每一个 SqlSession 都会拥有一个 Executor 对象,这个对象负责增删改查的具体操作,我们可以简单的将它理解为 JDBC 中 Statement 的封装版。 也可以理解为 SQL 的执行引擎,要干活总得有一个发起人吧,可以把 Executor 理解为发起人的角色。 首先先从 Executor 的继承体系来认识一下 如上图所示,位于继承体系最顶层的是 Executor 执行器,它有两个实现类,分别是BaseExecutor和 CachingExecutor。 BaseExecutor 是一个抽象类,这种通过抽象的实现接口的方式是适配器设计模式之接口适配 的体现,是Executor 的默认实现,实现了大部分 Executor 接口定义的功能,降低了接口实现的难度。BaseExecutor 的子类有三个,分别是 SimpleExecutor、ReuseExecutor 和 BatchExecutor。 SimpleExecutor : 简单执行器,是 MyBatis 中默认使用的执行器,每执行一次 update 或 select,就开启一个Statement 对象,用完就直接关闭 Statement 对象(可以是 Statement 或者是 PreparedStatment 对象) ReuseExecutor : 可重用执行器,这里的重用指的是重复使用 Statement,它会在内部使用一个 Map 把创建的Statement 都缓存起来,每次执行 SQL 命令的时候,都会去判断是否存在基于该 SQL 的 Statement 对象,如果存在 Statement 对象并且对应的 connection 还没有关闭的情况下就继续使用之前的 Statement 对象,并将其缓存起来。因为每一个 SqlSession 都有一个新的 Executor 对象,所以我们缓存在 ReuseExecutor 上的 Statement作用域是同一个 SqlSession。 BatchExecutor : 批处理执行器,用于将多个 SQL 一次性输出到数据库 CachingExecutor: 缓存执行器,先从缓存中查询结果,如果存在就返回之前的结果;如果不存在,再委托给Executor delegate 去数据库中取,delegate 可以是上面任何一个执行器。 Executor 的创建和选择 我们上面提到 Executor 是由 Configuration 创建的,Configuration 会根据执行器的类型创建,如下 这一步就是执行器的创建过程,根据传入的 ExecutorType 类型来判断是哪种执行器,如果不指定 ExecutorType ,默认创建的是简单执行器。它的赋值可以通过两个地方进行赋值: 可以通过 标签来设置当前工程中所有的 SqlSession 对象使用默认的 Executor <settings> <!--取值范围 SIMPLE, REUSE, BATCH --> <setting name="defaultExecutorType" value="SIMPLE"/> </settings> 另外一种直接通过Java对方法赋值的方式 session = factory.openSession(ExecutorType.BATCH); Executor 的具体执行过程 Executor 中的大部分方法的调用链其实是差不多的,下面是深入源码分析执行过程,如果你没有时间或者暂时不想深入研究的话,给你下面的执行流程图作为参考。 我们紧跟着上面的 selectList 继续分析,它会调用到 executor.query 方法。 当有一个查询请求访问的时候,首先会经过 Executor 的实现类 CachingExecutor ,先从缓存中查询 SQL 是否是第一次执行,如果是第一次执行的话,那么就直接执行 SQL 语句,并创建缓存,如果第二次访问相同的 SQL 语句的话,那么就会直接从缓存中提取。 上面这段代码是从 selectList -> 从缓存中 query 的具体过程。可能你看到这里有些觉得类都是什么东西,我想鼓励你一下,把握重点,不用每段代码都看,从找到 SQL 的调用链路,其他代码想看的时候在看,看源码就是很容易发蒙,容易烦躁,但是切记一点,把握重点。 上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句,注意, list = doQuery 是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler 上面代码会判断缓存中是否有这条 SQL 语句的执行结果,如果没有的话,就再重新创建 Executor 执行器执行 SQL 语句,注意, list = doQuery 是真正执行 SQL 语句的过程,这个过程中会创建我们上面提到的三种执行器,这里我们使用的是简单执行器。 到这里,执行器所做的工作就完事了,Executor 会把后续的工作交给 StatementHandler 继续执行。下面我们来认识一下 StatementHandler StatementHandler 的继承结构 有没有感觉和 Executor 的继承体系很相似呢?最顶级接口是四大组件对象,分别有两个实现类 BaseStatementHandler 和 RoutingStatementHandler,BaseStatementHandler 有三个实现类, 他们分别是 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。 RoutingStatementHandler : RoutingStatementHandler 并没有对 Statement 对象进行使用,只是根据StatementType 来创建一个代理,代理的就是对应Handler的三种实现类。在MyBatis工作时,使用的StatementHandler 接口对象实际上就是 RoutingStatementHandler 对象。 BaseStatementHandler : 是 StatementHandler 接口的另一个实现类,它本身是一个抽象类,用于简化StatementHandler 接口实现的难度,属于适配器设计模式体现,它主要有三个实现类 SimpleStatementHandler: 管理 Statement 对象并向数据库中推送不需要预编译的SQL语句。PreparedStatementHandler: 管理 Statement 对象并向数据中推送需要预编译的SQL语句。CallableStatementHandler:管理 Statement 对象并调用数据库中的存储过程。 StatementHandler 的创建和源码分析 我们继续来分析上面 query 的调用链路,StatementHandler 的创建过程如下 MyBatis 会根据 SQL 语句的类型进行对应 StatementHandler 的创建。我们以预处理 StatementHandler 为例来讲解一下 执行器不仅掌管着 StatementHandler 的创建,还掌管着创建 Statement 对象,设置参数等,在创建完 PreparedStatement 之后,我们需要对参数进行处理了。 如 如果用一副图来表示一下这个执行流程的话我想是这样 这里我们先暂停一下,来认识一下第三个核心组件 ParameterHandler ParameterHandler - ParameterHandler 介绍 ParameterHandler 相比于其他的组件就简单很多了,ParameterHandler 译为参数处理器,负责为 PreparedStatement 的 sql 语句参数动态赋值,这个接口很简单只有两个方法 ParameterHandler 只有一个实现类 DefaultParameterHandler , 它实现了这两个方法。 getParameterObject: 用于读取参数setParameters: 用于对 PreparedStatement 的参数赋值ParameterHandler 的解析过程 上面我们讨论过了 ParameterHandler 的创建过程,下面我们继续上面 parameterSize 流程 这就是具体参数的解析过程了,下面我们来描述一下 下面用一个流程图表示一下 ParameterHandler 的解析过程,以简单执行器为例 我们在完成 ParameterHandler 对 SQL 参数的预处理后,回到 SimpleExecutor 中的 doQuery 方法 上面又引出来了一个重要的组件那就是 ResultSetHandler,下面我们来认识一下这个组件 ResultSetHandler - ResultSetHandler 简介 ResultSetHandler 也是一个非常简单的接口 ResultSetHandler 是一个接口,它只有一个默认的实现类,像是 ParameterHandler 一样,它的默认实现类是DefaultResultSetHandler ResultSetHandler 解析过程 MyBatis 只有一个默认的实现类就是 DefaultResultSetHandler,DefaultResultSetHandler 主要负责处理两件事 处理 Statement 执行后产生的结果集,生成结果列表 处理存储过程执行后的输出参数 按照 Mapper 文件中配置的 ResultType 或 ResultMap 来封装成对应的对象,最后将封装的对象返回即可。 其中涉及的主要对象有: ResultSetWrapper : 结果集的包装器,主要针对结果集进行的一层包装,它的主要属性有 ResultSet : Java JDBC ResultSet 接口表示数据库查询的结果。 有关查询的文本显示了如何将查询结果作为java.sql.ResultSet 返回。 然后迭代此ResultSet以检查结果。 TypeHandlerRegistry: 类型注册器,TypeHandlerRegistry 在初始化的时候会把所有的 Java类型和类型转换器进行注册。 ColumnNames: 字段的名称,也就是查询操作需要返回的字段名称 ClassNames: 字段的类型名称,也就是 ColumnNames 每个字段名称的类型 JdbcTypes: JDBC 的类型,也就是 java.sql.Types 类型 ResultMap: 负责处理更复杂的映射关系 在 DefaultResultSetHandler 中处理完结果映射,并把上述结构返回给调用的客户端,从而执行完成一条完整的SQL语句。 内容转载自:CSDN博主:cxuann 原文链接:https://blog.csdn.net/qq_36894974/article/details/104132876?depth_1-utm_source=distribute.pc_feed.none-task&request_id=&utm_source=distribute.pc_feed.none-task

问问小秘 2020-03-05 15:44:27 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站