• 关于

    信息率什么意思

    的搜索结果

问题

【精品问答】智能语音交互

montos 2020-04-09 09:57:02 12 浏览量 回答数 1

问题

网站优化之教你如何写好文章标题

aizhan 2019-12-01 20:55:50 7236 浏览量 回答数 0

回答

大数据就是多,就是多。原来的设备存不下、算不动。 ——啪菠萝·毕加索 大数据,不是随机样本,而是所有数据;不是精确性,而是混杂性;不是因果关系,而是相关关系。—— Schönberger 顾名思义“大数据”,从字面意思来理解就是“大量的数据”。 从技术的的角度来解释,大数据就是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合,是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。 IBM提出大数据具有5V特点:Volume(大量)、Velocity(高速)、Variety(多样)、Value(低价值密度)、Veracity(真实性)。 我们所谈论的大数据实际上更多是从应用的层面,比如某公司搜集、整理了大量的用户行为信息,然后通过数据分析手段对这些信息进行分析从而得出对公司有利用价值的结果。 比如:头条的推荐机制,就是建立在对海量用户的阅读信息的搜集、分析之上。这就是大数据在现实中具体体现。 那Hadoop又是什么?它和大数据又有什么联系呢? Hadoop是一个对海量数据进行处理的分布式系统架构,可以理解为Hadoop就是一个对大量的数据进行分析的工具,和其他组件搭配使用,来完成对大量数据的收集、存储和计算。 Hadoop框架最核心的设计就是:HDFS 和 MapReduce。 HDFS为海量的数据提供了存储;MapReduce为海量的数据提供了计算。 一套完整的Hadoop大数据生态系统基本包含这些组件。 HDFS:Hadoop分布式文件系统,专门存储超大数据文件,为整个Hadoop生态圈提供了基础的存储服务。 MapReduce:分布式离线计算框架,用来处理大量的已经存储在本地的离线数据。 Storm:分布式实时计算,主要特点是实时性,用来处理实时产生的数据。 ZooKeeper:用于Hadoop的分布式协调服务。Hadoop的许多组件依赖于Zookeeper,它运行在计算机集群上面,用于管理Hadoop操作。 HBase:是一个建立在HDFS之上,面向列的NoSQL数据库,用于快速读/写大量数据。 Hive:基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表。 Sqoop:是一个连接工具,用于在关系数据库、数据仓库和Hadoop之间转移数据。 Pig:它是MapReduce编程的复杂性的抽象。Pig平台包括运行环境和用于分析Hadoop数据集的脚本语言(Pig Latin)。

1748847708358317 2019-12-02 03:11:07 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

长尾关键词有什么特征

浅念夏天 2019-12-01 21:54:36 6554 浏览量 回答数 3

问题

个推推送Android问题检测 - 安卓报错

montos 2020-06-01 12:47:32 0 浏览量 回答数 1

问题

个推推送Android问题检测 :配置报错 

kun坤 2020-05-31 21:38:58 1 浏览量 回答数 1

回答

