轻量级数据库中间件利器Sharding-JDBC深度解析(有彩蛋)

讲师介绍



640.png?wxfrom=5&wx_lazy=1

张亮

当当架构部总监


  • 负责分布式中间件和私有云平台建设

  • 目前主导开源项目:Elastic-Job及Sharding-JDBC 



主题简介:

1、关系型数据库中间件核心功能介绍

2、Sharding-JDBC架构及内核解析

3、Sharding-JDBC未来展望


一、关系型数据库中间件核心功能介绍


关系型数据库凭借灵活查询的SQL和稳定的存储及事务引擎,一直以来是业务存储领域的首选。而在规模越来越大的互联网年代,单一的关系型数据库却已难满足需求。开发人员不愿放弃SQL查询的灵活度及对之前代码的兼容性,而又无法承受数据量过大时所带来的性能瓶颈。因此NoSQL和NewSQL分别产生,而NoSQL的不兼容性和NewSQL的不成熟,也使得关系型数据库仍将长期存在下去。所以,继续使用关系型数据库,并能解决互联网规模所带来的冲击,数据库中间层这个既不保守,也不激进的平衡方案被大多数互联网公司所接受。关系型数据库中间件采用单体向分布式透明转化的方案来解决数据量和访问量巨大这两个互联网场景的核心问题。


关系型数据库在大于自身数据量阀值的情况下,性能会急剧下降。在面对互联网海量数据情况时,所有数据都存于单表,显然会轻易超过数据库表可承受的范围。这个单表可承受的数据量阀值,需根据数据库和并发量的差异,通过实际测试获得。


通过分库和分表拆分数据来使得各个表的数据量保持在阀值以下。拆分方式为垂直拆分和水平拆分。垂直拆分是根据业务将单库(表)拆分为多库(表)。如:将常用的字段和不常用的字段拆分至不同的库(表)中。但垂直拆分需要对架构和设计进行调整,往往来不及应对互联网业务需求的快速变化,而且也并不能真正的解决单点瓶颈。水平拆分则是根据分片算法将一个库(表)拆分为多个库(表)。如:根据ID的最后一位以10取余,尾数是0的放入0库(表),尾数是1的放入1库(表)。水平拆分从理论上突破了单机数据量处理的瓶颈,并且扩展相对自由,是分库分表的标准解决方案。


分库和读写分离疏导流量是应对高访问量的常见手段。分表虽然可以解决海量数据导致的性能问题,但无法解决过多请求访问同一数据库,导致其响应变慢的问题。所以水平拆分通常要采取分库的方式,一并解决数据量和访问量巨大的问题。读写分离是另一个疏导流量的办法,但读写数据间的延迟是架构设计时需要考虑的问题。

 

虽然分库可以解决上述问题,但分布式架构在获得了收益的同时,也带来了新的问题。


跨库事务是分布式数据库要面对的棘手事情。合理采用分表,可以在降低单表数据量的情况下,尽量使用本地事务,善于使用同库不同表可有效避免分布式事务带来的麻烦。在不能避免跨库事务的场景,有些业务仍然需要保持事务的一致性。而基于XA的分布式事务由于性能低下,无法被互联网公司所采纳,它们大多采用最终一致性的柔性事务代替分布式事务。


因此,最佳实践是合理地配合使用分库+分表。在实现上,分表的难度远大于分库,它需要对SQL解析,并且对表名改写,而分库则不需要。

 

另一个分布式衍生的问题是主键生成。它必须可以保证分布式唯一。分布式主键的生成方式分为中心化和去中心化两大类。中心化可以继续采用数据库生成自增主键的方式,为每个不同的分库设置不同的初始值,并将步长设置为分片的个数即可,这种方式对分片个数有依赖,一旦再次水平扩展,原有的分布式主键不易迁移。还有一种中心化生成分布式主键的方式,即采用Redis在内存中生成自增序列,但此种方式新增加了一个外部组件的依赖,一旦Redis不可用,则整个数据库将无法在插入,可用性会大大下降,另外Redis的单点问题也需要解决,部署复杂度较高。去中心化方式无需额外部署,可扩展性也很好,因此更推荐使用。UUID是去中心化生成分布式主键较为常见的一种方式,但它的主键很长,而且无序,通过主键排序时对数据库的性能影响较大,不建议使用。目前较为完美的方案是使用snowflake算法生成分布式唯一和基本有序的主键。

 

