Modern JavaScript - Startup with Babel

  1. 1. plugin
    1. 1.1. ECMAScript 2015
    2. 1.2. ECMAScript 2016
    3. 1.3. ECMAScript 2017
  2. 2. preset
  3. 3. runtime
  4. 4. build
  5. 5. babel-register
  6. 6. 实战

Babel 对 Modern JavaScript 的推动功不可没,它使得开发者可以愉悦的享受新的语法带来的快感,然后再翻译成生产环境需要支持的旧的代码集。

JavaScript 的功能和版本之争吵了很多年,Babel 是个用行动来代替争吵的勇士。

当然更好的消息是,越来越多的功能被 node/浏览器 原生给实现,这意味着,越来越少的东西需要被编译。


## 概念入门

Babel 是一个转码器,它可以将 JavaScript 中的一个新语法进行转换,让旧的引擎支持。

翻译中的两个概念是 plugin & preset。

plugin

每个 plugin 对应的是一个 feature,可以在这里看到各个版本中对应的更新和 node 的支持度:http://node.green/

下面列出了 ECMA 近期版本中的一些变化点。每个点都是一个 plugin。

ECMAScript 2015

ES2015 确实是一个很大的改变,光看这个列表就能知道。使用该这些 plugins 可以将代码编译为 ES5 环境能执行的。

  • es2015-template-literals
  • es2015-literals
  • es2015-function-name
  • es2015-arrow-functions
  • es2015-block-scoped-functions
  • es2015-classes
  • es2015-object-super
  • es2015-shorthand-properties
  • es2015-duplicate-keys
  • es2015-computed-properties
  • es2015-for-of
  • es2015-sticky-regex
  • es2015-unicode-regex
  • es2015-constants
  • es2015-spread
  • es2015-parameters
  • es2015-destructuring
  • es2015-block-scoping
  • es2015-typeof-symbol
  • es2015-modules-commonjs

ECMAScript 2016

  • transform-exponentiation-operator

ECMAScript 2017

  • syntax-trailing-function-commas
  • transform-async-to-generator

preset

preset 就是一组 plugin 的集合,可以让开发都不用一个个的单独配置 plugin。

es2015, es2016, es2017 是目前三个主要的版本。

这三个版本中,相互没有包含关系,里面的 plugin 都是 ecma 所对应的 features.

最近加了一个 latest,它其实就是将上面三个 preset 再次包装了一次,使用起来更方便了。

1
2
3
4
5
presets: [
opts.es2015 !== false && [buildPreset, opts.es2015],
opts.es2016 !== false && require("babel-preset-es2016"),
opts.es2017 !== false && require("babel-preset-es2017")
]

除了 上面说的 ECMAScript 标准的集合,也会有一些自定义的集合可用。像

  • babel-preset-react

## Babel 上手速成
  • .babelrc
  • babel-cli
  • babel-register

Babel 常有两种使用方式,一个是即时的,常用在开发环境使用,快速,方便。
另一个是编译用的,用在线上发布使用。

runtime

在开发模式中,我们会用 babel-register 来做时时支持。但这样的效率不高,不适合用于 production 环境。

一个标准的 babel 配置如下,开发环境可以 require 这个配置,就能正常运行新的 feature 了。

1
2
3
4
require("babel-core/register")({
presets: ["es2017"],
plugins: ["transform-es2015-modules-commonjs"]
});

build

线上发布会使用 build 后的代码,.babelrc 是一个 build 所需要的配置文件,它的配置内容和 runtime 是一样的,只是写法有所不同。

1
2
3
4
5
{
"presets": ["es2017"],
"plugins": ["transform-es2015-modules-commonjs"],
"sourceMaps": "both"
}

babel-register

babel-register 模块改写 require 命令,为它加上一个钩子。此后,每当使用 require 加载 .js、.jsx、.es和 .es6 后缀名的文件,就会先用 Babel 进行转码。

需要注意的是,babel-register 只会对 require 命令加载的文件转码,而不会对当前文件转码。另外,由于它是实时转码,所以只适合在开发环境使用。

1
2
3
4
5
require("babel-core/register")({
presets: ['es2017']
});

require("./src/index.js");

entry.js 中是不能使用新语法的。一定要定义在另一个新文件中。


## 最佳实践

Babel 可以 javascript 翻译成任何一个你想到的旧版本。但如果环境已经支持的一些 feature 再被 transpile,就不够优雅了。