提交注册表单到后端处理时,调用第三方短信服务(手机号码,后端生成验证码),限制多少时间内重发。验证码可以保存数据库中有效时间,或者session中设置过期时间问题有些太开放,宽松制约。重新考虑一下需求;如你所说的确实是个问题。或者其他人解答一下手机号码注册,短信只作验证功能(省事,用户群体比较有质量,手机号码唯一性,防止恶意刷注册用户数)/邮箱验证也可以,用户体验上手机较好(后期还可以通过手机号码去分析用户) 如果每次登陆都用短信验证码(短信服务还是要钱的...这个一天登陆一多就懂) ######首先 非常感谢你这么认真的回答。 我看完后 再回复你。先谢谢你###### 1. 你们服务端生成 短信 内容 提交到他们那,验证码可以放到缓存里,用户确定的时候检查缓存.写一个公共的服务组件, Linux可用crontab Win可用定时任务,在指定时间段内 每分钟查询下数据库,提交到短信提供商.最好不要使用短信登陆. ######关于问题1 ,一般都是用户前台输入手机号,点击获取验证码按钮后先在自己服务器根据短信服务商的接口规范生成url(包括验证码的生成,生成之后保存在session或者数据库中),然后用curl发请求,收到一个唯一的短信id就表示发送成功了(但是有可能是对方服务器出了问题,收到了id用户还没收到短信,我遇到过这个问题,最后他们换了一个线路解决了)###### 引用来自“p2ng”的评论 提交注册表单到后端处理时,调用第三方短信服务(手机号码,后端生成验证码),限制多少时间内重发。验证码可以保存数据库中有效时间,或者session中设置过期时间问题有些太开放,宽松制约。重新考虑一下需求;如你所说的确实是个问题。或者其他人解答一下手机号码注册,短信只作验证功能(省事,用户群体比较有质量,手机号码唯一性,防止恶意刷注册用户数)/邮箱验证也可以,用户体验上手机较好(后期还可以通过手机号码去分析用户) 如果每次登陆都用短信验证码(短信服务还是要钱的...这个一天登陆一多就懂) 回复1: 一般大家的普通做法是 保持数据库中还是SESSION中 还是内存中啊。  验证码好像就是临时的吧。 这个手机验证码 是需要我自定义生成吗? 我做过登陆图形验证码。用户输入后判断下。就行。提交一次重新生成。 但这个貌似可以提交多次,直到你输入正确的短信验证码是吧? 回复2: 举个简单例子吧: 我加入要做个提醒的功能。  我在web网站上 设置好时间,设置后内容。然后我当天那个时间收到这个短信内容。 我就是提供给会员这个事情。  比如我做一个比赛预告的WEB页面,用户点击比赛前1小时短信提醒我。  回复3: 每次短信登陆的确很麻烦也很费钱。 但类似微博就是这种的啊。 可以提供手机短信登陆的啊。 短信登陆唯一好处就是 用户注册/登陆都一样的,这样用户注册的时候就没有密码设置这一项,用户第一次实用的体验度会很好。。。。 ###### 引用来自“金马超”的评论 你们服务端生成 短信 内容 提交到他们那,验证码可以放到缓存里,用户确定的时候检查缓存.写一个公共的服务组件, Linux可用crontab Win可用定时任务,在指定时间段内 每分钟查询下数据库,提交到短信提供商.最好不要使用短信登陆. 回复1: 大体思路我明白了,只是之前没做过短信注册这个模块。 我看看第三方1069通道的 应该不难。 或者网上搜搜下 回复2: linux的crontab定时任务和win我都会,但我的意思是 一个用户 写周四下午3点 那我这边就执行一个定时任务会不会太浪费了?也太多了? 或者你说,每分钟查下数据库 也就是每隔一分钟执行一个php脚本文件,遍历循环下? 有的话就发 没有的 就不发。 就等于轮询? 这样是不是很耗费服务器资源? 有没有其他解解方法 回复3: 你的意思是 短信注册可以,然后引导用户设置密码。 但手机短信登陆不建议ma? 但我的意思,像weibo就是手机短信验证码直接登陆 携程也是这样 还有一种是你说的只能手机+密码来登陆 那我的意思是,如果二者并行,怎么设计这个表。。。 ###### 引用来自“西南茂”的评论关于问题1 ,一般都是用户前台输入手机号,点击获取验证码按钮后先在自己服务器根据短信服务商的接口规范生成url(包括验证码的生成,生成之后保存在session或者数据库中),然后用curl发请求,收到一个唯一的短信id就表示发送成功了(但是有可能是对方服务器出了问题,收到了id用户还没收到短信,我遇到过这个问题,最后他们换了一个线路解决了) 回复1: 你的流程是   用户填手机号---》用户点击提交获取验证码--》服务器生成一个验证码---》这个验证码通过sdk 用curl发送到短信运营商---》短信运营商服务器收到后发到用户手机号上---》 用户收到输入这个验证码--》我们后台核对  OK? ######对,差不多就是这个流程###### 引用来自“金马超”的评论 你们服务端生成 短信 内容 提交到他们那,验证码可以放到缓存里,用户确定的时候检查缓存.写一个公共的服务组件, Linux可用crontab Win可用定时任务,在指定时间段内 每分钟查询下数据库,提交到短信提供商.最好不要使用短信登陆. 引用来自“kacc850”的评论 回复1: 大体思路我明白了,只是之前没做过短信注册这个模块。 我看看第三方1069通道的 应该不难。 或者网上搜搜下 回复2: linux的crontab定时任务和win我都会,但我的意思是 一个用户 写周四下午3点 那我这边就执行一个定时任务会不会太浪费了?也太多了? 或者你说,每分钟查下数据库 也就是每隔一分钟执行一个php脚本文件,遍历循环下? 有的话就发 没有的 就不发。 就等于轮询? 这样是不是很耗费服务器资源? 有没有其他解解方法 回复3: 你的意思是 短信注册可以,然后引导用户设置密码。 但手机短信登陆不建议ma? 但我的意思,像weibo就是手机短信验证码直接登陆 携程也是这样 还有一种是你说的只能手机+密码来登陆 那我的意思是,如果二者并行,怎么设计这个表。。。 写成一个公共的组件...   这个服务从指定的一张表里查询数据 eg  ID 内容 下发时间 手机号 ... 只要代码没问题的话  对服务器来说不会是什么太大的问题.嗯   是这样的,不建议短信登陆,可以用短信找回密码. 你们公司不是微博/携程,没人家那种财力,最好不要这么做, 而且短信这个行业也不是你想的那么简单,提交就能发的. 如果要并行的话,单独建立一张表用来记录短信登陆比较好. 用户表只放基本信息,短信登陆表 带上用户名,手机号,登陆时间,IP可有可无. 和用户表稍微关联下就行. ######硕达通短信平台,发验证码5秒到,发通知5秒到,速度快,到达率98%以上,成功计费(失败不计费)实时状态报告(成功失败一目了然)支持上下行 北京硕达通  www.shdat.com  买短信有红包!######凌凯短信:快/3秒响应 ·12年品牌塑造,三网(移动、联通、电信)通道,覆盖所有手机号码 ·3秒快速响应,行业领先(www.028lk.com)######雨林木风短信平台,三网合一,五秒达到,到达率99%,欢迎站内信哟~~~~

kun坤 2020-06-11 10:43:41 0 浏览量 回答数 0

问题

Android 常见问题有哪些

猫饭先生 2019-12-01 21:59:21 792 浏览量 回答数 0

问题

个推推送Android问题检测:报错

kun坤 2020-06-13 23:53:00 0 浏览量 回答数 1

回答

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

回答