综上所述,分布式数据库中间件的功能模块非常清晰,有其存在的必要和市场价值。但与旺盛的需求形成鲜明对比,成熟的关系型数据库中间件凤毛麟角。主要原因是各公司均开发各自的中间件,没有形成统一的标准及规范,也没有动力从公司现有的系统中解耦并独立开源。当当同样自研,并决定将其开源,命名为Sharding-JDBC。从2016年开源至今,已发布了15个版本,其中包含5个里程碑版本升级。在经历了整体架构的数次精炼以及稳定性打磨后,如今它已积累了足够的底蕴,相信可以成为开发者选择技术组件时的一个参考。


二、Sharding-JDBC架构及内核解析


Sharding-JDBC完整的实现了分库分表,读写分离和分布式主键功能,并初步实现了柔性事务。


它直接实现JDBC接口,旧代码迁移成本几乎为零,可适用于任何基于Java的ORM框架,如:JPA、Hibernate、Mybatis、SpringJDBC Template等;理论上可以支持所有实现JDBC协议的数据库,但由于各种数据库的SQL方言差别较大,每种SQL都需要独立的解析器,Sharding-JDBC目前仅支持MySQL、PostgreSQL、Oracle和SQL Server这4种最主流的数据库。由于柔性事务与JDBC没有直接关系,因此正在考虑将它拆分为一个独立的项目。


Sharding-JDBC与基于MySQL等数据库协议实现的Proxy中间层在部署架构上差别很大,但在代码的核心逻辑上差别并不大。Sharding-JDBC作为lib库,是与业务代码部署在一起的,而基于Proxy的中间层则是架在数据库的前方,与应用代码在部署上隔绝。无论使用哪种架构,它们的核心逻辑均极为相似,都会分为分片规则配置、SQL解析、SQL路由、SQL改写、SQL执行以及结果归并模块,区别仅在于协议实现层的不同(JDBC或数据库协议)。


Sharding-JDBC架构图如下:

640?wxfrom=5&wx_lazy=1


左边部分是部署架构图,右边部分则是核心逻辑架构图。

 

使用Sharding-JDBC,性能是大家最关心的问题。


在数据量一致的情况下,使用Sharding-JDBC和原生JDBC的性能测试报告如下:

  • 查询操作:Sharding-JDBC的TPS为JDBC的TPS的99.8%。

  • 插入操作:Sharding-JDBC的TPS为JDBC的TPS的90.2%。

  • 更新操作:Sharding-JDBC的TPS为JDBC的TPS的93.1%。

  • 可以看到,Sharding-JDBC在查询中的性能损失非常低,插入和更新略高。

 

将单表的数据拆分为二,放入两个表中,使用Sharding-JDBC和原生JDBC的性能测试报告如下:

  • 查询操作:TPS双库比单库可以增加大约94%的性能。

  • 插入操作:TPS双库比单库可以增加大约60%的性能。

  • 更新操作:TPS双库比单库可以增加大约89%的性能。

  • 结果表明,Sharding-JDBC可有效利用水平扩展大幅度提升性能。

 

下面我将按照模块深度剖析Sharding-JDBC的详细功能和主要实现,请大家和我一起探索与评估它的水有多深。


分片规则配置


Sharding-JDBC的分片策略配置是自定义的,因此可以通过编程的方式最大限度的灵活调整。它并不仅支持=运算符分片,可支持BETWEEN和IN的运算符分片,支持将一条逻辑SQL最终散落至多个数据节点。同时支持多分片键,例如:根据用户ID分库,订单ID分表这种分库分表结合的分片策略;或根据年分库,月份+用户区域ID分表这样的多片键分片。


通过编程的方式定制分片规则虽然灵活,但配置起来略显繁琐。因此Sharding-JDBC又提供了Inline表达式编写分片策略的方式,用于配置集中化,以避免配置散落在配置文件和代码中的情况。此外,它还提供了定制化的Spring命名空间和YAML进一步简化配置。


JDBC规范重写


Sharding-JDBC对JDBC规范的重写思路是针对DataSource、Connection、Statement、PreparedStatement和ResultSet这5个核心接口封装,将多个实现类集合纳入Sharding-JDBC实现类管理。分布式主键也属于JDBC协议的一部分。


Sharding-JDBC尽量最大化实现JDBC协议,但分布式毕竟与原生JDBC不同,所以目前仍有未实现的接口,包括游标,存储过程、SavePoint以及向前遍历和修改ResultSet等不太常用的功能。此外,为了保证兼容性,并未实现JDBC 4.1及其后发布的接口(如:DBCP 1.x版本不支持JDBC 4.1)。