所以,更好的作法是,根据具体需求,来选择 preset/plugin,就翻译哪些。

Node 7 对这些语法的支持,已经接近完美了。(只差 async/esm),如果用到了这两个 feature,就直接加这些 plugin 好了。

实战

关于代码的组织结构,参考了 Rails,而不是 node/web 常用的 src 方案,因为我们相信很快,代码就不用再 build,而且直接上线。

Server 端用 Node.js 6.4.0,开发中用到的 feature 列表中,只有两项还暂不支持:

  • es2015 module-commonjs
  • es2017 async-functions

所以在配置 babel 时,根据自己的需求,只需要单独引入就可以了,这样,编译出来的代码几乎相同了,.babelrc 配置如下:

1
2
3
4
5
{
"presets": ["es2017"],
"plugins": ["transform-es2015-modules-commonjs"],
"sourceMaps": "both"
}

## REF::

IT 设备折腾记 - U2717D, Ticwatch2, SSD & Router

  1. 1. DELL U2717D

DELL U2717D

同事新入了 U2717D,号称 InfinityEdge,听起来很诱惑,其实相对于 U2515H,只是下边框更薄,拼屏时,看起来会更好看一些,但四拼无优势,并排三拼还可以。

借用来体验了一天,尺寸大了后,字体也放大了一些,看起来没那么累,当然差别也没那么大,毕竟都是 2K,想换的心还是有的,只是这一点的差距和 3500 的价格,感觉划不来。


**---------------峰回路转--------------------------------------------**

不过由于没向他家领导报备,只能偷偷的买,再偷偷的出掉,被我拿下,于是我就又多了一款显示器,现在数下来,就有显示器如下:

Model Size Resolution Desc
iMac 27 27’ 2K TDM 很难用
U2717D 27’ 2K 20160815
U2515H 25’ 2K 说明
U2412H 24’ 1080P 已经接受不了这分辨率

这样,我就可以把 24 放卧室的新角落,2515 和 iMac 27 放书房。2717 暂放公司。

本计划 9 月份入新 iMac 5K,这下,太多了,就不知怎么处理了。

现在公司好像也让申请 2515,感觉一下子显示器爆炸了。好爽。我果然是一个收集控。


## Ticwatch 2

我家领导过生日,给自己选了一个手表,我也不是很理解为什么是一款国产手表。

  • 嫌 Apple Watch 显太丑,喜欢圆型表盘,好一个看脸的时代。
  • 同事买了一个,感觉不错。

到手后体验了下,质感跟 Apple Watch 比,差了不少,不过确实好看不少。表盘也很丰富,比 Apple Watch 强不少。

功能上,差不多 Apple Watch 用到的这上面也都有,蓝牙匹配好之后,各种通知,消息也都能正常接收,NB 的是一些国产软件的便捷度更高。

关于我对 Apple Watch 的评价,看 Apple Watch - 1st Month Experience

看到这些新国货做的好也蛮开心的,不过换作是我,肯定不会选的。

最后,智能手表是个体验不错,但并无卵用的东东,经常忘记充电,屏幕一掉就碎。

话说,现在倒比以前更喜欢戴苹果表,原因是已经烂了,多戴一天赚一天吧。


## Samsung 750EVO 250G

Macky 上是一块 128G 的 Crucial M4,性能是没得说,但大小,实在是,经常要删一些文件,才能解除警报。

计划 618 入一款新的 250G 的,土豪龙 推荐这款,没等到 618,在淘宝上看到一个不错的价格,就直接入了。现在是这样的组合:

没有用 Fusion Drive,那样太慢,现在是通过 soft link,把 BD 的各个目录 link 到 home 下。使用起来很方便。** 这是我喜欢 linux 的几大原由之一。**

性能没感觉出差别,毕竟老本上只是 SATA,瓶颈不在硬盘上。


## 极路由Go

好 SB 的我又买了一次极路由,确实再次证明了我的 SB。

本是需要将一款仅支持有线的网络打印机,通过路由器的桥接来实现无线功能,看说明感觉可以,就下了单,结果,对接自家的路由器,完全没这个功能。

而且只有 2.4G 的网络,和一个阉割版的后台。

当然,这都只能说明是我自己的问题,或者是 SB 产品经理的问题,不是路由器本身的问题。