楼主这是节点遍历时,通过函数指针动态加载节点处理函数的设计方法。这个几年前写过,后来不这么写了。主要有以下几个问题。 1、每个节点被访问时,操作可能不一样,通用的函数指针的入口参数,要么可变参,要么多套,入口指针,都是很繁琐的事情,把代码逻辑结构搞的会更复杂。 2、操作函数和操作对象没有绑定,这个在规模开发时,很容易引起混乱。这样设计的代码,我自己到后面都觉得混乱,更别说基于我的架子让别人开发,楼主你的例子不够复杂可能感觉不到。 3、上面两个问题,也导致,代码复用率不高。 现在我的设计思想,如果是基础的数据结构,如同你这个例子中就是个线形表,我都全部独立成模版,在头文件中。 特定数据的处理不会和处理方法绑定,而是调用不同通用模块来处理,这样是尽可能的让数据和处理松耦合。而关联数据再怎么关联,处理时,也是一类整体处理的,同时一批数据再怎么复合,总可以拆成不同大部分串联处理(例如,读取、处理、写出,通过增加cache的方式可以分批分步骤完成,而不是读、处理、写 、一个完整操作周期,仅针对一个单元)。所以这类数据的整体处理落在通用模块里,通过数据和处理的紧耦合的提升效率。 ###### 另外,补充说一下,楼主的函数式风格,和我的函数式风格理解相差颇大。我的理解如下,所谓函数式风格,是将一批数据的若干处理,分解为正交串接的多个子步骤,每个步骤都是对整体数据的某个操作的实现。楼主的方案实质是对一个处理,可以挂接不同的操作方法。 我的理解函数式的风格在于每个独立模块处理极少的有逻辑关联的操作,可以看作针对一个数据池的原子操作。依次将数据池的数据灌入不同的独立模块,实现数据处理。当然差异的模块调用顺序和不同处理模块的组合,可以有不同的效果。 但无论如何,都是函数与数据松耦合的设计。这个和面向对象是反过来的。 ######相互嵌套耦合,牵一发动全身######楼主的代码有很浓重的其他语言的味道######楼主文章不错,我看现在的C模块基本就是你所说的面向对象风格,其实就是用数据结构组织起来。###### 引用来自“中山野鬼”的答案 楼主这是节点遍历时,通过函数指针动态加载节点处理函数的设计方法。这个几年前写过,后来不这么写了。主要有以下几个问题。 1、每个节点被访问时,操作可能不一样,通用的函数指针的入口参数,要么可变参,要么多套,入口指针,都是很繁琐的事情,把代码逻辑结构搞的会更复杂。 2、操作函数和操作对象没有绑定,这个在规模开发时,很容易引起混乱。这样设计的代码,我自己到后面都觉得混乱,更别说基于我的架子让别人开发,楼主你的例子不够复杂可能感觉不到。 3、上面两个问题,也导致,代码复用率不高。 现在我的设计思想,如果是基础的数据结构,如同你这个例子中就是个线形表,我都全部独立成模版,在头文件中。 特定数据的处理不会和处理方法绑定,而是调用不同通用模块来处理,这样是尽可能的让数据和处理松耦合。而关联数据再怎么关联,处理时,也是一类整体处理的,同时一批数据再怎么复合,总可以拆成不同大部分串联处理(例如,读取、处理、写出,通过增加cache的方式可以分批分步骤完成,而不是读、处理、写 、一个完整操作周期,仅针对一个单元)。所以这类数据的整体处理落在通用模块里,通过数据和处理的紧耦合的提升效率。 你说的问题#1和文章中函数式风格一节抱怨employee_read无法和Callback兼容的问题是类似的,说到底就是因为C语言静态类型等语法特性导致了对函数式风格支持不好;同时也反向说明了为什么大多数支持函数式风格的语言会选择“动态类型”,并且支持灵活的可变个数参数等特性,都是为了辅助函数式风格的编码。 #2这一点我不太同意。C语言里虽然没有类的概念把数据和函数在语法层次上绑定在一起,但通过规范地命令提供隐喻,比如代码中,所有操作Employee对象的函数都以employee_前缀开头。而且,这些接口之间也有层级关系,符合下表描述的抽象屏障。如果你把Employee相关的声明、操作独立出来放在一个文件里,然后头文件里只放置公开的接口信息,这样就变得简洁多了。 最高层:使用API的程序 main 基于Employee的接口实现的高级操作 employee_print, employee_adjust_salary 基于最底层的C,对象Employee的最基础的操作,包括读入、释放、遍历等 employee_read, employee_free, foreach, with_open_file C语言本身提供的最底层的工具 struct Empoloyee, for, free, calloc... 例如C语言自带的操作文件的接口同样符合这样的抽象屏障:我们只需要使用fopen、fclose、fread、fwrite等一系列操作FILE对象的接口,无需关心FILE结构体里有些什么内容,表示什么意思,以及各个接口是怎么实现的。 #3的确是一个问题,而且我在文章里也可以没有提及,因为这不是这篇文章要表达的重点。它最本质的问题在于将集合的数据结构和单个对象的信息保存在同一个地方。其他语言,例如Java的java.util.*容器、C++的STL容器,都符合你的设计,将容器这个单一职责抽象出来。当然,我自己实际的工作也是这样做的。 ###### 引用来自“中山野鬼”的答案 另外,补充说一下,楼主的函数式风格,和我的函数式风格理解相差颇大。我的理解如下,所谓函数式风格,是将一批数据的若干处理,分解为正交串接的多个子步骤,每个步骤都是对整体数据的某个操作的实现。楼主的方案实质是对一个处理,可以挂接不同的操作方法。 我的理解函数式的风格在于每个独立模块处理极少的有逻辑关联的操作,可以看作针对一个数据池的原子操作。依次将数据池的数据灌入不同的独立模块,实现数据处理。当然差异的模块调用顺序和不同处理模块的组合,可以有不同的效果。 但无论如何,都是函数与数据松耦合的设计。这个和面向对象是反过来的。 我认为你说的是“责任单一原则”,让每个函数、每个模块责任都尽可能地单一,然后通过类似搭积木一样的灵活组合,完成不同的任务。就像UNIX下的命令,每个单独命令都只完成一件事情,通过管道等把这些功能单一的命令组织在一起,协作完成一个复杂的任务! 我个人认为这是一种设计思想,和源自Lambda演算的函数式风格并没有太大关系。 ###### 引用来自“杨同学”的答案 楼主的代码有很浓重的其他语言的味道 因为其他语言也能写“面向对象风格”和“函数式风格”的代码,并且看起来比C更“专业”。 ###### 引用来自“优游幻世”的答案 楼主文章不错,我看现在的C模块基本就是你所说的面向对象风格,其实就是用数据结构组织起来。 嗯,将数据和操作数据的方法集中在一起会让代码更容易维护。 就像我在六楼回复里提到的,很多C模块往往还会更进一步,把容器和对象也分离开来。这样容器能容纳各种不同的对象,对象则只保留数据本身,不关心和其他对象是以什么形式组织在一起的。 ###### 引用来自“redraiment”的答案 引用来自“中山野鬼”的答案 楼主这是节点遍历时,通过函数指针动态加载节点处理函数的设计方法。这个几年前写过,后来不这么写了。主要有以下几个问题。 1、每个节点被访问时,操作可能不一样,通用的函数指针的入口参数,要么可变参,要么多套,入口指针,都是很繁琐的事情,把代码逻辑结构搞的会更复杂。 2、操作函数和操作对象没有绑定,这个在规模开发时,很容易引起混乱。这样设计的代码,我自己到后面都觉得混乱,更别说基于我的架子让别人开发,楼主你的例子不够复杂可能感觉不到。 3、上面两个问题,也导致,代码复用率不高。 现在我的设计思想,如果是基础的数据结构,如同你这个例子中就是个线形表,我都全部独立成模版,在头文件中。 特定数据的处理不会和处理方法绑定,而是调用不同通用模块来处理,这样是尽可能的让数据和处理松耦合。而关联数据再怎么关联,处理时,也是一类整体处理的,同时一批数据再怎么复合,总可以拆成不同大部分串联处理(例如,读取、处理、写出,通过增加cache的方式可以分批分步骤完成,而不是读、处理、写 、一个完整操作周期,仅针对一个单元)。所以这类数据的整体处理落在通用模块里,通过数据和处理的紧耦合的提升效率。 你说的问题#1和文章中函数式风格一节抱怨employee_read无法和Callback兼容的问题是类似的,说到底就是因为C语言静态类型等语法特性导致了对函数式风格支持不好;同时也反向说明了为什么大多数支持函数式风格的语言会选择“动态类型”,并且支持灵活的可变个数参数等特性,都是为了辅助函数式风格的编码。 #2这一点我不太同意。C语言里虽然没有类的概念把数据和函数在语法层次上绑定在一起,但通过规范地命令提供隐喻,比如代码中,所有操作Employee对象的函数都以employee_前缀开头。而且,这些接口之间也有层级关系,符合下表描述的抽象屏障。如果你把Employee相关的声明、操作独立出来放在一个文件里,然后头文件里只放置公开的接口信息,这样就变得简洁多了。 最高层:使用API的程序 main 基于Employee的接口实现的高级操作 employee_print, employee_adjust_salary 基于最底层的C,对象Employee的最基础的操作,包括读入、释放、遍历等 employee_read, employee_free, foreach, with_open_file C语言本身提供的最底层的工具 struct Empoloyee, for, free, calloc... 例如C语言自带的操作文件的接口同样符合这样的抽象屏障:我们只需要使用fopen、fclose、fread、fwrite等一系列操作FILE对象的接口,无需关心FILE结构体里有些什么内容,表示什么意思,以及各个接口是怎么实现的。 #3的确是一个问题,而且我在文章里也可以没有提及,因为这不是这篇文章要表达的重点。它最本质的问题在于将集合的数据结构和单个对象的信息保存在同一个地方。其他语言,例如Java的java.util.*容器、C++的STL容器,都符合你的设计,将容器这个单一职责抽象出来。当然,我自己实际的工作也是这样做的。 第二个问题其实是不同设计思想的核心问题。你举的例子只能说是些简单的系统中的模块。如果是个大系统中的底层模块特别是引擎方面(会产生数据加工的),这种方法最终组合出来的系统,会比面向对象出来的类套类更复杂。说实话,还不如用面相对象实现。 面向对象,是将数据和操作,进行耦合,并且封装在类里面。这种做法是有它的好处的。这样不会导致数据和操作之间出现问题。而c如果这么写,说实话还不如用c++的类进行实现,因为类描述这些逻辑更为清晰,而且语法和编译器可以帮你做大量的事情。 而相反面向数据,是一批数据(不是一个具体数据单元),存在一批不同操作。如何分析数据之间的无关性和前后操作的无关性是重点,这两个分析清楚,那么并发计算,和分步骤计算就得以实现。并发计算不谈,分步骤计算的思想就是原子操作,或者微指令集管道设计思想。这样设计,可以令复杂的数据处理,根据流程细分到步骤,每个步骤细分到子步骤单元,而每个子步骤单元只负责处理,不负责数据的格式问题。 上面这段的设计思想和面向对象是反过来的,数据和操作松耦合。数据的特殊性导致的操作,是通过各种操作模块组合调用实现(这些操作模块可以看作上面独立的子步骤单元和外部特定数据结构无关的)。 这样做的好处是,模块的设计,可以独立进行,让外部数据格式依赖自身,而不是操作对应数据格式(面向对象是后者,成员变量类型决定了成员函数的实际操作),模块复用率高,同时是整批数据处理,只要数据流程(调用不同模块的系统设计良好),运行效率会很高。而且便于并发操作。 并发操作并不单单是一批数据,分层几组让同一个操作的多个进程处理。流水线技术的使用,一样可以实现。 这里顺带喷下hadoop。貌似hadoop的map reduce并没有在流水线方面有什么突破的思路,这块需要考虑到不同计算单元之间数据流动的费用, hadoop整天扯分布计算,根本不考虑数据整体计算周期内的相关性的问题,基本上都是推给用户自己处理,而用户应该无法控制具体计算硬件设备,最后能有好效果就扯淡了。