SQL解析


SQL解析作为分库分表类产品的核心,性能和兼容性是最重要的衡量指标。目前常见的SQL解析器主要有fdb,jsqlparser和Druid。Sharding-JDBC1.4.x之前的版本使用Druid作为SQL解析器,经实际测试,它的性能远超其它解析器。

从1.5.x版本开始,Sharding-JDBC采用完全自研的SQL解析引擎。由于目的不同,它并不需要将SQL转为AST语法树,也无需通过Visitor的方式二次遍历。它采用对SQL“半理解”的方式,仅提炼分片需要关注的上下文,因此SQL解析的性能和容错性得到了进一步的提高。


SQL解析模块由Lexer和Parser两个模块组成。Lexer用于将SQL拆解为Token,并将其归类为关键词,表达式,字面量和操作符。Parser则用于理解SQL和提炼分片上下文,并标记可能需要改写的位置。分片上下文包含SELECTItems、表信息、分片条件、自增主键信息、排序信息、分组信息和Limit信息。一次解析过程是不可逆的,一个个Token的依次解析,因此解析性能很高。由于各种数据库的SQL差异很大,因此在解析模块对每种数据库提供方言的支持。


Sharding-JDBC支持各种连接、聚合、排序、分组以及分页的解析,并且可以有限度的支持子查询。


SQL路由


SQL路由是根据分片规则配置以及解析上下文中的分片条件,将SQL定位至真正的数据源。它又分为直接路由、简单路由和笛卡尔积路由。


满足直接路由的条件比较苛刻,如果通过Hint(通过HintAPI直接指定路由至库表)方式分片,且仅分库,则无需SQL解析和结果归并。因此它的SQL兼容性最好,可以执行包括子查询、OR、UNION等复杂情况的任意SQL。


简单路由是Sharding-JDBC最推荐使用的分片方式,它是指不包含JOIN或仅包含Binding表JOIN的SQL。Binding表是指使用同样的分片键和分片规则的一组表,也就是说任何情况下,Binding表的分片结果应与主表一致。例如:order表和order_item表,都根据order_id分片,结果应是order_1与order_item_1成对出现。这样的关联查询和单表查询复杂度和性能相当。如果分片条件不是等于,而是BETWEEN或IN,则路由结果不一定落入单库(表),因此一条逻辑SQL最终可能拆分为多条SQL语句。


笛卡尔积查询最为复杂,因为无法根据Binding关系定位分片规则的一致性,所以非Binding表的关联查询需要拆解为笛卡尔积组合执行。查询性能较低,而且数据库连接数较高,需谨慎使用。


SQL改写


SQL改写模块的用途是将逻辑SQL改写为可以分布式执行的SQL。在Sharding-JDBC 1.5.x版本,SQL改写进行了调整和大量优化。1.4.x及之前版本,SQL改写是在SQL路由之前完成的,在1.5.x中调整为SQL路由之后,因为SQL改写可以根据路由至单库表还是多库表而进行进一步优化。SQL改写分为正确性改写和优化改写两部分。


正确性改写包括将分表的逻辑表名称替换为真实表名称,修正分页信息和增加补列。举两个例子:


  • AVG计算。分布式场景,以avg1 + avg2 + avg3 / 3计算平均值并不正确,需要改写为 (sum1 + sum2 + sum3)  / (count1 + count2 + count3)。这就需要将包含AVG的SQL改写为SUM和COUNT,并在结果归并时重新计算平均值。


  • 分页。假设每10条数据为一页,取第2页数据。在分片环境下获取LIMIT 10, 10,归并之后再根据排序条件取出前10条数据是不正确的结果。正确的做法是将分条件改写为LIMIT 0, 20,取出所有前两页数据,再结合排序条件计算出正确的数据。因此越是获取靠后数据,分页的效率就会越低。有很多方法可避免使用LIMIT进行分页。比如构建记录行记录数和行偏移量的二级索引,或使用上次分页数据结尾ID作为下次查询条件的分页方式。


优化改写是1.5.x重点提升的部分,实现的功能比较零散,这里同样举两个例子:


  • 单路由拒绝改写。这是将SQL改写挪到SQL路由之后的原因。当获得路由结果之后,单路由的情况因为不涉及到结果归并,因此分页、补列等改写都无需存在。尤其是分页,无需将数据从第1条开始取,节省了网络带宽。


  • 流式归并改写。一会讲到归并时会说,这里先提一句,将仅包含GROUPBY的SQL改写为GROUPBY + ORDERBY。