当 Node 遇到多核

Node.js® is a JavaScript runtime built on Chrome’s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

Node 是单线程, Event IO 模型,但服务器是 32 核心的,怎么充分利用。

Q: 进程,线程和 CPU 内核的对应关系?

线程只是用于分配单个处理器处理时间的一种机制。但假如操作系统本身支持多个 CPU/内核,那么每个线程都可以得到一个不同自己的 CPU/内核,实现真正的 并行运算。在这种情况下,多线程程序可以提高资源使用效率。

  • 单进程,单线程
  • 单进程,多线程
  • 多进程,单线程
  • 多进程,多线程

操作系统才会关心进程,CPU 是没有进程的概念的?

  • 线程是进程的一部分
  • CPU 调度的是线程
  • 系统为进程分配资源,不对线程分配资源

线程是cpu调度的一个基本单位,一个cpu同时只能执行一个线程的任务,同样一个线程任务也只能在一个cpu上执行

Q: Node 的单线程

Node 是单线程程序,它只有一个 event loop,也只占用一个 CPU/内核。现在大部分服务器都是多 CPU 或多核的,当 Node 程序的 event loop 被 CPU 密集型的任务占用,导致有其它任务被阻塞时,却还有 CPU/内核 处于闲置的状态,造成资源的浪费。

同时一旦这个进程崩掉,那么整个 web 服务就崩掉了。

单线程的好处:

  • 安全: 避免多个线程同时操作
  • 简单: 不用并发锁
  • 性能: 避免了频繁的线程切换开销

Q: Event-driven

Q: Non-blocking I/O Model

如何做到异步/非阻塞 I/O的呢?

其实 Node 在底层访问 I/O 还是多线程的,有兴趣的朋友可以翻看 Node 的 fs 模块的源码,里面会用到 libuv 来处理 I/O,所以在我们看来 Node 的代码就是非阻塞和异步形式的。

Q: Node 单线程和多核

如何更好的利用多个 CPU/内核,答案就是启动多进程。有两种方案:

  • Nginx
  • Culster/Webworker

这两种方案其实都是如何做反向代理和负载均衡。只是分发的工作谁来做 Nginx 天生是做这件事的,非常适合,但成本会比较高,毕竟每次都要配置。

Culster 成本就低不少,通过 PM2,只要启动时加一个参数,就可以指定 worker 的数量。而且支持自动创建新的 worker。

Q: Cluster 是什么,如何做到的

Taobao 有一篇好文,《当我们谈论 cluster 时我们在谈论什么 》,详细讲解了 cluster 的原理。

Q: CPU/内核 和 Node 进程最佳配置

一般来说,有多少内核就分配多少个 worker。

Q: Node 的多线程

I/O 的多线程就是通过 libuv 来实现的。

Q: Docker 怎么利用多核?

Docker 不关心这些,给 Container 分配多少,就用多少,和在 host 中跑没有区别。

Q: Node 的适用场景

其实主要是 EventIO 的适用场景,大量 IO,网络请求的操作。

特别适合 API。

Q: 注意事项

  • 一定不要阻塞线程
  • 把运算型的方法,通过空间换时间。

Node.js软肋之CPU密集型任务
http://www.infoq.com/cn/articles/nodejs-weakness-cpu-intensive-tasks

Node.js的线程和进程
https://github.com/DoubleSpout/threadAndPackage/blob/master/chapter.7.thread_and_process.md

http://taobaofed.org/blog/2015/10/29/deep-into-node-1/

8384

阿里兰口口面

七月的上海最火的除了太阳,还有一件事件,阿里兰牛肉面事件。

大致的缘由是一家兰州拉面馆,没有向化隆黑帮(伊组织)报备,而且违反了化隆的 500 米内不得有 2 家拉面店的协议,于是对该店进行围堵打砸恐吓等流氓行为。

事发地点在上海南京路的一条支道上,首先是上海,这是我心中大陆唯一的一个法制城市,其次是南京路,上海最繁华地带。

警察,做了些什么?调停,少数民族两少一宽的政策让警察在里面和稀泥,无作为。在围堵的20来天中,河南一例围堵 KFC 被拘留事件再次给上海警方一记啪啪啪。这是多数和少数民族的区别?

调停的结果是阿里兰招牌上不准出现牛肉二字,化隆黑帮退去,警察也不用纠结,看似三方胜利,那倒底谁输了呢?