kun坤 2020-06-10 09:29:21 0 浏览量 回答数 0

回答

楼主这是节点遍历时,通过函数指针动态加载节点处理函数的设计方法。这个几年前写过,后来不这么写了。主要有以下几个问题。 1、每个节点被访问时,操作可能不一样,通用的函数指针的入口参数,要么可变参,要么多套,入口指针,都是很繁琐的事情,把代码逻辑结构搞的会更复杂。 2、操作函数和操作对象没有绑定,这个在规模开发时,很容易引起混乱。这样设计的代码,我自己到后面都觉得混乱,更别说基于我的架子让别人开发,楼主你的例子不够复杂可能感觉不到。 3、上面两个问题,也导致,代码复用率不高。 现在我的设计思想,如果是基础的数据结构,如同你这个例子中就是个线形表,我都全部独立成模版,在头文件中。 特定数据的处理不会和处理方法绑定,而是调用不同通用模块来处理,这样是尽可能的让数据和处理松耦合。而关联数据再怎么关联,处理时,也是一类整体处理的,同时一批数据再怎么复合,总可以拆成不同大部分串联处理(例如,读取、处理、写出,通过增加cache的方式可以分批分步骤完成,而不是读、处理、写 、一个完整操作周期,仅针对一个单元)。所以这类数据的整体处理落在通用模块里,通过数据和处理的紧耦合的提升效率。 ###### 另外,补充说一下,楼主的函数式风格,和我的函数式风格理解相差颇大。我的理解如下,所谓函数式风格,是将一批数据的若干处理,分解为正交串接的多个子步骤,每个步骤都是对整体数据的某个操作的实现。楼主的方案实质是对一个处理,可以挂接不同的操作方法。 我的理解函数式的风格在于每个独立模块处理极少的有逻辑关联的操作,可以看作针对一个数据池的原子操作。依次将数据池的数据灌入不同的独立模块,实现数据处理。当然差异的模块调用顺序和不同处理模块的组合,可以有不同的效果。 但无论如何,都是函数与数据松耦合的设计。这个和面向对象是反过来的。 ######相互嵌套耦合,牵一发动全身######楼主的代码有很浓重的其他语言的味道######楼主文章不错,我看现在的C模块基本就是你所说的面向对象风格,其实就是用数据结构组织起来。###### 引用来自“中山野鬼”的答案 楼主这是节点遍历时,通过函数指针动态加载节点处理函数的设计方法。这个几年前写过,后来不这么写了。主要有以下几个问题。 1、每个节点被访问时,操作可能不一样,通用的函数指针的入口参数,要么可变参,要么多套,入口指针,都是很繁琐的事情,把代码逻辑结构搞的会更复杂。 2、操作函数和操作对象没有绑定,这个在规模开发时,很容易引起混乱。这样设计的代码,我自己到后面都觉得混乱,更别说基于我的架子让别人开发,楼主你的例子不够复杂可能感觉不到。 3、上面两个问题,也导致,代码复用率不高。 现在我的设计思想,如果是基础的数据结构,如同你这个例子中就是个线形表,我都全部独立成模版,在头文件中。 特定数据的处理不会和处理方法绑定,而是调用不同通用模块来处理,这样是尽可能的让数据和处理松耦合。而关联数据再怎么关联,处理时,也是一类整体处理的,同时一批数据再怎么复合,总可以拆成不同大部分串联处理(例如,读取、处理、写出,通过增加cache的方式可以分批分步骤完成,而不是读、处理、写 、一个完整操作周期,仅针对一个单元)。所以这类数据的整体处理落在通用模块里,通过数据和处理的紧耦合的提升效率。 你说的问题#1和文章中函数式风格一节抱怨employee_read无法和Callback兼容的问题是类似的,说到底就是因为C语言静态类型等语法特性导致了对函数式风格支持不好;同时也反向说明了为什么大多数支持函数式风格的语言会选择“动态类型”,并且支持灵活的可变个数参数等特性,都是为了辅助函数式风格的编码。 #2这一点我不太同意。C语言里虽然没有类的概念把数据和函数在语法层次上绑定在一起,但通过规范地命令提供隐喻,比如代码中,所有操作Employee对象的函数都以employee_前缀开头。而且,这些接口之间也有层级关系,符合下表描述的抽象屏障。如果你把Employee相关的声明、操作独立出来放在一个文件里,然后头文件里只放置公开的接口信息,这样就变得简洁多了。 最高层:使用API的程序 main 基于Employee的接口实现的高级操作 employee_print, employee_adjust_salary 基于最底层的C,对象Employee的最基础的操作,包括读入、释放、遍历等 employee_read, employee_free, foreach, with_open_file C语言本身提供的最底层的工具 struct Empoloyee, for, free, calloc... 例如C语言自带的操作文件的接口同样符合这样的抽象屏障:我们只需要使用fopen、fclose、fread、fwrite等一系列操作FILE对象的接口,无需关心FILE结构体里有些什么内容,表示什么意思,以及各个接口是怎么实现的。 #3的确是一个问题,而且我在文章里也可以没有提及,因为这不是这篇文章要表达的重点。它最本质的问题在于将集合的数据结构和单个对象的信息保存在同一个地方。其他语言,例如Java的java.util.*容器、C++的STL容器,都符合你的设计,将容器这个单一职责抽象出来。当然,我自己实际的工作也是这样做的。 ###### 引用来自“中山野鬼”的答案 另外,补充说一下,楼主的函数式风格,和我的函数式风格理解相差颇大。我的理解如下,所谓函数式风格,是将一批数据的若干处理,分解为正交串接的多个子步骤,每个步骤都是对整体数据的某个操作的实现。楼主的方案实质是对一个处理,可以挂接不同的操作方法。 我的理解函数式的风格在于每个独立模块处理极少的有逻辑关联的操作,可以看作针对一个数据池的原子操作。依次将数据池的数据灌入不同的独立模块,实现数据处理。当然差异的模块调用顺序和不同处理模块的组合,可以有不同的效果。 但无论如何,都是函数与数据松耦合的设计。这个和面向对象是反过来的。 我认为你说的是“责任单一原则”,让每个函数、每个模块责任都尽可能地单一,然后通过类似搭积木一样的灵活组合,完成不同的任务。就像UNIX下的命令,每个单独命令都只完成一件事情,通过管道等把这些功能单一的命令组织在一起,协作完成一个复杂的任务! 我个人认为这是一种设计思想,和源自Lambda演算的函数式风格并没有太大关系。 ###### 引用来自“杨同学”的答案 楼主的代码有很浓重的其他语言的味道 因为其他语言也能写“面向对象风格”和“函数式风格”的代码,并且看起来比C更“专业”。 ###### 引用来自“优游幻世”的答案 楼主文章不错,我看现在的C模块基本就是你所说的面向对象风格,其实就是用数据结构组织起来。 嗯,将数据和操作数据的方法集中在一起会让代码更容易维护。 就像我在六楼回复里提到的,很多C模块往往还会更进一步,把容器和对象也分离开来。这样容器能容纳各种不同的对象,对象则只保留数据本身,不关心和其他对象是以什么形式组织在一起的。 ###### 引用来自“redraiment”的答案 引用来自“中山野鬼”的答案 楼主这是节点遍历时,通过函数指针动态加载节点处理函数的设计方法。这个几年前写过,后来不这么写了。主要有以下几个问题。 1、每个节点被访问时,操作可能不一样,通用的函数指针的入口参数,要么可变参,要么多套,入口指针,都是很繁琐的事情,把代码逻辑结构搞的会更复杂。 2、操作函数和操作对象没有绑定,这个在规模开发时,很容易引起混乱。这样设计的代码,我自己到后面都觉得混乱,更别说基于我的架子让别人开发,楼主你的例子不够复杂可能感觉不到。 3、上面两个问题,也导致,代码复用率不高。 现在我的设计思想,如果是基础的数据结构,如同你这个例子中就是个线形表,我都全部独立成模版,在头文件中。 特定数据的处理不会和处理方法绑定,而是调用不同通用模块来处理,这样是尽可能的让数据和处理松耦合。而关联数据再怎么关联,处理时,也是一类整体处理的,同时一批数据再怎么复合,总可以拆成不同大部分串联处理(例如,读取、处理、写出,通过增加cache的方式可以分批分步骤完成,而不是读、处理、写 、一个完整操作周期,仅针对一个单元)。所以这类数据的整体处理落在通用模块里,通过数据和处理的紧耦合的提升效率。 你说的问题#1和文章中函数式风格一节抱怨employee_read无法和Callback兼容的问题是类似的,说到底就是因为C语言静态类型等语法特性导致了对函数式风格支持不好;同时也反向说明了为什么大多数支持函数式风格的语言会选择“动态类型”,并且支持灵活的可变个数参数等特性,都是为了辅助函数式风格的编码。 #2这一点我不太同意。C语言里虽然没有类的概念把数据和函数在语法层次上绑定在一起,但通过规范地命令提供隐喻,比如代码中,所有操作Employee对象的函数都以employee_前缀开头。而且,这些接口之间也有层级关系,符合下表描述的抽象屏障。如果你把Employee相关的声明、操作独立出来放在一个文件里,然后头文件里只放置公开的接口信息,这样就变得简洁多了。 最高层:使用API的程序 main 基于Employee的接口实现的高级操作 employee_print, employee_adjust_salary 基于最底层的C,对象Employee的最基础的操作,包括读入、释放、遍历等 employee_read, employee_free, foreach, with_open_file C语言本身提供的最底层的工具 struct Empoloyee, for, free, calloc... 例如C语言自带的操作文件的接口同样符合这样的抽象屏障:我们只需要使用fopen、fclose、fread、fwrite等一系列操作FILE对象的接口,无需关心FILE结构体里有些什么内容,表示什么意思,以及各个接口是怎么实现的。 #3的确是一个问题,而且我在文章里也可以没有提及,因为这不是这篇文章要表达的重点。它最本质的问题在于将集合的数据结构和单个对象的信息保存在同一个地方。其他语言,例如Java的java.util.*容器、C++的STL容器,都符合你的设计,将容器这个单一职责抽象出来。当然,我自己实际的工作也是这样做的。 第二个问题其实是不同设计思想的核心问题。你举的例子只能说是些简单的系统中的模块。如果是个大系统中的底层模块特别是引擎方面(会产生数据加工的),这种方法最终组合出来的系统,会比面向对象出来的类套类更复杂。说实话,还不如用面相对象实现。 面向对象,是将数据和操作,进行耦合,并且封装在类里面。这种做法是有它的好处的。这样不会导致数据和操作之间出现问题。而c如果这么写,说实话还不如用c++的类进行实现,因为类描述这些逻辑更为清晰,而且语法和编译器可以帮你做大量的事情。 而相反面向数据,是一批数据(不是一个具体数据单元),存在一批不同操作。如何分析数据之间的无关性和前后操作的无关性是重点,这两个分析清楚,那么并发计算,和分步骤计算就得以实现。并发计算不谈,分步骤计算的思想就是原子操作,或者微指令集管道设计思想。这样设计,可以令复杂的数据处理,根据流程细分到步骤,每个步骤细分到子步骤单元,而每个子步骤单元只负责处理,不负责数据的格式问题。 上面这段的设计思想和面向对象是反过来的,数据和操作松耦合。数据的特殊性导致的操作,是通过各种操作模块组合调用实现(这些操作模块可以看作上面独立的子步骤单元和外部特定数据结构无关的)。 这样做的好处是,模块的设计,可以独立进行,让外部数据格式依赖自身,而不是操作对应数据格式(面向对象是后者,成员变量类型决定了成员函数的实际操作),模块复用率高,同时是整批数据处理,只要数据流程(调用不同模块的系统设计良好),运行效率会很高。而且便于并发操作。 并发操作并不单单是一批数据,分层几组让同一个操作的多个进程处理。流水线技术的使用,一样可以实现。 这里顺带喷下hadoop。貌似hadoop的map reduce并没有在流水线方面有什么突破的思路,这块需要考虑到不同计算单元之间数据流动的费用, hadoop整天扯分布计算,根本不考虑数据整体计算周期内的相关性的问题,基本上都是推给用户自己处理,而用户应该无法控制具体计算硬件设备,最后能有好效果就扯淡了。