SQL执行


路由至真实数据源后,Sharding -JDBC将采用多线程并发执行SQL。它用3种执行引擎分别对应处理Statement,PreparedStatement和AddBatchPreparedStatement。Sharding-JDBC线程池放在一个名为ShardingContext的对象中,它的生命周期同ShardingDataSource保持一致。如果一个应用中创建了多个Sharding-JDBC的数据源,它们将持有不同的线程池。



结果归并



Sharding-JDBC支持的结果归并从功能上分为遍历、排序、分组和分页4种类型,它们是组合而非互斥的关系。从结构划分,可分为流式归并、内存归并和装饰者归并。流式归并和内存归并是互斥的,装饰者归并可以在流式归并和内存归并之上做进一步的处理。


流式归并是将数据游标与结果集的游标保持一致,顺序的从结果集中一条条的获取正确的数据。遍历和排序都是流式归并,分组比较复杂,分为流式分组和内存分组。内存归并则是需要将结果集的所有数据都遍历并存储在内存中,再通过内存归并后,将内存中的数据伪装成结果集返回。


遍历类型最为简单,只需将多结果集组成链表,遍历完成当前结果集后,将链表位置后移,继续遍历下一个结果集即可。


排序类型稍微复杂,由于ORDER BY的原因,每个结果集自身数据是有序的,因此只需要将结果集当前游标指向的值排序即可。Sharding-JDBC在排序类型归并时,将每个结果集的当前排序数据实现了比较器,并将其放入优先级队列。每次JDBC调用next时,将队列顶端的结果集出队并next,然后获取新的队列顶端的结果集供JDBC获取数据。


分组类型最为复杂,分组归并已经不属于OLTP范畴,而更面向OLAP,但由于遗留系统使用很多,因此Sharding-JDBC还是将其实现。分组归并分成流式分组归并和内存分组归并。流式分组归并节省内存,但必须要求排序和分组的数据保持一致。如果GROUPBY和ORDER BY的内容不一致,则必须使用内存分组归并。由于数据不是按照分组需要的顺序取出,因此需要将结果集中的所有数据全部加载至内存。在SQL改写时提到的仅有GROUP BY的SQL,会优化增加ORDER BY语句,即使将内存分组归并优化为流式分组归并的提升。


无论是流式分组还是内存分组,对聚合的处理都是一致的。聚合分为比较、累加和平均值3种类型。比较聚合包括MAX和MIN,只返回最大(小)结果。累加聚合包括SUM和COUNT,需要将结果累加后返回。平均值聚合则是通过SQL改写的SUM和COUNT计算,相关内容已在SQL改写涵盖,不再赘述。

最后再聊一下装饰者归并,他是对所有的结果集归并进行统一的功能增强,目前装饰者归并只有分页一种类型。


上述的所有归并类型,都可能分页或不分页,因此可以通过装饰者模式来增加分页的能力。分页归并会将改写的LIMIT中,不需要获取的数据过滤掉。Sharding-JDBC的分页很容易产生误解,很多人认为分页会占用大量内存,因为Sharding-JDBC会因为分布式正确性的考量,将LIMIT 100000, 10改写为LIMIT 0, 100010,产生Sharding-JDBC会将100010数据都加载到内存的错觉。通过上面分析可知,会全部加载到内存的只有内存分组归并这一种情况。其他情况都是通过流式获取结果集数据的方式,因此Sharding-JDBC会通过结果集的next方法将无需取出的数据全部跳过,并不会将其存入内存。


分布式主键


分布式主键在这里单独提炼出一个章节,因为它是贯穿于Sharding-JDBC整个生命周期的。


分布式主键最独立的部分是生成策略,Sharding-JDBC提供灵活的配置分布式主键生成策略方式。在分片规则配置模块可配置每个表的主键生成策略,默认使用snowflake。


通过策略生成的分布式主键可以无缝的融入JDBC协议,它实现了Statement的getGeneratedKeys方法,将其返回改写后的Result和ResultMetaData,将Sharding-JDBC生成的分布式主键伪装为数据库生成的自增主键返回。


SQL解析时,需要根据分布式主键配置策略判断是否在逻辑SQL中已包含主键列,如果未包含则需要将INSERTItems和INSERT Values的最后位置写入解析上下文。