法律,中国依法治国几十年的今天,上海的工商管理法竟不抵一个青海的小萝卜章拉面规定,可笑可笑,网友戏称不如改叫伊法治国。

国内的记者不敢提,BBC 的报道让人三思,民族问题从不应如此回避,这样只会激化更大的矛盾。

去吃了一碗口口面,只为纪念。

因为晚上去的,手机拍照不清晰,图片是网上搜的。


## 不安全的世界

现在看里约奥运,就像一个玩笑,晚上微博里还有古天乐在拍戏听到的枪声,到处都是抢劫还有蚊帐的趣闻。

和里约奥运一样,更多的地方都有着相同的困扰,法国,德国。

实在是不明白默克尔为什么在难民问题上采取这样的态度,难民重要,难道本国的安全问题、民族问题就不重要吗。

电台里听到,为什么恐怖会如此喜爱法国,那是因为他们憎恨自由和浪漫,而法国又同时拥有这两样东西。

人类文明终结于 21 世纪?


## 某些问题

本以为反ri是一个民族情节,这次却证明了并不是,爱国贼就像一个异jiao.tu,只要有组织,他们可以咬向任何一个地方。去’合法’的抢别人的劳动果实,去做任何伤天害理之事。细思极孔,感觉回到了上个世纪的那些年代。


## 个人

个人真的很讨厌zz,也不明白为啥好多人一听说要打台湾一脸的兴奋,更喜欢台湾的小确幸,确信小家胜大家。

但有些事情,确实让人烦躁。

data sync in remote system

我们遇到一个问题,比较麻烦,也很常见。

大型系统中都会将用户系统独立,但用户系统被每一个系统依赖。

像评论,如果100 个评论,跨网访问100次的话,疯了

或都,遍历出所有的 id,通过 batch 来访问。然后再把这个值进行组合拼装。性能会好很多,但怎么看也就是一个丑小丫吧。

能不能,把用户信息还存在自己系统里,然后统一向用户中心,如果用户中心发生了修改,所有系统收到消息,然后进行修正。

这样的代价,就是需要一个消息机制,像 MessageQueue 这样的东西。

或者在用户中心注册自己的 API。但这个通知是异步的,就是还会有用户刷新完看不到新信息的情况,但已经很小了。

典型的空间换时间。

struct-performance

我们那棵树长什么样、

task > section > group > subtask

lrs_task > lrs_group > lrs_subtask

当学完一个 subtask 时,需要更新自己,并更新 lrs_task 的进度。
当 subtask 增加时,需要对所有的 lrs_task 进行更新进度。

何解?

方案1:

每次都去读 task 下的 subtask 总数
然后再读这个用户的做过的 subtask 总数,进行比较。

进化 》

将 task 总数进行 cache

进化》
将进度进行 cache,如果课程更新,删除 cache。
每次读的时候,如果进度为空,最上面的方案读一次就好了。

方案2:

微服务中的缓存套路

我在的部门新系统开发近一年,业务从开始的混沌也渐渐清晰。本着绝不过早、过度优化的原则,只是在一些数据结构上做了一些改进,在一些需要算的地方用空间换时间。

业务稳定后,逐渐把关注点放在性能上,比如说:缓存。

当然缓存有很多种方式:

  • 把一些公用数据放在内存中
  • 像刚提到的,把一些计算的值从读时计算改成写入时计算
  • 当然还有最熟悉的 Redis,Memcached 这种内存型 key-value 缓存方案。

这里要重点推荐下耗子哥的好文缓存更新的套路,文中总结了四种常用模式:

  • Cache Aside Pattern
  • Read Through Pattern
  • Write Through Pattern
  • Write Behind Caching Pattern

这几种模式在系统中也有涉及。

缓存的粒度

Second Level Cache

对于对象来说,这个粒度会产生更多的对象,也会有更多的 DB call,从性能上来讲,肯定是不被接受的,但在 Cache 上分析,这却又变成了好处,更多的对象,意味着可以被更多的缓存,对于更新可以不用全局删除缓存。

会有一些通用的库,一般在 ORM 中。

Biz Level Cache

这是我们目前用到的,它的颗粒度更大,好处是不用维护太多 key,坏处是小的修改也会触发缓存失效。

全手工写。

Content Service Cache - Cache Aside Pattern