kun坤 2020-06-09 22:08:58 0 浏览量 回答数 0

问题

方法追踪有哪几种?

猫饭先生 2019-12-01 21:03:55 875 浏览量 回答数 0

回答

说到区块链,我们必然会谈及它的共识机制。不了解区块链的共识机制,就无法理解区块链的真正意义。那么,今日份的区块链的共识机制了解一下? 共识机制是什么? 什么是共识?直取它的字面意思,就是"共同的认识". 人与人是不同的,这种不同不仅体现在身材、长相、能力,更体现在文化、观点、想法、利益诉求等等方面。 共识,简而言之,就是一个群体的成员在某一方面达成的一致意见。 我们了解到,信任是社会运转中的一大痛点,银行有自己的信用体系,过去的金融体系服务于只服务于极少的企业家,因为建立信用体系耗资巨大。后来支付宝有了芝麻信用,信用已经关系到生活的很多方面,信用卡额度、花呗额度,芝麻信用高出国还可以免签。我们正享受着信用给我们带来的便捷。 区块链本质是去中心化,去中心化的核心是共识机制,区块链上的共识机制主要解决由谁来构造区块,以及如何维护区块链统一的问题。 区块链共识机制的目标是使所有的诚实节点保存一致的区块链视图,同时满足两个性质: 1)一致性:所有诚实节点保存的区块链的前缀部分完全相同。 2)有效性:由某诚实节点发布的信息终将被其他所有诚实节点记录在自己的区块链中。 区块链的自信任主要体现于分布于区块链中的用户无须信任交易的另一方,也无须信任一个中心化的机构,只需要信任区块链协议下的软件系统即可实现交易。 共识机制是什么?PoW 、PoS 、DPOW都是什么意思? 共识机制的必要性? 分布式系统中,多个主机通过异步通信方式组成网络集群。在这样的一个异步系统中,需要主机之间进行状态复制,以保证每个主机达成一致的状态共识。错误信息可能出现在异步系统内并不断传播,因此需要在默认不可靠的异步网络中定义容错协议,以确保各主机达成安全可靠的状态共识,这就是共识机制诞生的必要性。 这种自信任的前提是区块链的共识机制(consensus),即在一个互不信任的市场中,要想使各节点达成一致的充分必要条件是每个节点出于对自身利益最大化的考虑,都会自发、诚实地遵守协议中预先设定的规则,判断每一笔记录的真实性,最终将判断为真的记录记入区块链之中。attachments-2018-08-9yY7VRHa5b738e3d96021.jpg 换句话说,如果各节点具有各自独立的利益并互相竞争,则这些节点几乎不可能合谋欺骗你,而当节点们在网络中拥有公共信誉时,这一点体现得尤为明显。区块链技术正是运用一套基于共识的数学算法,在机器之间建立"信任"网络,从而通过技术背书而非中心化信用机构来进行全新的信用创造。 当今区块链的几种共识机制介绍 区块链上的共识机制有多种,但任何一种都不是完美无缺,或者说适用于所有应用场景的。 PoW 工作量证明 整个系统中每个节点为整个系统提供计算能力(简称算力),通过一个竞争机制,让计算工作完成最出色的节点获得系统的奖励,即完成新生成货币的分配,简单理解就是多劳多得,bitcoin、LTC等货币型区块链就应用POW机制。 优点 完全去中心化节点自由进出,算法简单,容易实现破坏系统花费的成本巨大,只要网络破坏者的算力不超过网络总算力的50%,网络的交易状态就能达成一致 缺点 浪费能源,这是最大的缺点区块的确认时间难以缩短,如bitcoin每秒只能做7笔交易,不适合商业应用新的区块链必须找到一种不同的散列算法,否则就会面临bitcoin的算力攻击对节点的性能网络环境要求高容易产生分叉,需要等待多个确认无法达成最终一致性 PoS 权益证明 也称股权证明,类似于你把财产存在银行,这种模式会根据你持有加密货币的数量和时间,分配给你相应的利息。 优点 对节点性能要求低,达成共识时间短 缺点 没有最终一致性,需要检查点机制来弥补最终性 DPOW 委托股权证明 DPOW是 PoS 的进化方案,在常规 PoW和 PoS 中,任何一个新加入的区块,都需要被整个网络所有节点做确认,非常影响效率。 DPoS则类似于现代董事会的投票机制,通过选举代表来进行投票和决策。被选举出的n个记账节点来做新区块的创建、验证、签名和相互监督,这样就极大地减少了区块创建和确认所需要消耗的时间和算力成本。 优点 大幅缩小参与验证和记账节点的数量,可以达到秒级的共识验证 缺点 牺牲了去中心化的概念,不适合公有链 PBFT 实用拜占庭容错 实用拜占庭容错机制是一种采用"许可投票、少数服从多数"来选举领导者并进行记账的共识机制,该共识机制允许拜占庭容错,允许强监督节点参与,具备权限分级能力,性能更高,耗能更低,而且每轮记账都会由全网节点共同选举领导者,允许33%的节点作恶,容错率为33%.实用拜占庭容错特别适合联盟链的应用场景。 优点 会背离中心化,加密货币的存在及奖励机制会产生马太效应,让社区中的穷者更穷,富者更富共识效率高,可实现高频交易 缺点 当系统只剩下33%的节点运行时,系统会停止运行 dBFT 授权拜占庭容错 这种机制是用权益来选出记账人,然后记账人之间通过拜占庭容错算法达成共识。授权拜占庭容错机制最核心的一点,就是最大限度地确保系统的最终性,使区块链能够适用于真正的金融应用场景。 优点 专业化的记账人可以容忍任何类型的错误记账由多人协同完成,每一个区块都有最终性,不会分叉算法的可靠性有严格的数学证明 缺点 当三分之一或以上记账人停止工作后,系统将无法提供服务当三分之一或以上记账人联合作恶,可能会使系统出现分叉 Pool 验证池 基于传统的分布式一致性技术,加上数据验证机制。 优点 不需要加密货币也可以工作,在成熟的分布式一致性算法(Pasox、Raft)基础上,实现秒级共识验证。 缺点 去中心化程度不如bitcoin,更适合多方参与的多中心商业模式。 Paxos 这是一种传统的分布式一致性算法,是一种基于选举领导者的共识机制。领导者节点拥有绝对权限,并允许强监督节点参与,其性能高,资源消耗低。所有节点一般有线下准入机制,但选举过程中不允许有作恶节点,不具备容错性。 Paxos算法中将节点分为三种类型: proposer:提出一个提案,等待大家批准为结案。往往是客户端担任该角色 acceptor:负责对提案进行投票。往往是服务端担任该角色 learner:被告知结案结果,并与之统一,不参与投票过程。可能为客户端或服务端 Paxos 能保证在超过50%的正常节点存在时,系统能达成共识。 瑞波共识机制 瑞波共识算法使一组节点能够基于特殊节点列表形成共识,初始特殊节点列表就像一个俱乐部,要接纳一个新成员,必须由该俱乐部51%的会员投票通过。共识遵循这些核心成员的"51%权利",外部人员则没有影响力。由于该俱乐部由中心化开始,它将一直是中心化的,而如果它开始腐化,股东们什么也做不了。与bitcoin及Peercoin一样,瑞波系统将股东们与其投票权隔开,因此,它比其他系统更中心化。 Peercoin Peercoin(点点币,PPC),混合了POW工作量证明及POS权益证明方式,其中POW主要用于发行货币,未来预计随着挖矿难度上升,产量降低,系统安全主要由POS维护。 在区块链网络中,由于应用场景的不同,所设计的目标各异,不同的区块链系统采用了不同的共识算法。每种共识算法都不是完美的,都有其优点和局限性。 区块链解决了在不可信信道上传输可信信息、价值转移的问题,而共识机制解决了区块链如何分布式场景下达成一致性的问题。 虽然区块链目前还处于发展的早期,行业发展还面临着一些阻碍,但社会已经足够多地认识到区块链的价值,区块链发展的脚步绝不会停滞不前,行业发展也定会找到突破阻碍的方法。

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