SQL改写时,将根据解析上下文中的位置改写SQL,增加未包含的主键列名称和值。如果是Statement则在INSERT Values后追加生成后的分布式主键;如果是PreparedStatement则在INSERT Values后追加?,并在传入的参数后追加生成后的分布式主键。


受限于篇幅,读写分离、柔性事务就不在此说明了。


三、Sharding-JDBC未来展望


首先,请和我一同回顾下Sharding-JDBC每个里程碑版本的历程。


1.0.x:分库分表

1.1.x:配置简易化

1.2.x:柔性事务

1.3.x:读写分离

1.4.x:分布式主键

1.5.x:自研解析引擎 + 多数据库支持

 

通过这5个版本的迭代可以看到,Sharding-JDBC的精力主要集中在透明化分布式数据库这部分,因此经常有人问Sharding-JDBC和基于Proxy的数据库中间层有什么区别?和NewSQL数据库又有什么区别?


尽管部署架构不同,但功能上确实差异不明显。不过结构的不同终会将它们推向不同的方向。Sharding-JDBC与业务代码部署在一起的架构,非常适合作为微服务的数据访问层基础开发组件。Proxy和NewSQL是面向运维的数据库,而Sharding-JDBC的定位与当当一并开源的DubboX、Elastic-Job一样,是面向开发的微服务基础类库,它始终以云原生的基础开发套件为目标。


Sharding-JDBC 1.6.x到来,将会愈加明显的划清界限。Sharding-JDBC 1.6.x的目标是配置动态化和数据库治理,通过将配置存入注册中心,达到治理分库分表+读写分离的数据库的目的。在应用端进行数据库发现、流量疏导、故障转移、熔断等功能,向治理服务一样治理数据库。


Sharding-JDBC将作为面向OLTP在线业务的分片化的数据库治理微服务基础组件积极的发展下去。真诚邀请感兴趣的人关注和参与。


来源:中生代技术

原文链接

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/394673.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

const 常量_条款03:尽可能使用const

const 允许你指定一个语义约束(也就是指定一个“不该被改动”的对象),而编译器会强制实施这项约束。1、const指针如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量&…

javascript高级程序设计---js事件思维导图

绘制思维软件与平时用的笔记,以及导出功能,这三个问题综合起来,于是我把思维导图分开画 1、js事件的基本概念 2、js事件的事件处理程序 3、js事件的事件对象 转载于:https://www.cnblogs.com/Jamie1032797633/p/10567419.html

和 Thrift 的一场美丽邂逅

一. 与 Thrift 的初识 也许大多数人接触 Thrift 是从序列化开始的。每次搜索 “java序列化” “方式”、“对比” 或 “性能” 等关键字时,搜索引擎总是会返回一大堆有关各种序列化方式的使用方法或者性能对比的结果给你,而其中必定少不了 Thrift&#…

弹出框 每次打开 滚动条置顶_微信置顶文字怎么弄?微信置顶一句话教程

今日支付宝红包支付宝首页搜索511501453马上领取红包(支付宝双十二活动,瓜分15亿红包)(领取后一定要记得使用,不然会浪费的呦,更会影响第二天的领取!)奶思靓机“ 一 个 有 用 的 公 众 号 の ”嗨,最近很流行在微信上面…

python 3 面向过程编程

python 3 面向过程编程 核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就像设计好一条工业流水线,是一种机械式的思维方式。 1、优点:程序结构清晰,可以把复杂的问题简单化&…

在ionic/cordova中使用百度地图插件

在ionic项目中,如果想实现定位功能,可以使用ng-cordova提供的cordova-plugin-geolocation。 但由于高墙的缘故,国内andorid环境下,此插件不起作用(ios环境下可用)。 国内比较好的是现实使用百度地图提供的A…

汉诺塔问题递归算法python代码_[python]汉诺塔问题递归实现

一、问题描述及算法步骤 汉诺塔问题的大意是有三根柱子a, b, c,现在a柱有N个盘子从下往上尺寸递减排列,要求: 1. 将a上的盘子移动到c柱上; 2. 每次移动一个盘子; 3. 柱子上的盘子始终必须是大的在下面image.png 汉诺塔问题的经典实现算法步骤…

plsql 批量调存储过程_数控双端开榫机:批量铣榫头真牛气

数控双端开榫机主要用于实木家具批量化铣榫头专用,因为其本身的优势逐渐被家具厂老板们所接受,是目前家具生产不可缺少的一款自动化设备,给企业节约了生产成本,今天又焦峰小编来给大家讲解一下。主要技术参数:知乎视频…