内容是最小可复用体,它可能会被多个 Gateway 引用。所以单独的 Cache 是有意义的。

我们把内容服务分为两部分,内容 & 记录(关于这一块,更多可了解。。。)。

内容一旦被写入,几乎不会再做修改。但记录这一块,不是很确定,所以没有直接上线缓存。

把接口数据抽像为了两部分:

1
2
3
4
5
6
7
8
{
"content": {
"title": "The best content for learning"
},
"record": {
"score": 99
}
}

第一部,我们只是对 Content 进行了缓存,上线几周后,在 ELK 里看关于 Record 的数据,看 POST 和 GET Method 的对比,来判断用户的读写习惯。

数据为 1.7:8.3,Get 请求比还是很可观的,说明用户经常打开一些题目,只是看看,并没有进行做题,这样我们对 Record 也做了缓存。

缓存的流程图大概就是这样了。

  • 如何保证更新的事务。保证写入 Cache 成功。

Gateway Service Cache - Read/Write though Pattern

内容被多个 Gateway 引用,从业务场景上来看,一些只读的数据,无 Record 的。

理论上数据有一个短暂的不一致,在业务上是许可的,可接受的。

  • Read Only Content
  • Token Authentication

基于这个原则,我们来设计缓存规则。

不做更新。

Read Write Through Pattern 这种模式更像是被用作 ORM 中。

https://github.com/hooopo/second_level_cache
Write Through and Read Through caching library

Write Behind Caching Pattern

极端事务处理模式:Write-behind缓存

这种模式,有点像 Redis 本身,In memory,但也会做持久化。

当然,也可以用作一种业务上的缓存模式,只是见到的会比较少。

另外,Write Back实现逻辑比较复杂,因为他需要track有哪数据是被更新了的,需要刷到持久层上。操作系统的 write back会在仅当这个cache需要失效的时候,才会被真正持久起来,比如,内存不够了,或是进程退出了等情况,这又叫lazy write。

ORACLE: Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching
MSDN: Cache-Aside Pattern 缓存模式

4 Simple Memoization Patterns in Ruby (and One Gem) http://www.justinweiss.com/articles/4-simple-memoization-patterns-in-ruby-and-one-gem/

Hierarchical Data Pattern

Tree Structure 是一种很常见的需求(像分类,目录等),关系数据库中,有几种经典模式:

  • Adjacency List Model
  • Nested Set Model
  • Nested Interval Model
  • Materialized Path Model

关于这几种模式的一些通用介绍,参考:

Ruby Toolbox 中一些对 Tree 的通用库:


1. Adjacency List Model

Use ancestry. It has more powerful query capabilities as it implements the materialized path pattern, as opposed to acts_as_tree that implements adjacency list.

父节点引用的方式(也称父节点引用模式),将树形结构中的每个节点单独存放到一个文档中,并在每个文档中保存节点的父节点引用。 考虑以下数据结构:

id type(*optional) parent_id depth
1 course 1
5 section 1 1
7 courseware 5 2
8 courseware 5 2

查询出该课程:

1
2
3
4
5
6
7
8
9
10
11
12
13
SELECT t1.type AS lev1, t2.type as lev2, t3.type as lev3
FROM structs AS t1
LEFT JOIN structs AS t2 ON t2.parent_id = t1.id
LEFT JOIN structs AS t3 ON t3.parent_id = t2.id
WHERE t1.id = 1;


+-------------+----------------------+-----------------+
| lev1 | lev2 | lev3 |
+-------------+----------------------+-----------------+
| course(1) | section(5) | courseware(7) |
| course(1) | section(5) | courseware(8) |
+-------------+----------------------+-----------------+

.1 优势

实现起来特别简单,因为只是存储 parent_id,没有多余的算法。

.2 不足

如果想构造一根树,在 DB 查询时,需要太多次循环调用。产生性能问题。

Recursive CTEs

Generating Depth based tree from Hierarchical Data in MySQL (no CTEs)

.3 Rails 实现

Organise ActiveRecord model into a tree structure

.4 REF


2. Nested Set Model

.1 Nested Set (前置树)领域模型模式

对应到的 DB Scheme 如下(p_id 非必须,depth 也非必须):

id type(*optional) parent_id lft rgt depth
1 course 1 8 0
5 section 1 2 7 1
7 courseware 5 3 4 2
8 courseware 5 5 6 2