问题

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

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

回答

硬盘读写速度现在怎么都达不到千兆, ssd读也达不到。(特殊设备除外,貌似看到新闻说有实验室的产品读写速度可以过G) 不过可以采取,写入缓冲的方式,数据先保存在内存,再写入到硬盘,不过缺点怕掉电。 读的话,采取分布式的读,可以达到很高的吞吐量。 网络传输的话,问题在于怎么保证传输稳定和不出错######这对内存的要求很高啊,而且还不能耽误其他程序对内存的使用,这个内存我觉得应该很大吧###### 引用来自“十一文”的答案 硬盘读写速度现在怎么都达不到千兆, ssd读也达不到。(特殊设备除外,貌似看到新闻说有实验室的产品读写速度可以过G) 不过可以采取,写入缓冲的方式,数据先保存在内存,再写入到硬盘,不过缺点怕掉电。 读的话,采取分布式的读,可以达到很高的吞吐量。 网络传输的话,问题在于怎么保证传输稳定和不出错 这么说来, 在顺序存取方面 , 网络传输速度相对与硬盘 io 速度还是有优势的,不知道这么理解是否成立。 因为机放内部设备间千兆网卡很常见,传输速度相当快,并且成本相对硬盘少许多。 ######看贴不跟帖,帖子要沉了。需要顶。 无论对错。发表点个人观点也好。不能让它沉。 ###### "千兆网卡很常见", 這裡 “ 千兆” 是指1000M bits, 大概也就100M Bytes。 Intel SSD 520 Sequential Read 已经可高达550M Bytes per second, 顺序写可高达275M Bytes per second.######回复 @十一文 : 现在的HBA卡4G的已经很通用了,好像12G的都出来了,一般服务器都有好几个接口的,再来个负载均很啥的,网络传输不是问题######汗 查了哈 还真是这样。擦我out了!######网络允许帮定双网卡。所以,网络传输可以更快点,相对来说,速度提升技术性难度小点。 磁盘阵列是否回更好? 光线通讯用的网卡是否会更快点。 ###### 顺便提点应用。 是这样的, 排序在信息处理方面很常见。 无论用什么算法。都是在一个相对平等的环境中。 现实中应用,比如1g内容的排序和1T内容排序难点还是数据交换上。 1g可以全部加载进内存玩。1T就要涉及到信息交换了。如果一个系统界面,把存储信息部分扩展到近乎无限空间大小。 就好比内存数据交换比磁盘数据交换要快许多。 比如1T大小数据做排序。 只要一个设备顺序读取数据,按照开头部分把数据通过网络分发给N 台机器,处理除了开头部分数据,后面的数据排序。这样就可以N多设备协同工作。效率达到 1+1 > 2 的目的。 否则如果是1台设备需要 加载数据,排序, 临时存储, 加载另外数据,处理,临时存储,加载.... 汇总分结果,获得总结果。 1台设备这么处理,做了很多重复劳动。如果网络够快 多台设备 避免了重复加载。 达到 1+1>2######回复 @十一文 : 差不多的意思。######hadoop是把数据分成分成多个部分,每部分各自处理结果,然后汇总处理。即把你的1t的数据分成n份,然后每份分发给不同机器处理。然后汇总结果。不知道适用你的场景不?######貌似这中数据分析,现在流行用hadoop。楼主可以调研哈######这么说不好理解。形象一点说一下:假设有1000个数据样本,每个样本里有1T条数据内容。 一知每个样本内数据条目重复率为0.001%.目的,找出这1000个样本内,每个样本中重复的样本。并统计所有样本中重复的次数。 这个如果算复杂运算,不如说是大数据处理。 假设 每个设备 一次能加载1G条数据。######硬盘技术感觉好多年都在原地踏步没有质的飞跃啊###### @johnzhang68 毕竟转速有影响######磁性硬盘在容量方面还是有明显的飞跃。速度方面提高得慢一些。######或许未来,存储虚拟化是条路子。  数据处理和数据交互关系密切。 以数据处理为目的,建立多系统群集方式在处理上或许会比高计算系统群集更有优势。 ######又没落了。顶起 ######没有试过,关注一下######再看了哈貌似你真的很需要hadoop

kun坤 2020-06-07 22:18:40 0 浏览量 回答数 0

问题

【精品回答】移动推送

montos 2020-04-09 09:57:11 14 浏览量 回答数 1

问题

Android目录结构(详解):报错

kun坤 2020-06-07 21:39:11 0 浏览量 回答数 1

问题

DIYRubyCPU分析——PartI

sunny夏筱 2019-12-01 21:55:37 7137 浏览量 回答数 0

问题

如何彻底消灭Bug?

问问小秘 2020-06-29 11:07:58 13 浏览量 回答数 2

问题

大数据被用来犯罪怎么办

游客ftkex2f22paya 2019-12-01 19:34:14 2 浏览量 回答数 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

问题

【每日一题】SQL 知识大测验 | 持续更新

茶什i 2019-12-01 22:03:05 20900 浏览量 回答数 37

问题

十大经典排序算法最强总结(内含代码实现)

游客pklijor6gytpx 2020-01-09 14:44:55 1240 浏览量 回答数 2

回答

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