python时间差怎么转换为数字_pandas进行时间数据的转换和计算时间差并提取年月日...

#pd.to_datetime函数 #读取数据 import pandas as pd data pd.read_csv(police.csv) #将stop_date转化为datetime的格式的dataframe,存到stop_datetime data[stop_datetime] pd.to_datetime(data.stop_date) #自定义一个时间,计算时间差 data_new pd.…

人脸识别html5效果,用HTML5实现人脸识别

注:今天 HTML5 小组沙龙《论道 HTML5 》分享时有朋友问到一个问题, getUserMedia 是否会支持人脸识别,我当时的答案是这应该是应用来实现的功能,而不是规范要完成的工作。而我之前在网上看到过一篇关于 getUserMedia 和人脸识别的…

华为mate40会不会有鸿蒙系统,鸿蒙OS系统正式推送,拿华为Mate40更新后,发现了优缺点...

自从鸿蒙系统正式推送之后,笔者一直都带着好奇心在体验着HarmonyOS 2带来的变化,生怕错过惊喜,也担心系统本身会出现不足。因为鸿蒙系统就像是年轻人一样,才刚刚出炉,需要时间去磨练,然后才能发挥出真正强大…

jstack使用

jstack主要用来查看某个Java进程内的线程堆栈信息,根据堆栈信息我们可以定位到具体代码,所以它在JVM性能调优中使用得非常多,语法格式如下: jstack [option] pid jstack [option] executable core jstack [option] [server-id]rem…

删除目录软链接注意事项

2019独角兽企业重金招聘Python工程师标准>>> 实验环境: 在root 目录下创建一个目录 1 ,并在该目录下创建一个2.txt 的文件,写入内容 1.txt: [rootserver ~]# mkdir 1 [rootserver ~]# echo 1.txt > 1/2.txt [rootserver ~]# tree 1 1 └─…

html如何模拟点击,Javascript 模拟点击事件(点击链接与html点击) 兼容IE/Firefox

一把情况下模拟点击一般两个方面,模拟点击超级连接事件firefox的兼容的函数为对HTMLAnchorElement 加入onclick事件try {// create a element so that HTMLAnchorElement is accessibledocument.createElement(a);HTMLElement.prototype.click function () {if (ty…

mvn编写主代码与测试代码

maven编写主代码与测试代码 3.2 编写主代码 项目主代码和测试代码不同,项目的主代码会被打包到最终的构件中(比如jar),而测试代码只在运行测试时用到,不会被打包。默认情况下,Maven假设项目主代码位于src/…

打印速度快点的打印机_SLM推出了功能强大的新型金属3D打印机,速度快20倍

德国金属3D打印机制造商SLM Solutions在Formnext Connect贸易展览会上推出了功能强大的新系统NXG XII 600。SLM的大幅面机器配备了十二个可同时运行的1 KW激光器,使其速度比该公司自己的单激光SLM 280快20倍。NXG XII 600经过定制设计,可大量生产大型零件…

1.7Oob 继承关系中构造方法的使用

1:父类中最好要有一个空参数的构造方法,因为默认的构造方法在自定义了构造方法后就不存在了,需要显示的写出来。 若父类中没有空参数的构造方法,则子类必须有自定义的构造方法,且用super()调用父…

JavaScript浮点运算0.2+0.1 !== 0.3

浮点运算JavaScript 本文主要讨论JavaScript的浮点运算,主要包括 JavaScript number基本类型二进制表示十进制浮点数的精度number 数字类型 在JavaScript中,数字只有number这一种类型; var intS 2,floatA 0.1; typeof intS; // number typeof floatA…

三菱模拟量输入与输出程序_初学PLC是学习西门子还是三菱?

PLC的种类繁多,品牌大多分为欧系、日系、美系。德系PLC以西门子为主,日系有三菱、欧姆龙、松下……,美系有罗克韦尔(A-B)通用电气(GE)公司、莫迪(MODICON)公司等。美国和欧洲的PLC技术是在相互隔离情况下独立研究开发的,因此美国和…

性能测试十四:Xshell链接linux虚拟机

一、先装一个linux虚拟机 VBoxcentos1、先下载Linux镜像文件的ovf或者OVA文件2、打开vbox,点击菜单栏“管理”-“导入虚拟电脑3、选择解压路径中的ovf或者OVA文件,点击下一步 4、点击“导入”,等待完成5、导入成功后,选择新导入的…