如果只是这样看的话,lft, rgt 很难看明白,但换成下面的图,就立即清晰了。

.2 原理

它构建了一个森林(整体),而不是一棵棵的树。

读时,是通过一个条件,将大于根左并且小于根右的全读出即可。

写时,因为整体的原因,在第一颗树上的任意修改,都会反应到后面的树上。

查询该课程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT structs.id
FROM structs AS node,
structs AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND parent.id = 1
ORDER BY node.lft;

+-------------+
| id |
+-------------+
| 1 |
| 5 |
| 7 |
| 8 |
+-------------+

查询出所有的叶节点:

1
2
3
4
5
6
7
8
SELECT id FROM structs WHERE rgt = lft + 1;

+--------------+
| id |
+--------------+
| 7 |
| 8 |
+--------------+

.3 明测

480W 条数据,在最前部加入一个新节点(对 DB 所有记录 lft rft 进行 +2 的方式,全局更新),耗费 97s。😂

可见,这个将会是很严重的性能问题。

不过有种办法可以 fix 这个问题,就是在将森林还原为一棵棵树,每次操作只对树进行,不会影响到其它的树。

这样的话,每颗树的的 lft rgt 0 开始,看起来也清楚点。只是不能跨树进行移动。

.4 REF

.5 Rails 实现


3. Nested Interval Model

跟 Nested Set 差不多,也是有左右的。

.4 Rails 实现


4. Materialized Path Model

Materialized Path(物化路径)通过在数据中加入它的一个路径,来实现层级关系。

DB Scheme

id type(*optional) path depth(*optional)
1 course / 1
5 section /1/ 2
7 courseware /1/5/ 3
8 courseware /1/5/ 3

找到所有的 Root

1
2
-- find all ROOT
SELECT * FROM structures WHERE path="/"

找到课程1下的所有章节或课件

1
2
3
4
5
-- find all sections
SELECT * FROM structs WHERE path="/1/%" AND deep=2

-- find all coursewares in the course
SELECT * FROM structs WHERE path="/1/%" AND deep=3

性能

可能在 path 上加上 Index,来提高性能。

硬盘接口那些事

  1. 1. 硬盘类型
  2. 2. 总线(Bus)
    1. 2.1. 系统总线
      1. 2.1.1. - PCIe
    2. 2.2. 外部总线(I/O 总线)
      1. 2.2.1. - SATA
  3. 3. 硬盘接口
    1. 3.1. - PATA (Parallel ATA)
    2. 3.2. - SATA (Serial ATA)
    3. 3.3. - mSATA (mini-SATA, SATA 3.1)
    4. 3.4. - M.2
  4. 4. iMac 中的标准
  5. 5. REF::

想入新 iMac,本着自己动手其乐无穷的精神,准备自己 DIY 升级,于是在配置上看了下,新的内存是可以方便的拆换的,但硬盘就麻烦了。无论是 HDD,或 PCIe 的 SSD,更纠结的是这个 SSD 到底是什么接口。

于是一个关于硬盘接口的科普文产生。


硬盘类型

  • HDD
  • SSD

传统 HDD 过渡到 SSD 带来的变化是翻天覆地,SSD 正在影响着每一个人,而且由于技术的原因,它可以做的更小,被用做更多的地方,所以再次引发一次新的总线、接口变革。


总线(Bus)

提到硬盘,就不能不提总线。总线是指计算机组件间规范化的交换数据的方式。

方面 包含 说明
分类 系统总线 前端总线 后端总线 控制总线 地址总线 -
标准 PCI, PCI Extended(PCI-X), AGP, PCIe, SATA -
存储 并行 ATA(PATA), DMA, USB, 串行ATA(SATA), mSATA, m.2 -

总线可同时传输的数据数就称为宽度(width),以比特为单位,总线宽度愈大,传输性能就愈佳。总线的带宽(即单位时间内可以传输的总数据数)为:总线带宽 = 频率x宽度(Bytes/sec)。

系统总线

总线有不同的级别,顶级的是系统总线,往下分级调度,常见系统总线主要有以下两种,(x86 架构):

  • PCI
  • PCIe (也称 PCI-E) 2001 年提出 / 第三代 / 目前版本为 3.0

- PCIe

PCIe 每个设备自己的专用连接,不需要向整个总线请求带宽,而且可以把数据传输率提高到一个很高的频率,达到 PCI 所不能提供的高带宽。

规格 理论传输速度(带宽) 实际 说明
PCIe 3.0 x1 - - -
PCIe 3.0 x4 32 Gb/s (4 GB/s) 31.5 Gb/s (3.9 GB/s) m.2 接口用的是这个
PCIe 3.0 x8 - - -
PCIe 3.0 x16 - 一般显卡会用这个 -

外部总线(I/O 总线)

今天我们讨论的存储是 外部总线,用作主板和大量存储设备之间的数据传输。

题外:
以前系统中有北桥、南桥。北桥其实就是 PCI/PCIe 总线,然后边围的设备,像 USB,硬盘,通过南桥芯片进行通信,降低对北桥的带宽消耗。后来,Intel 北桥总线由 FSB 技术进化到 QPI,再后来,直接将北桥集成入 CPU。CPU 直接连接南桥、显卡、内存,这根总线叫 DMI。DMI 总线带宽需要分配给 SATA 3.0、USB 3.0、PCIe 等端口。

它们之前的通信关系如下:

1
PCIe < DMI < SATA

接口后面所对应的数据标准和总线。

- SATA

SATA 适配器 (DMI总线) 从属 PCI/PCIe,其设备除备限于 PCI/PCIe,还受限于 SATA 自身带宽的限制(主控)。

PCIe 直接走系统总线,可以接近最大传输速率。

规格 理论传输速度(带宽) 实际 说明
SATA III 6.0 Gb/s (750 MB/s) 4.8 Gb/s (600 MB/s) mSATA 还是用了 SATA 总线,限制了它的最高速度
USB III 5.0 Gb/s (625 MB/s) - -

硬盘接口

对应存储,每种标准后都有自己的接口。标准升级带动的是设备升级和不兼容。

  • PATA (我用电脑时的东东,太古老了)
  • SATA (1,2,3)
  • mSATA (金手指不大一样)
  • M.2(NGFF)
  • PCIe (就像显卡的那种大型接口,一般会用在服务器上吧。)

- PATA (Parallel ATA)

一般叫 IDE 较多,硬盘用 40 针的数据线,很宽每次插拔起来都不大方便,而且线长度受限,传输速率也不高。

ATA (Advanced Technology Attachment) 技术是一个关于 IDE(Integrated Device Electronics)的技术规范族。最初,IDE 只是一项企图把控制器与盘体集成在一起为主要意图的硬盘接口技术。 随着日益广泛的应用,全球标准化协议将其纳成为全球硬盘标准,这样就产生了 ATA。

ATA 一共有 7 个版本。

- SATA (Serial ATA)

SATA 1.0 提供 150 MB/s 的高峰传输速率,相对于 IDE,功耗也有所降低,接口也变得更小。

SATA 2.0 则升级为 300m/s。

而 3.0 运行速度 6Gb/s, 最高传输速度可达 600m/s,速度再翻一番。

- mSATA (mini-SATA, SATA 3.1)

时间再往后推,硬盘从主流的 3.5 都换成了 2.5,更小尺寸的存储设备也都普及了,SATA 的接口显得过于巨大,小巧的设备需要更小巧的接口。

SATA 协会推出新的 mSATA 接口。

它走的通道是 PCIe,理论传输速率可达 6 G/s。

- M.2

由于 SSD 的飞速发展,mSATA 刚推出不久,M.2 接口就来了。其是 Intel 带领制定的新一代接口标准,具有体积更小,接口更快的特点。

M.2 在设计之初,借鉴 mSATA 的缺陷,在兼容性上做了改进,根据金手指缺口区分 PCIe/SATA 总线

  • Socket 2:支持 SATA 3(6Gbps 速率PCIe x2 总线,降低生产成本。
  • Socket 3:为高性能设计,支持 PCIe 3.0 x4,提升性能,弥补 mSATA 的遗憾。

B+M keyed drive (left, Socket 2) and a M keyed drive (right, Socket 3)

目前 M.2 SSD 的主要规格有 3 种,主要是长度不一样,通过长度增加容量。


iMac 中的标准

看完这些,再看 iMac 中,你会发现,它用的是 mSATA 的接口,但却突破了 600 MB/s 的速度限制,怎么做到的?

因该是主板做了一些变通,直接连到了 PCIe 总线上(猜测)。


REF::