如何在 Apache Kafka 中通过 KSQL 分析 Twitter 数据

Summary: KSQL 是 Apache Kafka 中的开源的流式 SQL 引擎。它可以让你在 Kafka 主题topic上，使用一个简单的并且是交互式的 SQL 接口，很容易地做一些复杂的流处理。

![](/data/attachment/album/201711/03/230240ei0izdx0ldzlviyl.jpg)

### 介绍

[KSQL](https://github.com/confluentinc/ksql/) 是 Apache Kafka 中的开源的流式 SQL 引擎。它可以让你在 Kafka <ruby> 主题 <rt>  topic </rt></ruby>上，使用一个简单的并且是交互式的 SQL 接口，很容易地做一些复杂的流处理。在这个短文中，我们将看到如何轻松地配置并运行在一个沙箱中去探索它，并使用大家都喜欢的演示数据库源： Twitter。我们将从推文的原始流中获取，通过使用 KSQL 中的条件去过滤它，来构建一个聚合，如统计每个用户每小时的推文数量。

### Confluent

![](/data/attachment/album/201711/03/230309xi4qjpw5qqgej6i4.png)

首先， [获取一个 Confluent 平台的副本](https://www.confluent.io/download/)。我使用的是 RPM 包，但是，如果你需要的话，你也可以使用 [tar、 zip 等等](https://docs.confluent.io/current/installation.html?) 。启动 Confluent 系统：

```
$ confluent start

```

（如果你感兴趣，这里有一个 [Confluent 命令行的快速教程](https://www.youtube.com/watch?v=ZKqBptBHZTg)）

我们将使用 Kafka Connect 从 Twitter 上拉取数据。 这个 Twitter 连接器可以在 [GitHub](https://github.com/jcustenborder/kafka-connect-twitter) 上找到。要安装它，像下面这样操作：

```
# Clone the git repo
cd /home/rmoff
git clone https://github.com/jcustenborder/kafka-connect-twitter.git

```

```
# Compile the code
cd kafka-connect-twitter
mvn clean package

```

要让 Kafka Connect 去使用我们构建的[连接器](https://docs.confluent.io/current/connect/userguide.html#connect-installing-plugins)， 你要去修改配置文件。因为我们使用 Confluent 命令行，真实的配置文件是在 `etc/schema-registry/connect-avro-distributed.properties`，因此去修改它并增加如下内容：

```
plugin.path=/home/rmoff/kafka-connect-twitter/target/kafka-connect-twitter-0.2-SNAPSHOT.tar.gz

```

重启动 Kafka Connect：

```
confluent stop connect
confluent start connect

```

一旦你安装好插件，你可以很容易地去配置它。你可以直接使用 Kafka Connect 的 REST API ，或者创建你的配置文件，这就是我要在这里做的。如果你需要全部的方法，请首先访问 Twitter 来获取你的 [API 密钥](https://apps.twitter.com/)。

```
{
 "name": "twitter_source_json_01",
 "config": {
   "connector.class": "com.github.jcustenborder.kafka.connect.twitter.TwitterSourceConnector",
   "twitter.oauth.accessToken": "xxxx",
   "twitter.oauth.consumerSecret": "xxxxx",
   "twitter.oauth.consumerKey": "xxxx",
   "twitter.oauth.accessTokenSecret": "xxxxx",
   "kafka.delete.topic": "twitter_deletes_json_01",
   "value.converter": "org.apache.kafka.connect.json.JsonConverter",
   "key.converter": "org.apache.kafka.connect.json.JsonConverter",
   "value.converter.schemas.enable": false,
   "key.converter.schemas.enable": false,
   "kafka.status.topic": "twitter_json_01",
   "process.deletes": true,
   "filter.keywords": "rickastley,kafka,ksql,rmoff"
 }
}

```

假设你写这些到 `/home/rmoff/twitter-source.json`，你可以现在运行：

```
$ confluent load twitter_source -d /home/rmoff/twitter-source.json

```

然后推文就从大家都喜欢的网络明星 [rick] 滚滚而来……

```
$ kafka-console-consumer --bootstrap-server localhost:9092 --from-beginning --topic twitter_json_01|jq '.Text'
{
  "string": "RT @rickastley: 30 years ago today I said I was Never Gonna Give You Up. I am a man of my word - Rick x https://t.co/VmbMQA6tQB"
}
{
  "string": "RT @mariteg10: @rickastley @Carfestevent Wonderful Rick!!\nDo not forget Chile!!\nWe hope you get back someday!!\nHappy weekend for you!!\n❤…"
}

```

### KSQL

现在我们从 KSQL 开始 ! 马上去下载并构建它：

```
cd /home/rmoff
git clone https://github.com/confluentinc/ksql.git
cd /home/rmoff/ksql
mvn clean compile install -DskipTests

```

构建完成后，让我们来运行它：

```
./bin/ksql-cli local --bootstrap-server localhost:9092

```

```
                       ======================================
                       =      _  __ _____  ____  _          =
                       =     | |/ // ____|/ __ \| |         =
                       =     | ' /| (___ | |  | | |         =
                       =     |  <  \___ \| |  | | |         =
                       =     | . \ ____) | |__| | |____     =
                       =     |_|\_\_____/ \___\_\______|    =
                       =                                    =
                       =   Streaming SQL Engine for Kafka   =
Copyright 2017 Confluent Inc.

CLI v0.1, Server v0.1 located at http://localhost:9098

Having trouble? Type 'help' (case-insensitive) for a rundown of how things work!

ksql>

```

使用 KSQL， 我们可以让我们的数据保留在 Kafka 主题上并可以查询它。首先，我们需要去告诉 KSQL 主题上的<ruby> 数据模式 <rt>  schema </rt></ruby>是什么，一个 twitter 消息实际上是一个非常巨大的 JSON 对象， 但是，为了简洁，我们只选出其中几行：

```
ksql> CREATE STREAM twitter_raw (CreatedAt BIGINT, Id BIGINT, Text VARCHAR) WITH (KAFKA_TOPIC='twitter_json_01', VALUE_FORMAT='JSON');

Message
----------------
Stream created

```

在定义的模式中，我们可以查询这些流。要让 KSQL 从该主题的开始展示数据（而不是默认的当前时间点），运行如下命令：

```
ksql> SET 'auto.offset.reset' = 'earliest';
Successfully changed local property 'auto.offset.reset' from 'null' to 'earliest'

```

现在，让我们看看这些数据，我们将使用 LIMIT 从句仅检索一行：

```
ksql> SELECT text FROM twitter_raw LIMIT 1;
RT @rickastley: 30 years ago today I said I was Never Gonna Give You Up. I am a man of my word - Rick x https://t.co/VmbMQA6tQB
LIMIT reached for the partition.
Query terminated
ksql>

```

现在，让我们使用刚刚定义和可用的推文内容的全部数据重新定义该流：

```
ksql> DROP stream twitter_raw;
Message
--------------------------------
Source TWITTER_RAW was dropped

ksql> CREATE STREAM twitter_raw (CreatedAt bigint,Id bigint, Text VARCHAR, SOURCE VARCHAR, Truncated VARCHAR, InReplyToStatusId VARCHAR, InReplyToUserId VARCHAR, InReplyToScreenName VARCHAR, GeoLocation VARCHAR, Place VARCHAR, Favorited VARCHAR, Retweeted VARCHAR, FavoriteCount VARCHAR, User VARCHAR, Retweet VARCHAR, Contributors VARCHAR, RetweetCount VARCHAR, RetweetedByMe VARCHAR, CurrentUserRetweetId VARCHAR, PossiblySensitive VARCHAR, Lang VARCHAR, WithheldInCountries VARCHAR, HashtagEntities VARCHAR, UserMentionEntities VARCHAR, MediaEntities VARCHAR, SymbolEntities VARCHAR, URLEntities VARCHAR) WITH (KAFKA_TOPIC='twitter_json_01',VALUE_FORMAT='JSON');
Message
----------------
Stream created

ksql>

```

现在，我们可以操作和检查更多的最近的数据，使用一般的 SQL 查询：

```
ksql> SELECT TIMESTAMPTOSTRING(CreatedAt, 'yyyy-MM-dd HH:mm:ss.SSS') AS CreatedAt,\
EXTRACTJSONFIELD(user,'$.ScreenName') as ScreenName,Text \
FROM twitter_raw \
WHERE LCASE(hashtagentities) LIKE '%oow%' OR \
LCASE(hashtagentities) LIKE '%ksql%';

2017-09-29 13:59:58.000 | rmoff | Looking forward to talking all about @apachekafka & @confluentinc’s #KSQL at #OOW17 on Sunday 13:45 https://t.co/XbM4eIuzeG

```

注意这里没有 LIMIT 从句，因此，你将在屏幕上看到 “continuous query” 的结果。不像关系型数据表中返回一个确定数量结果的查询，一个持续查询会运行在无限的流式数据上， 因此，它总是可能返回更多的记录。点击 Ctrl-C 去中断然后返回到 KSQL 提示符。在以上的查询中我们做了一些事情：

* **TIMESTAMPTOSTRING** 将时间戳从 epoch 格式转换到人类可读格式。（LCTT 译注： epoch 指的是一个特定的时间 1970-01-01 00:00:00 UTC）
* **EXTRACTJSONFIELD** 来展示数据源中嵌套的用户域中的一个字段，它看起来像：

```
{
"CreatedAt": 1506570308000,
"Text": "RT @gwenshap: This is the best thing since partitioned bread :) https://t.co/1wbv3KwRM6",
[...]
"User": {
    "Id": 82564066,
    "Name": "Robin Moffatt \uD83C\uDF7B\uD83C\uDFC3\uD83E\uDD53",
    "ScreenName": "rmoff",
    [...]

```
* 应用断言去展示内容，对 #（hashtag）使用模式匹配， 使用 LCASE 去强制小写字母。（LCTT 译注：hashtag 是twitter 中用来标注线索主题的标签）

关于支持的函数列表，请查看 [KSQL 文档](https://github.com/confluentinc/ksql/blob/0.1.x/docs/syntax-reference.md)。

我们可以创建一个从这个数据中得到的流：

```
ksql> CREATE STREAM twitter AS \
SELECT TIMESTAMPTOSTRING(CreatedAt, 'yyyy-MM-dd HH:mm:ss.SSS') AS CreatedAt,\
EXTRACTJSONFIELD(user,'$.Name') AS user_Name,\
EXTRACTJSONFIELD(user,'$.ScreenName') AS user_ScreenName,\
EXTRACTJSONFIELD(user,'$.Location') AS user_Location,\
EXTRACTJSONFIELD(user,'$.Description') AS  user_Description,\
Text,hashtagentities,lang \
FROM twitter_raw ;

Message
----------------------------
Stream created and running

ksql> DESCRIBE twitter;
Field            | Type
------------------------------------
ROWTIME          | BIGINT
ROWKEY           | VARCHAR(STRING)
CREATEDAT        | VARCHAR(STRING)
USER_NAME        | VARCHAR(STRING)
USER_SCREENNAME  | VARCHAR(STRING)
USER_LOCATION    | VARCHAR(STRING)
USER_DESCRIPTION | VARCHAR(STRING)
TEXT             | VARCHAR(STRING)
HASHTAGENTITIES  | VARCHAR(STRING)
LANG             | VARCHAR(STRING)
ksql>

```

并且查询这个得到的流：

```
ksql> SELECT CREATEDAT, USER_NAME, TEXT \
FROM TWITTER \
WHERE TEXT LIKE '%KSQL%';

2017-10-03 23:39:37.000 | Nicola Ferraro | RT @flashdba: Again, I'm really taken with the possibilities opened up by @confluentinc's KSQL engine #Kafka https://t.co/aljnScgvvs

```

### 聚合

在我们结束之前，让我们去看一下怎么去做一些聚合。

```
ksql> SELECT user_screenname, COUNT(*) \
FROM twitter WINDOW TUMBLING (SIZE 1 HOUR) \
GROUP BY user_screenname HAVING COUNT(*) > 1;

oracleace | 2
rojulman | 2
smokeinpublic | 2
ArtFlowMe | 2
[...]

```

你将可能得到满屏幕的结果；这是因为 KSQL 在每次给定的时间窗口更新时实际发出聚合值。因为我们设置 KSQL 去读取在主题上的全部消息（`SET 'auto.offset.reset' = 'earliest';`），它是一次性读取这些所有的消息并计算聚合更新。这里有一个微妙之处值得去深入研究。我们的入站推文流正好就是一个流。但是，现有它不能创建聚合，我们实际上是创建了一个表。一个表是在给定时间点的给定键的值的一个快照。 KSQL 聚合数据基于消息的事件时间，并且如果它更新了，通过简单的相关窗口重申去操作后面到达的数据。困惑了吗？ 我希望没有，但是，让我们看一下，如果我们可以用这个例子去说明。 我们将申明我们的聚合作为一个真实的表：

```
ksql> CREATE TABLE user_tweet_count AS \
SELECT user_screenname, count(*) AS  tweet_count \
FROM twitter WINDOW TUMBLING (SIZE 1 HOUR) \
GROUP BY user_screenname ;

Message
---------------------------
Table created and running

```

看表中的列，这里除了我们要求的外，还有两个隐含列：

```
ksql> DESCRIBE user_tweet_count;

Field           | Type
-----------------------------------
ROWTIME         | BIGINT
ROWKEY          | VARCHAR(STRING)
USER_SCREENNAME | VARCHAR(STRING)
TWEET_COUNT     | BIGINT
ksql>

```

我们看一下这些是什么：

```
ksql> SELECT TIMESTAMPTOSTRING(ROWTIME, 'yyyy-MM-dd HH:mm:ss.SSS') , \
ROWKEY, USER_SCREENNAME, TWEET_COUNT \
FROM user_tweet_count \
WHERE USER_SCREENNAME= 'rmoff';

2017-09-29 11:00:00.000 | rmoff : Window{start=1506708000000 end=-} | rmoff | 2
2017-09-29 12:00:00.000 | rmoff : Window{start=1506711600000 end=-} | rmoff | 4
2017-09-28 22:00:00.000 | rmoff : Window{start=1506661200000 end=-} | rmoff | 2
2017-09-29 09:00:00.000 | rmoff : Window{start=1506700800000 end=-} | rmoff | 4
2017-09-29 15:00:00.000 | rmoff : Window{start=1506722400000 end=-} | rmoff | 2
2017-09-29 13:00:00.000 | rmoff : Window{start=1506715200000 end=-} | rmoff | 6

```

`ROWTIME` 是窗口开始时间， `ROWKEY` 是 `GROUP BY`（`USER_SCREENNAME`）加上窗口的组合。因此，我们可以通过创建另外一个衍生的表来整理一下：

```
ksql> CREATE TABLE USER_TWEET_COUNT_DISPLAY AS \
SELECT TIMESTAMPTOSTRING(ROWTIME, 'yyyy-MM-dd HH:mm:ss.SSS') AS WINDOW_START ,\
USER_SCREENNAME, TWEET_COUNT \
FROM user_tweet_count;

Message
---------------------------
Table created and running

```

现在它更易于查询和查看我们感兴趣的数据：

```
ksql> SELECT WINDOW_START ,  USER_SCREENNAME, TWEET_COUNT \
FROM USER_TWEET_COUNT_DISPLAY WHERE TWEET_COUNT> 20;

2017-09-29 12:00:00.000 | VikasAatOracle | 22
2017-09-28 14:00:00.000 | Throne_ie | 50
2017-09-28 14:00:00.000 | pikipiki_net | 22
2017-09-29 09:00:00.000 | johanlouwers | 22
2017-09-28 09:00:00.000 | yvrk1973 | 24
2017-09-28 13:00:00.000 | cmosoares | 22
2017-09-29 11:00:00.000 | ypoirier | 24
2017-09-28 14:00:00.000 | pikisec | 22
2017-09-29 07:00:00.000 | Throne_ie | 22
2017-09-29 09:00:00.000 | ChrisVoyance | 24
2017-09-28 11:00:00.000 | ChrisVoyance | 28

```

### 结论

所以我们有了它！ 我们可以从 Kafka 中取得数据， 并且很容易使用 KSQL 去探索它。 而不仅是去浏览和转换数据，我们可以很容易地使用 KSQL 从流和表中建立流处理。

![](/data/attachment/album/201711/03/230317ly8bg2k2es9suyy6.png)

如果你对 KSQL 能够做什么感兴趣，去查看：

* [KSQL 公告](https://www.confluent.io/blog/ksql-open-source-streaming-sql-for-apache-kafka/)
* [我们最近的 KSQL 在线研讨会](https://www.confluent.io/online-talk/ksql-streaming-sql-for-apache-kafka/) 和 [Kafka 峰会讲演](https://www.confluent.io/kafka-summit-sf17/Databases-and-Stream-Processing-1)
* [clickstream 演示](https://www.youtube.com/watch?v=A45uRzJiv7I)，它是 [KSQL 的 GitHub 仓库](https://github.com/confluentinc/ksql) 的一部分
* [我最近做的演讲](https://speakerdeck.com/rmoff/look-ma-no-code-building-streaming-data-pipelines-with-apache-kafka) 展示了 KSQL 如何去支持基于流的 ETL 平台

记住，KSQL 现在正处于开发者预览阶段。 欢迎在 KSQL 的 GitHub 仓库上提出任何问题， 或者去我们的 [community Slack group](https://slackpass.io/confluentcommunity) 的 #KSQL 频道。

---

via: <https://www.confluent.io/blog/using-ksql-to-analyse-query-and-transform-data-in-kafka>

作者：[Robin Moffatt](https://www.confluent.io/blog/author/robin/) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


开发者，原来你最讨厌的编程语言和技术是……

Summary: 原来 Perl、PHP 已经到了这个地步……

引战贴

原来 Perl、PHP 已经到了这个地步……

![](/data/attachment/album/201711/05/100744xl8tk2bmz811b6z1.png)

在 [Stack Overflow Jobs](https://stackoverflow.com/jobs) 上，你可以创建你自己的<ruby> <a href="https://stackoverflow.blog/2016/10/11/bye-bye-bullets-the-stack-overflow-developer-story-is-the-new-technical-resume/">  开发者故事 </a> <rp>  （ </rp> <rt>  Developer Story </rt> <rp>  ） </rp></ruby>来展示你的成就，表现你的职业生涯进步。在创建开发者故事时，你可以对你使用的技术/编程语言添加喜欢或**不喜欢**的标签，如下图：

![你可以对你使用的技术/编程语言添加喜欢或不喜欢的标签](/data/attachment/album/201711/05/100746v5am47q5z5fagmqg.png)

这就给了我们一个机会可以观察到这数十万开发者的喜好和厌恶。有许多方法可以评估一个语言的流行程度，举个栗子说，我们经常使用 [Stack Overflow 访问数或问题查看数](/article-8865-1.html)来评估这样的趋势。但是，当技术人员在他们的简历中表达他们**不喜欢**某种技术时，这个数据集就是一个找出技术人群不喜欢某种技术的独有方式。

（我[两年前曾经在我的个人博客](http://varianceexplained.org/r/polarizing-technologies/)上发表过一些这类分析，不过这篇文章使用了更新的数据集，以及有更多可视化结果和说明。）

### 编程语言

作为测量每个编程语言有多流行的指标，我们将看看它出现在某人“不喜欢”标签的时间与其出现在其他人的“喜欢”或“不喜欢”标签的频率相比。那么 50% 就意味着该语言喜欢与不喜欢各占一半，而 1% 则意味着 99 个人喜欢它而剩下 1 个人不喜欢它。（我们使用了[这篇文章](http://varianceexplained.org/r/empirical_bayes_baseball/)中描述的<ruby> 经验贝叶斯 <rp>  （ </rp> <rt>  empirical Bayes </rt> <rp>  ） </rp></ruby>方法来计算平均值，并使用[这个方法](http://varianceexplained.org/r/credible_intervals_baseball/)来计算得到 95% 置信区间）

让我们开始看看选出的语言列表（而不是像 Android 这样的平台或像 jQuery 这样的库），所有这些都曾在开发者故事中至少提及了 2000 次以上。

![每个语言有多不招人喜欢](/data/attachment/album/201711/05/100746zdm37arhal7ktarz.png)

最不喜欢的语言是 Perl、Delphi 和 VBA ，它们远远把其它语言抛下。接着的第二梯队是 PHP、Objective-C、 Coffeescript 和 Ruby。我们的团队很高兴地看到，R 语言相对于喜欢它的人数来说，对它不喜欢的人数是最少的。

如果你读过我们另外一些关于编程语言增长或萎缩的文章，你也许会注意到那些较少被不喜欢的语言往往是增长较快的。 在 Stack Overflow 上，R、Python、Typescript、Go 和 Rust 全是快速增长的编程语言（我们之前专门对 [Python](/article-8865-1.html) 和 [R](https://stackoverflow.blog/2017/10/10/impressive-growth-r/) 做过分析），而且它们全都属于看法比较<ruby> 分化 <rp>  （ </rp> <rt>  polarizing </rt> <rp>  ） </rp></ruby>的语言。类似的，大量萎缩的语言，比如 Perl、Objective-C 和 Ruby，如我们[之前观察](https://stackoverflow.blog/2017/08/01/flash-dead-technologies-might-next/)到的那样，在我们网站上它处于快速萎缩状况。

我们可以通过将每种语言的规模和增长与不喜欢它的人的百分比进行比较来进行调查，橙色点代表最不喜欢的语言。 为了使我们的分析与前几个帖子保持一致，我们将统计数据限制在高收入国家（如美国，英国，德国和加拿大）。

![语言的增长率与对该语言的不喜欢进行对比](/data/attachment/album/201711/05/100748ylwxicsoqc5lxlbs.png)

一般来说，编程语言的增长率和它有多不招人喜欢方面存在相关性。几乎每个在开发者故事中提及不喜欢的比率超过 3% 的语言都在 Stack Overflow 流量上处于萎缩状态（除了十分两极化的 VBA，它仍有轻度增长）。而不喜欢数量较少的语言，如 R、Rust、 Typescript 和 Kotlin，它们全处于快速增长领域（Typescript 和 Kotlin 增长的太快，以至于都跑出了上图范围）。

一个突出的编程语言是函数式编程语言 Clojure；几乎没有人表示过不喜欢它，但是它仍然处于快速萎缩中（根据[问题查看情况](https://insights.stackoverflow.com/trends?tags=clojure)，它在去年才开始萎缩）。另外一个例外是 MATLAB，它处于快速萎缩，但是没有很多人表示过不喜欢它。这可能是由于调查样本的数据所限：任何一个 Web 开发人员都可能对 PHP、C# 或 Ruby 有意见，但是不从事数据分析的人没理由对 MATLAB 不满意。（这也可能是 R 较少被提及“不喜欢”的部分原因）

我们不一定说这里存在因果关系——部分程序员不喜欢就会导致该语言会被抛弃。 另一种可能是，如果人们觉得这种语言已经越来越不流行，那么人们就会觉得很自然地表达他们也不喜欢了。 同样可以想象的是，开发人员经常使用这个字段来记录他们曾经使用的技术，但是不会再使用该技术了。 这将导致那些自然而然地“被替代的”技术就一直停留在“不喜欢”标签里面。

### 最不喜欢的和最喜欢的

上面的分析仅考虑了编程语言，不涉及操作系统、平台或库（框架）。那么就整体而言，最不喜欢的技术是什么？为了专注于我们有足够数据的更主要的技术，我们其限制为至少提及了 1000 次的技术。

![最不喜欢的技术](/data/attachment/album/201711/05/100748zrgpr9vpuav14rv2.png)

最不喜欢的其中有几个是微软的技术，特别是 IE 和 VB，以及 “微软” 标签（“评估” 也出现在这个列表中，但是没那么糟糕）。[有个好消息是](https://stackoverflow.blog/2017/08/01/flash-dead-technologies-might-next/)，大多数人都不喜欢 Flash。此外，较老的语言，比如 COBOL、 Fortran 和 Pascal 也出现在此处。

值得再次强调的是，这不是对技术及其品质或受欢迎程度的批评。 这只是衡量哪些技术激起了强烈的消极情绪，至少在一部分愿意公开分享其感受的开发人员中如此。

我们也集中观察了那些最流行的技术、那些最不可能被不喜欢的技术（这次，由于喜欢标签出现的比较多，我们仅关注那些被提及至少 10000 次的）。

![哪些技术最令人喜欢](/data/attachment/album/201711/05/100749nkhee9m04jme08el.png)

Git 也许是许多开发者的沮丧源头（绝对包括我！），但人们很少在简历中承认这一点，这是因为它是我们的开发者故事中最受欢迎的标签之一。 R 也出现在了这个列表，但它并不是唯一一个没有争议的与数据科学相关的语言。 机器学习被 23000 人所喜欢，而且很少被人不喜欢。 诸如 Python-3.X、CSS3 和 HTML5 等标签可能表明开发者很少指定他们不喜欢技术的特定版本（如果他们会指定的话）。 当然，[jQuery 在 Stack Overflow 上一直是如此受欢迎](http://i.stack.imgur.com/ssRUr.gif)。

### 分化的标签网络

我们可以把所有这些标签组合成一个网络。在最近的一篇文章中，[Julia Silge 展示了我们如何构建一个代表整个软件生态系统的技术网络](https://stackoverflow.blog/2017/10/03/mapping-ecosystems-software-development/)。 如果我们根据每个标签不喜欢的程度对节点进行着色，我们可以了解该生态系统的哪些部分比其它部分更有争议。

![分化的标签网络](/data/attachment/album/201711/05/100751enf9qdfvfrx8ukcu.png)

通过将开发者故事的标签放置到次生态系统中，该网络揭示了哪些类型的标签趋于两级分化。 在微软（以 C＃ 和 .NET 为中心，左上角），PHP（与 WordPress 和 Drupal 一起，左下角）以及移动开发（特别是 Objective-C，右下角）的子生态系统中都有一些意见分化的标签聚合。 在操作系统聚合中（右下），我们可以看到诸如 OSX 之类的系统，以及特别是 Windows 都有不喜欢的人，但是像 Linux、Ubuntu 和 Unix 这样的标签却没有。

### 竞争

如果某人喜欢某个特定的标签，是否意味着通常他们喜欢或不喜欢另外的标签呢？

我们可以使用出现在特定喜欢标签之间的 [phi 系数](https://en.wikipedia.org/wiki/Phi_coefficient)来衡量它。 （当计算这些相关性时，我们只考虑那些至少有一个不喜欢标签的人。）

![技术竞争程度](/data/attachment/album/201711/05/100751r2bbx0arkbxl2qwc.png)

这突出显示了软件生态系统的一些“竞争”：Linux 和 OSX vs Windows，Git vs SVN，vim vs emacs 以及（对我来说）R vs SAS。 这些配对中的大多数并不代表“相反”的技术，而是反映了两种解决类似问题的方法。 它们中的许多表明了从以前流行的技术发展到更现代的技术（SVN 由 Git 取代，XML 由 JSON 取代，VB 由 C＃ 取代）。 这对于人们想在简历中列出的内容是有意义的；开发者通常会表明他们不愿意使用他们认为过时的东西。

### 总结

我对“语言战争”没有任何兴趣，我也不会对用户分享的喜欢或不喜欢的任何技术进行裁断。 对微软技术的两级分化的看法通常会鼓励我分享我的个人经验。 我是一个 Mac 和 UNIX 终身拥趸，几乎我所有的大学和研究生的编程学习都围绕着 Python 和 R。尽管如此，我很高兴能够加入一个 .NET 栈的公司，我很高兴我来了 —— 因为我喜欢这个团队、产品和数据。 我不能代表其他人说话，但我很高兴自己可以从事于自己想做的事情，而不是那些不想要做的事情。

如果您有兴趣分享您喜欢和不喜欢的技术，并想找到您职业生涯的下一份工作，那么您可以[创建自己的开发者故事](http://stackoverflow.com/users/story/join)。

想找一份你喜欢的技术的工作？ 在 [Stack Overflow Jobs](https://stackoverflow.com/jobs) 找到你的下一份工作，在那里你可以搜索你喜欢做的技术工作。


Postgres 索引类型探索之旅

![](/data/attachment/album/201711/07/003224iotte1erl0tsgl3p.jpg)

在 Citus 公司，为让事情做的更好，我们与客户一起在数据建模、优化查询、和增加 [索引](https://www.citusdata.com/blog/2017/10/11/index-all-the-things-in-postgres/)上花费了许多时间。我的目标是为客户的需求提供更好的服务，从而创造成功。我们所做的其中一部分工作是[持续](https://www.citusdata.com/product/cloud)为你的 Citus 集群保持良好的优化和 [高性能](https://www.citusdata.com/blog/2017/09/29/what-performance-can-you-expect-from-postgres/)；另外一部分是帮你了解关于 Postgres 和 Citus 你所需要知道的一切。毕竟，一个健康和高性能的数据库意味着 app 执行的更快，并且谁不愿意这样呢？ 今天，我们简化一些内容，与客户分享一些关于 Postgres 索引的信息。

Postgres 有几种索引类型， 并且每个新版本都似乎增加一些新的索引类型。每个索引类型都是有用的，但是具体使用哪种类型取决于（1）数据类型，有时是（2）表中的底层数据和（3）执行的查找类型。接下来的内容我们将介绍在 Postgres 中你可以使用的索引类型，以及你何时该使用何种索引类型。在开始之前，这里有一个我们将带你亲历的索引类型列表：

* B-Tree
* <ruby> 倒排索引 <rt>  Generalized Inverted Index </rt></ruby> (GIN)
* <ruby> 倒排搜索树 <rt>  Generalized Inverted Seach Tree </rt></ruby> (GiST)
* <ruby> 空间分区的 <rt>  Space partitioned </rt></ruby> GiST (SP-GiST)
* <ruby> 块范围索引 <rt>  Block Range Index </rt></ruby> (BRIN)
* Hash

现在开始介绍索引。

### 在 Postgres 中，B-Tree 索引是你使用的最普遍的索引

如果你有一个计算机科学的学位，那么 B-Tree 索引可能是你学会的第一个索引。[B-tree 索引](https://en.wikipedia.org/wiki/B-tree) 会创建一个始终保持自身平衡的一棵树。当它根据索引去查找某个东西时，它会遍历这棵树去找到键，然后返回你要查找的数据。使用索引是大大快于顺序扫描的，因为相对于顺序扫描成千上万的记录，它可以仅需要读几个 [页](https://www.8kdata.com/blog/postgresql-page-layout/) (当你仅返回几个记录时)。

如果你运行一个标准的 `CREATE INDEX` 语句，它将为你创建一个 B-tree 索引。 B-tree 索引在大多数的数据类型上是很有价值的，比如文本、数字和时间戳。如果你刚开始在你的数据库中使用索引，并且不在你的数据库上使用太多的 Postgres 的高级特性，使用标准的 B-Tree 索引可能是你最好的选择。

### GIN 索引，用于多值列

<ruby> 倒排索引 <rt>  Generalized Inverted Index </rt></ruby>，一般称为 [GIN](https://www.postgresql.org/docs/10/static/gin.html)，大多适用于当单个列中包含多个值的数据类型。

据 Postgres 文档：

>
> “GIN 设计用于处理被索引的条目是复合值的情况，并且由索引处理的查询需要搜索在复合条目中出现的值。例如，这个条目可能是文档，查询可以搜索文档中包含的指定字符。”
>
>
>

包含在这个范围内的最常见的数据类型有：

* [hStore](https://www.citusdata.com/blog/2016/07/14/choosing-nosql-hstore-json-jsonb/)
* Array
* Range
* [JSONB](https://www.citusdata.com/blog/2016/07/14/choosing-nosql-hstore-json-jsonb/)

关于 GIN 索引中最让人满意的一件事是，它们能够理解存储在复合值中的数据。但是，因为一个 GIN 索引需要有每个被添加的单独类型的数据结构的特定知识，因此，GIN 索引并不是支持所有的数据类型。

### GiST 索引， 用于有重叠值的行

<ruby> 倒排搜索树 <rt>  Generalized Inverted Seach Tree </rt></ruby>（GiST）索引多适用于当你的数据与同一列的其它行数据重叠时。GiST 索引最好的用处是：如果你声明一个几何数据类型，并且你希望知道两个多边型是否包含一些点时。在一种情况中一个特定的点可能被包含在一个盒子中，而与此同时，其它的点仅存在于一个多边形中。使用 GiST 索引的常见数据类型有：

* 几何类型
* 需要进行全文搜索的文本类型

GiST 索引在大小上有很多的固定限制，否则，GiST 索引可能会变的特别大。作为其代价，GiST 索引是有损的（不精确的）。

据官方文档：

>
> “GiST 索引是有损的，这意味着索引可能产生虚假匹配，所以需要去检查真实的表行去消除虚假匹配。 (当需要时 PostgreSQL 会自动执行这个动作)”
>
>
>

这并不意味着你会得到一个错误结果，它只是说明了在 Postgres 给你返回数据之前，会做了一个很小的额外工作来过滤这些虚假结果。

特别提示：同一个数据类型上 GIN 和 GiST 索引往往都可以使用。通常一个有很好的性能表现，但会占用很大的磁盘空间，反之亦然。说到 GIN 与 GiST 的比较，并没有某个完美的方案可以适用所有情况，但是，以上规则应用于大部分常见情况。

### SP-GiST 索引，用于更大的数据

空间分区 GiST （SP-GiST）索引采用来自 [Purdue](https://www.cs.purdue.edu/spgist/papers/W87R36P214137510.pdf) 研究的空间分区树。 SP-GiST 索引经常用于当你的数据有一个天然的聚集因素，并且不是一个平衡树的时候。 电话号码是一个非常好的例子 (至少 US 的电话号码是)。 它们有如下的格式：

* 3 位数字的区域号
* 3 位数字的前缀号 (与以前的电话交换机有关)
* 4 位的线路号

这意味着第一组前三位处有一个天然的聚集因素，接着是第二组三位，然后的数字才是一个均匀的分布。但是，在电话号码的一些区域号中，存在一个比其它区域号更高的饱合状态。结果可能导致树非常的不平衡。因为前面有一个天然的聚集因素，并且数据不对等分布，像电话号码一样的数据可能会是 SP-GiST 的一个很好的案例。

### BRIN 索引， 用于更大的数据

块范围索引（BRIN）专注于一些类似 SP-GiST 的情形，它们最好用在当数据有一些自然排序，并且往往数据量很大时。如果有一个以时间为序的 10 亿条的记录，BRIN 也许就能派上用场。如果你正在查询一组很大的有自然分组的数据，如有几个邮编的数据，BRIN 能帮你确保相近的邮编存储在磁盘上相近的地方。

当你有一个非常大的比如以日期或邮编排序的数据库， BRIN 索引可以让你非常快的跳过或排除一些不需要的数据。此外，与整体数据量大小相比，BRIN 索引相对较小，因此，当你有一个大的数据集时，BRIN 索引就可以表现出较好的性能。

### Hash 索引， 总算不怕崩溃了

Hash 索引在 Postgres 中已经存在多年了，但是，在 Postgres 10 发布之前，对它们的使用一直有个巨大的警告，它不是 WAL-logged 的。这意味着如果你的服务器崩溃，并且你无法使用如 [wal-g](https://www.citusdata.com/blog/2017/08/18/introducing-wal-g-faster-restores-for-postgres/) 故障转移到备机或从存档中恢复，那么你将丢失那个索引，直到你重建它。 随着 Postgres 10 发布，它们现在是 WAL-logged 的，因此，你可以再次考虑使用它们 ，但是，真正的问题是，你应该这样做吗?

Hash 索引有时会提供比 B-Tree 索引更快的查找，并且创建也很快。最大的问题是它们被限制仅用于“相等”的比较操作，因此你只能用于精确匹配的查找。这使得 hash 索引的灵活性远不及通常使用的 B-Tree 索引，并且，你不能把它看成是一种替代品，而是一种用于特殊情况的索引。

### 你该使用哪个？

我们刚才介绍了很多，如果你有点被吓到，也很正常。 如果在你知道这些之前， `CREATE INDEX` 将始终为你创建使用 B-Tree 的索引，并且有一个好消息是，对于大多数的数据库， Postgres 的性能都很好或非常好。 :) 如果你考虑使用更多的 Postgres 特性，下面是一个当你使用其它 Postgres 索引类型的备忘清单：

* B-Tree - 适用于大多数的数据类型和查询
* GIN - 适用于 JSONB/hstore/arrays
* GiST - 适用于全文搜索和几何数据类型
* SP-GiST - 适用于有天然的聚集因素但是分布不均匀的大数据集
* BRIN - 适用于有顺序排列的真正的大数据集
* Hash - 适用于相等操作，而且，通常情况下 B-Tree 索引仍然是你所需要的。

如果你有关于这篇文章的任何问题或反馈，欢迎加入我们的 [slack channel](https://slack.citusdata.com/)。

---

via: <https://www.citusdata.com/blog/2017/10/17/tour-of-postgres-index-types/>

作者：[Craig Kerstiens](https://www.citusdata.com/blog/2017/10/17/tour-of-postgres-index-types/) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


为何 Kubernetes 如此受欢迎？

Summary: Google 开发的这个容器管理系统很快成为开源历史上最成功的案例之一。

Google 开发的这个容器管理系统很快成为开源历史上最成功的案例之一。

>
> Google 开发的这个容器管理系统很快成为开源历史上最成功的案例之一。
>
>
>

![](/data/attachment/album/201711/07/013606ont6zxs8znc9x94m.png)

[Kubernetes](https://kubernetes.io/) 是一个在过去几年中快速蹿升起来的开源的容器管理系统。它被众多行业中最大的企业用于关键任务，已成为开源方面最成功的案例之一。这是怎么发生的？该如何解释 Kubernetes 的广泛应用呢？

### Kubernetes 的背景：起源于 Google 的 Borg 系统

随着计算世界变得更加分布式、更加基于网络、以及更多的云计算，我们看到了大型的<ruby> 独石 <rt>  monolithic </rt></ruby>应用慢慢地转化为多个敏捷微服务。这些微服务能让用户单独缩放应用程序的关键功能，以处理数百万客户。除此之外，我们还看到像 Docker 这样的容器等技术出现在企业中，为用户快速构建这些微服务创造了一致的、可移植的、便捷的方式。

随着 Docker 继续蓬勃发展，管理这些微服务器和容器成为最重要的要求。这时已经运行基于容器的基础设施已经多年的 Google 大胆地决定开源一个名为 [Borg](http://queue.acm.org/detail.cfm?id=2898444) 的项目。Borg 系统是运行诸如 Google 搜索和 Gmail 这样的 Google 服务的关键。谷歌决定开源其基础设施为世界上任何一家公司创造了一种像顶尖公司一样运行其基础架构的方式。

### 最大的开源社区之一

在开源之后，Kubernetes 发现自己在与其他容器管理系统竞争，即 Docker Swarm 和 Apache Mesos。Kubernetes 近几个月来超过这些其他系统的原因之一得益于社区和系统背后的支持：它是最大的开源社区之一（GitHub 上超过 27,000 多个星标），有来自上千个组织（1,409 个贡献者）的贡献，并且被集中在一个大型、中立的开源基金会里，即[原生云计算基金会](https://www.cncf.io/)（CNCF）。

CNCF 也是更大的 Linux 基金会的一部分，拥有一些顶级企业成员，其中包括微软、谷歌和亚马逊。此外，CNCF 的企业成员队伍持续增长，SAP 和 Oracle 在过去几个月内加入白金会员。这些加入 CNCF 的公司，其中 Kubernetes 项目是前沿和中心的，这证明了有多少企业投注于社区来实现云计算战略的一部分。

Kubernetes 外围的企业社区也在激增，供应商提供了带有更多的安全性、可管理性和支持的企业版。Red Hat、CoreOS 和 Platform 9 是少数几个使企业级 Kubernetes 成为战略前进的关键因素，并投入巨资以确保开源项目继续得到维护的公司。

### 混合云带来的好处

企业以这样一个飞速的方式采用 Kubernetes 的另一个原因是 Kubernetes 可以在任何云端工作。大多数企业在现有的内部数据中心和公共云之间共享资产，对混合云技术的需求至关重要。

Kubernetes 可以部署在公司先前存在的数据中心内、任意一个公共云环境、甚至可以作为服务运行。由于 Kubernetes 抽象了底层基础架构层，开发人员可以专注于构建应用程序，然后将它们部署到任何这些环境中。这有助于加速公司的 Kubernetes 采用，因为它可以在内部运行 Kubernetes，同时继续构建云战略。

### 现实世界的案例

Kubernetes 继续增长的另一个原因是，大型公司正在利用这项技术来解决业界最大的挑战。Capital One、Pearson Education 和 Ancestry.com 只是少数几家公布了 Kubernetes [使用案例](https://kubernetes.io/case-studies/)的公司。

[Pokemon Go](https://cloudplatform.googleblog.com/2016/09/bringing-Pokemon-GO-to-life-on-Google-Cloud.html) 是最流行的宣传 Kubernetes 能力的使用案例。在它发布之前，人们都觉得在线多人游戏会相当的得到追捧。但当它一旦发布，就像火箭一样起飞，达到了预期流量的 50 倍。通过使用 Kubernetes 作为 Google Cloud 之上的基础设施层，Pokemon Go 可以大规模扩展以满足意想不到的需求。

最初作为来自 Google 的开源项目，背后有 Google 15 年的服务经验和来自 Borg 的继承- Kubernetes 现在是有许多企业成员的大型基金会（CNCF）的一部分。它继续受到欢迎，并被广泛应用于金融、大型多人在线游戏（如 Pokemon Go）以及教育公司和传统企业 IT 的关键任务中。考虑到所有，所有的迹象表明，Kubernetes 将继续更加流行，并仍然是开源中最大的成功案例之一。

---

作者简介：

Anurag Gupta - Anurag Gupta 是推动统一日志层 Fluentd Enterprise 发展的 Treasure Data 的产品经理。 Anurag 致力于大型数据技术，包括 Azure Log Analytics 和如 Microsoft System Center 的企业 IT 服务。

---

via: <https://opensource.com/article/17/10/why-kubernetes-so-popular>

作者：[Anurag Gupta](https://opensource.com/users/anuraggupta) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


2017 年哪个公司对开源贡献最多？让我们用 GitHub 的数据分析下

Summary: 微软排名第一、谷歌排名第二。

微软排名第一、谷歌排名第二。

![](/data/attachment/album/201711/07/122034hfecf9ehgzm9b8gr.png)

在这篇分析报告中，我们将使用 2017 年度截止至当前时间（2017 年 10 月）为止，GitHub 上所有公开的推送事件的数据。对于每个 GitHub 用户，我们将尽可能地猜测其所属的公司。此外，我们仅查看那些今年得到了至少 20 个星标的仓库。

以下是我的报告结果，你也可以[在我的交互式 Data Studio 报告上进一步加工](https://datastudio.google.com/open/0ByGAKP3QmCjLU1JzUGtJdTlNOG8)。

### 顶级云服务商的比较

2017 年它们在 GitHub 上的表现：

* 微软看起来约有 1300 名员工积极地推送代码到 GitHub 上的 825 个顶级仓库。
* 谷歌显示出约有 900 名员工在 GitHub 上活跃，他们推送代码到大约 1100 个顶级仓库。
* 亚马逊似乎只有 134 名员工活跃在 GitHub 上，他们推送代码到仅仅 158 个顶级项目上。
* 不是所有的项目都一样：在超过 25% 的仓库上谷歌员工要比微软员工贡献的多，而那些仓库得到了更多的星标（53 万对比 26 万）。亚马逊的仓库 2017 年合计才得到了 2.7 万个星标。

![](/data/attachment/album/201711/07/122034f5tw99h51y5hddsh.png)

### 红帽、IBM、Pivotal、英特尔和 Facebook

如果说亚马逊看起来被微软和谷歌远远抛在了身后，那么这之间还有哪些公司呢？根据这个排名来看，红帽、Pivotal 和英特尔在 GitHub 上做出了巨大贡献：

注意，下表中合并了所有的 IBM 地区域名（各个地区会展示在其后的表格中）。

![](/data/attachment/album/201711/07/122035k4yh11ae70jpd76y.png)

![](/data/attachment/album/201711/07/122035ic481mfat9m8c0mt.png)

Facebook 和 IBM（美）在 GitHub 上的活跃用户数同亚马逊差不多，但是它们所贡献的项目得到了更多的星标（特别是 Facebook）：

![](/data/attachment/album/201711/07/122036ceqobjegdrsczqek.png)

接下来是阿里巴巴、Uber 和 Wix：

![](/data/attachment/album/201711/07/122037ii5z910fivatzilt.png)

以及 GitHub 自己、Apache 和腾讯：

![](/data/attachment/album/201711/07/122037vyzi65o220zw062w.png)

百度、苹果和 Mozilla：

![](/data/attachment/album/201711/07/122038i9v2uv410001peh0.png)

（LCTT 译注：很高兴看到国内的顶级互联网公司阿里巴巴、腾讯和百度在这里排名前列！）

甲骨文、斯坦福大学、麻省理工、Shopify、MongoDb、伯克利大学、VmWare、Netflix、Salesforce 和 Gsa.gov：

![](/data/attachment/album/201711/07/122038vvojdjnor5me76nn.png)

LinkedIn、Broad Institute、Palantir、雅虎、MapBox、Unity3d、Automattic（WordPress 的开发商）、Sandia、Travis-ci 和 Spotify：

![](/data/attachment/album/201711/07/122039s229620c94o2a2h2.png)

Chromium、UMich、Zalando、Esri、IBM (英)、SAP、EPAM、Telerik、UK Cabinet Office 和 Stripe：

![](/data/attachment/album/201711/07/122040uy9sc26ch2ccnty3.png)

Cern、Odoo、Kitware、Suse、Yandex、IBM (加)、Adobe、AirBnB、Chef 和 The Guardian：

![](/data/attachment/album/201711/07/122040my0ciooyucyciivo.png)

Arm、Macports、Docker、Nuxeo、NVidia、Yelp、Elastic、NYU、WSO2、Mesosphere 和 Inria：

![](/data/attachment/album/201711/07/122041fno0e8fe8gl0h0f8.png)

Puppet、斯坦福（计算机科学）、DatadogHQ、Epfl、NTT Data 和 Lawrence Livermore Lab：

![](/data/attachment/album/201711/07/122042z8u12010u3930miy.png)

### 我的分析方法

#### 我是怎样将 GitHub 用户关联到其公司的

在 GitHub 上判定每个用户所属的公司并不容易，但是我们可以使用其推送事件的提交消息中展示的邮件地址域名来判断。

* 同样的邮件地址可以出现在几个用户身上，所以我仅考虑那些对此期间获得了超过 20 个星标的项目进行推送的用户。
* 我仅统计了在此期间推送超过 3 次的 GitHub 用户。
* 用户推送代码到 GitHub 上可以在其推送中显示许多不同的邮件地址，这部分是由 GIt 工作机制决定的。为了判定每个用户的组织，我会查找那些在推送中出现更频繁的邮件地址。
* 不是每个用户都在 GitHub 上使用其组织的邮件。有许多人使用 gmail.com、users.noreply.github.com 和其它邮件托管商的邮件地址。有时候这是为了保持匿名和保护其公司邮箱，但是如果我不能定位其公司域名，这些用户我就不会统计。抱歉。
* 有时候员工会更换所任职的公司。我会将他们分配给其推送最多的公司。

#### 我的查询语句

```
#standardSQL
WITH
period AS (
  SELECT *
  FROM `githubarchive.month.2017*` a
),
repo_stars AS (
  SELECT repo.id, COUNT(DISTINCT actor.login) stars, APPROX_TOP_COUNT(repo.name, 1)[OFFSET(0)].value repo_name
  FROM period
  WHERE type='WatchEvent'
  GROUP BY 1
  HAVING stars>20
),
pushers_guess_emails_and_top_projects AS (
  SELECT *
    # , REGEXP_EXTRACT(email, r'@(.*)') domain
    , REGEXP_REPLACE(REGEXP_EXTRACT(email, r'@(.*)'), r'.*.ibm.com', 'ibm.com') domain
  FROM (
    SELECT actor.id
      , APPROX_TOP_COUNT(actor.login,1)[OFFSET(0)].value login
      , APPROX_TOP_COUNT(JSON_EXTRACT_SCALAR(payload, '$.commits[0].author.email'),1)[OFFSET(0)].value email
      , COUNT(*) c
      , ARRAY_AGG(DISTINCT TO_JSON_STRING(STRUCT(b.repo_name,stars))) repos
    FROM period a
    JOIN repo_stars b
    ON a.repo.id=b.id
    WHERE type='PushEvent'
    GROUP BY  1
    HAVING c>3
  )
)
SELECT * FROM (
  SELECT domain
    , githubers
    , (SELECT COUNT(DISTINCT repo) FROM UNNEST(repos) repo) repos_contributed_to
    , ARRAY(
        SELECT AS STRUCT JSON_EXTRACT_SCALAR(repo, '$.repo_name') repo_name
        , CAST(JSON_EXTRACT_SCALAR(repo, '$.stars') AS INT64) stars
        , COUNT(*) githubers_from_domain FROM UNNEST(repos) repo
        GROUP BY 1, 2
        HAVING githubers_from_domain>1
        ORDER BY stars DESC LIMIT 3
      ) top
    , (SELECT SUM(CAST(JSON_EXTRACT_SCALAR(repo, '$.stars') AS INT64)) FROM (SELECT DISTINCT repo FROM UNNEST(repos) repo)) sum_stars_projects_contributed_to
  FROM (
    SELECT domain, COUNT(*) githubers, ARRAY_CONCAT_AGG(ARRAY(SELECT * FROM UNNEST(repos) repo)) repos
    FROM pushers_guess_emails_and_top_projects
    #WHERE domain IN UNNEST(SPLIT('google.com|microsoft.com|amazon.com', '|'))
    WHERE domain NOT IN UNNEST(SPLIT('gmail.com|users.noreply.github.com|qq.com|hotmail.com|163.com|me.com|googlemail.com|outlook.com|yahoo.com|web.de|iki.fi|foxmail.com|yandex.ru', '|')) # email hosters
    GROUP BY 1
    HAVING githubers > 30
  )
  WHERE (SELECT MAX(githubers_from_domain) FROM (SELECT repo, COUNT(*) githubers_from_domain FROM UNNEST(repos) repo  GROUP BY repo))>4 # second filter email hosters
)
ORDER BY githubers DESC

```

### FAQ

#### 有的公司有 1500 个仓库，为什么只统计了 200 个？有的仓库有 7000 个星标，为什么只显示 1500 个？

我进行了过滤。我只统计了 2017 年的星标。举个例子说，Apache 在 GitHub 上有超过 1500 个仓库，但是今年只有 205 个项目得到了超过 20 个星标。

![](/data/attachment/album/201711/07/122042uyvt7yvt0y5vvqv3.png)

![](/data/attachment/album/201711/07/122043ydlz8fk0coaokrlk.png)

#### 这表明了开源的发展形势么？

注意，这个对 GitHub 的分析没有包括像 Android、Chromium、GNU、Mozilla 等顶级社区，也没有包括 Apache 基金会或 Eclipse 基金会，还有一些[其它](https://developers.google.com/open-source/organizations)项目选择在 GitHub 之外开展起活动。

#### 这对于我的组织不公平

我只能统计我所看到的数据。欢迎对我的统计的前提提出意见，以及对我的统计方法给出改进方法。如果有能用的查询语句就更好了。

举个例子，要看看当我合并了 IBM 的各个地区域名到其顶级域时排名发生了什么变化，可以用一条 SQL 语句解决：

```
SELECT *, REGEXP_REPLACE(REGEXP_EXTRACT(email, r'@(.*)'), r'.*.ibm.com', 'ibm.com') domain

```

![](/data/attachment/album/201711/07/122044ud4eyqyzbtmdbyce.png)

![](/data/attachment/album/201711/07/122045ei3n20fi3zl11h0n.png)

当合并了其地区域名后， IBM 的相对位置明显上升了。

#### 回音

* [关于“ GitHub 2017 年顶级贡献者”的一些思考](https://redmonk.com/jgovernor/2017/10/25/some-thoughts-on-the-top-contributors-to-github-2017/)

### 接下来

我以前犯过错误，而且以后也可能再次出错。请查看所有的原始数据，并质疑我的前提假设——看看你能得到什么结论是很有趣的。

* [用一下交互式 Data Studio 报告](https://datastudio.google.com/open/0ByGAKP3QmCjLU1JzUGtJdTlNOG8)

感谢 [Ilya Grigorik](https://medium.com/@igrigorik) 保留的 [GitHub Archive](http://githubarchive.org/) 提供了这么多年的 GitHub 数据！

想要看更多的文章？看看我的 [Medium](http://medium.com/@hoffa/)、[在 twitter 上关注我](http://twitter.com/felipehoffa) 并订阅 [reddit.com/r/bigquery](https://reddit.com/r/bigquery)。[试试 BigQuery](https://www.reddit.com/r/bigquery/comments/3dg9le/analyzing_50_billion_wikipedia_pageviews_in_5/)，每个月可以[免费](https://cloud.google.com/blog/big-data/2017/01/how-to-run-a-terabyte-of-google-bigquery-queries-each-month-without-a-credit-card)分析 1 TB 的数据。

---

via: <https://medium.freecodecamp.org/the-top-contributors-to-github-2017-be98ab854e87>

作者：[Felipe Hoffa](https://medium.freecodecamp.org/@hoffa?source=post_header_lockup) 译者：[wxy](https://github.com/wxy) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


GitLab：我们正将源码贡献许可证切换到 DCO

Summary: 我们希望通过取消“贡献者许可协议”（CLA）来支持“开发者原创证书”（DCO），让每个人都能更轻松地做出贡献。

>
> 我们希望通过取消“<ruby> 贡献者许可协议 <rt>  Contributor License Agreement </rt></ruby>”（CLA）来支持“<ruby>   开发者原创证书  <rt>  Developer's Certificate of Origin </rt></ruby>”（DCO），让每个人都能更轻松地做出贡献。
>
>
>

![](/data/attachment/album/201711/07/164800bsco8jooz5cb58kc.jpg)

我们致力于成为[开源的好管家](https://about.gitlab.com/2016/01/11/being-a-good-open-source-steward/)，而这一承诺的一部分意味着我们永远不会停止重新评估我们如何做到这一点。承诺“每个人都可以贡献”就是消除贡献的障碍。对于我们的一些社区，“<ruby> 贡献者许可协议 <rt>  Contributor License Agreement </rt></ruby>”（CLA）是对 GitLab 贡献的阻碍，所以我们改为“<ruby>   开发者原创证书  <rt>  Developer's Certificate of Origin </rt></ruby>”（DCO）。

许多大型的开源项目都想成为自己命运的主人。拥有基于开源软件运行自己的基础架构的自由，以及修改和审计源代码的能力，而不依赖于供应商，这使开源具有吸引力。我们希望 GitLab 成为每个人的选择。

### 为什么改变？

贡献者许可协议（CLA）是对其它项目进行开源贡献的行业标准，但对于不愿意考虑法律条款的开发人员来说，这是不受欢迎的，并且由于需要审查冗长的合同而潜在地放弃他们的一些权利。贡献者发现协议不必要的限制，并且阻止开源项目的开发者使用 GitLab。我们接触过 Debian 开发人员，他们考虑放弃 CLA，而这就是我们正在做的。

### 改变了什么？

到今天为止，我们正在推出更改，以便 GitLab 源码的贡献者只需要一个项目许可证（所有仓库都是 MIT，除了 Omnibus 是 Apache 许可证）和一个[开发者原创证书](https://developercertificate.org/) （DCO）即可。DCO 为开发人员提供了更大的灵活性和可移植性，这也是 Debian 和 GNOME 计划将其社区和项目迁移到 GitLab 的原因之一。我们希望这一改变能够鼓励更多的开发者为 GitLab 做出贡献。谢谢 Debian，提醒我们做出这个改变。

>
> “我们赞扬 GitLab 放弃他们的 CLA，转而使用对 OSS 更加友好的方式，开源社区诞生于一个汇集在一起并转化为项目的贡献海洋，这一举动肯定了 GitLab 愿意保护个人及其创作过程，最重要的是，把知识产权掌握在创造者手中。”
>
>
> —— GNOME 董事会主席 Carlos Soriano
>
>
>

>
> “我们很高兴看到 GitLab 通过从 CLA 转换到 DCO 来简化和鼓励社区贡献。我们认识到，做这种本质性的改变并不容易，我们赞扬 GitLab 在这里所展示的时间、耐心和深思熟虑的考虑。”
>
>
> —— Debian 项目负责人 Chris Lamb
>
>
>

你可以[阅读这篇关于我们做出这个决定的分析](https://docs.google.com/a/gitlab.com/document/d/1zpjDzL7yhGBZz3_7jCjWLfRQ1Jryg1mlIVmG8y6B1_Q/edit?usp=sharing)。阅读所有关于我们 [GitLab 社区版的管理](https://about.gitlab.com/stewardship/)。

---

via: <https://about.gitlab.com/2017/11/01/gitlab-switches-to-dco-license/>

作者：[Jamie Hurewitz](https://about.gitlab.com/team/#hurewitzjamie) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


Linux 是如何成功运作的

![](/data/attachment/album/201711/07/233825v5wswwdztqo2wssz.jpg)

>
> 在大量金钱与围绕 Linux 激烈争夺的公司之间，真正给操作系统带来活力的正是那些开发者。
>
>
>

事实证明上，Linux 社区是可行的，因为它本身无需太过担心社区的正常运作。尽管 Linux 已经在超级计算机、移动设备和云计算等多个领域占据了主导的地位，但 Linux 内核开发人员更多的是关注于代码本身，而不是其所在公司的利益。

这是一个出现在 [Dawn Foster 博士](https://opensource.com/article/17/10/collaboration-linux-kernel)研究 Linux 内核协作开发的博士论文中的重要结论。Foster 是在英特尔公司和<ruby> 木偶实验室 <rt>  Puppet Labs </rt></ruby>的前任社区领导人，他写到，“很多人首先把自己看作是 Linux 内核开发者，其次才是作为一名雇员。”

随着大量的“<ruby> 基金洗劫型 <rt>  foundation washing </rt></ruby>”公司开始侵蚀各种开源项目，意图在虚构的社区面具之下隐藏企业特权，但 Linux 依然设法保持了自身的纯粹。问题是这是怎么做到的？

### 跟随金钱的脚步

毕竟，如果有任何开源项目会进入到企业贪婪的视线中，那它一定是 Linux。早在 2008 年，[Linux 生态系统的估值已经达到了最高 250 亿美元](http://www.osnews.com/story/20416/Linux_Ecosystem_Worth_25_Billion)。最近 10 年，伴随着数量众多的云服务、移动端，以及大数据基础设施对于 Linux 的依赖，这一数据一定倍增了。甚至在像 Oracle 这样单独一个公司里，Linux 也能提供数十亿美元的价值。

那么就难怪有这样一个通过代码来影响 Linux 发展方向的必争之地。

在 [Linux 基金会的最新报道](https://www.linux.com/publications/linux-kernel-development-how-fast-it-going-who-doing-it-what-they-are-doing-and-who-5)中，让我们看看在过去一年中那些最活跃的 Linux 贡献者，以及他们所在的企业[像](/article-8220-1.html)[“海龟”一样](https://en.wikipedia.org/wiki/Turtles_all_the_way_down)高高叠起。

![linux companies](/data/attachment/album/201711/07/233835bp1gwypyp60yn0p2.jpg)

这些企业花费大量的资金来雇佣开发者去为自由软件做贡献，并且每个企业都从这些投资中得到了回报。由于存在企业对 Linux 过度影响的潜在可能，导致一些人对引领 Linux 开发的 Linux 基金会[表示不满](https://www.datamation.com/open-source/the-linux-foundation-and-the-uneasy-alliance.html)。在像微软这样曾经的开源界宿敌的企业挥舞着钞票进入 Linux 基金会之后，这些批评言论正变得越来越响亮。

但这只是一位虚假的敌人，坦率地说，这是一个以前的敌人。

虽然企业为了利益而给 Linux 基金会投入资金已经是事实，不过这些赞助并不能收买基金会而影响到代码。在这个最伟大的开源社区中，金钱可以帮助招募到开发者，但这些开发者相比关注企业而更专注于代码。就像 Linux 基金会执行董事 [Jim Zemlin 所强调的](https://thenewstack.io/linux-foundation-critics/)：

>
> “我们的项目中技术角色都是独立于企业的。没有人会在其提交的内容上标记他们的企业身份： 在 Linux 基金会的项目当中有关代码的讨论是最大声的。在我们的项目中，开发者可以从一个公司跳槽到另一个公司而不会改变他们在项目中所扮演的角色。之后企业或政府采用了这些代码而创造的价值，反过来又投资到项目上。这样的良性循环有益于所有人，并且也是我们的项目目标。”
>
>
>

任何读过 [Linus Torvalds 的](https://github.com/torvalds) 的邮件列表评论的人都不可能认为他是个代表着这个或那个公司的人。这对于其他的杰出贡献者来说也是一样的。虽然他们几乎都是被大公司所雇佣，但是一般情况下，这些公司为这些开发者支付薪水让他们去做想做的开发，而且事实上，他们正在做他们想做的。

毕竟，很少有公司会有足够的耐心或承受风险来为资助一群新手 Linux 内核开发者，并等上几年，等他们中出现几个人可以贡献出质量足以打动内核团队的代码。所以他们选择雇佣已有的、值得信赖的开发者。正如 [2016 Linux 基金会报告](https://www.linux.com/publications/linux-kernel-development-how-fast-it-going-who-doing-it-what-they-are-doing-and-who-5)所写的，“无薪开发者的数量正在持续地缓慢下降，同时 Linux 内核开发被证明是一种雇主们所需要的日益有价值的技能，这确保了有经验的内核开发者不会长期停留在无薪阶段。”

然而，这样的信任是代码所带来的，并不是通过企业的金钱。因此没有一个 Linux 内核开发者会为眼前的金钱而丢掉他们已经积攒的信任，当出现新的利益冲突时妥协代码质量就很快失去信任。因此不存在这种问题。

### 不是康巴亚，就是权利的游戏，非此即彼

最终，Linux 内核开发就是一种身份认同， Foster 的研究是这样认为的。

为 Google 工作也许很棒，而且也许带有一个体面的头衔以及免费的干洗。然而，作为一个关键的 Linux 内核子系统的维护人员，很难得到任意数量的公司承诺高薪酬的雇佣机会。

Foster 这样写到，“他们甚至享受当前的工作并且觉得他们的雇主不错，许多（Linux 内核开发者）倾向于寻找一些临时的工作关系，那样他们作为内核开发者的身份更被视作固定工作，而且更加重要。”

由于作为一名 Linux 开发者的身份优先，企业职员的身份次之，Linux 内核开发者甚至可以轻松地与其雇主的竞争对手合作。之所以这样，是因为雇主们最终只能有限制地控制开发者的工作，原因如上所述。Foster 深入研究了这一问题：

>
> “尽管企业对其雇员所贡献的领域产生了一些影响，在他们如何去完成工作这点上，雇员还是很自由的。许多人在日常工作中几乎没有接受任何指令，来自雇主的高度信任对工作是非常有帮助的。然而，他们偶尔会被要求做一些特定的零碎工作或者是在一个对公司重要的特定领域投入兴趣。
>
>
> 许多内核开发者也与他们的竞争者展开日常的基础协作，在这里他们仅作为个人相互交流，而不需要关心雇主之间的竞争。这是我在 Intel 工作时经常见到的一幕，因为我们内核开发者几乎都是与我们主要的竞争对手一同工作的。”
>
>
>

那些公司可能会在运行 Linux 的芯片上、或 Linux 发行版，亦或者是被其他健壮的操作系统支持的软件上产生竞争，但开发者们主要专注于一件事情：使 Linux 越来越好。同样，这是因为他们的身份与 Linux 维系在一起，而不是编码时所在防火墙（指公司）。

Foster 通过 USB 子系统的邮件列表（在 2013 年到 2015 年之间）说明了这种相互作用，用深色线条描绘了公司之间更多的电子邮件交互：

![linux kernel](/data/attachment/album/201711/07/233837kvxgvnignxt0cczv.jpg)

在价格讨论中一些公司明显的来往可能会引起反垄断机构的注意，但在 Linux 大陆中，这只是简单的商业行为。结果导致为所有各方在自由市场相互竞争中得到一个更好的操作系统。

### 寻找合适的平衡

这样的“合作”，如 Novell 公司的创始人 Ray Noorda 所说的那样，存在于最佳的开源社区里，但只有在真正的社区里才存在。这很难做到，举个例子，对一个由单一供应商所主导的项目来说，实现正确的合作关系很困难。由 Google 发起的 [Kubernetes](https://kubernetes.io/) 表明这是可能的，但其它像是 Docker 这样的项目却在为同样的目标而挣扎，很大一部分原因是他们一直不愿放弃对自己项目的技术领导。

也许 Kubernetes 能够工作的很好是因为 Google 并不觉得必须占据重要地位，而且事实上，它*希望*其他公司担负起开发领导的职责。凭借出色的代码解决了一个重要的行业需求，像 Kubernetes 这样的项目就能获得成功，只要 Google 既能帮助它，又为它开辟出一条道路，这就鼓励了 Red Hat 及其它公司做出杰出的贡献。

不过，Kubernetes 是个例外，就像 Linux 曾经那样。成功是因为企业的贪婪，有许多要考虑的，并且要在之间获取平衡。如果一个项目仅仅被公司自己的利益所控制，常常会在公司的技术管理上体现出来，而且再怎么开源许可也无法对企业产生影响。

简而言之，Linux 的成功运作是因为众多企业都想要控制它但却难以做到，由于其在工业中的重要性，使得开发者和构建人员更愿意作为一名 Linux 开发者 而不是 Red Hat （或 Intel 亦或 Oracle … ）工程师。

---

via: <https://www.datamation.com/open-source/why-linux-works.html>

作者：[Matt Asay](https://www.datamation.com/author/Matt-Asay-1133910.html) 译者：[softpaopao](https://github.com/softpaopao) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


世界上最流行的操作系统不是 Linux 或 Windows，而是 MINIX

![](/data/attachment/album/201711/08/002801dk99olicslr93ql9.jpg)

### 运行在“Ring -3” 的 MINIX

你可能不知道，但是在你的英特尔系统里，除了你的主操作系统之外，还有一个操作系统在运行，这就是 MINIX。

早在今年 5月，[EFF 就发布了一篇文章](https://www.eff.org/deeplinks/2017/05/intels-management-engine-security-hazard-and-users-need-way-disable-it)，介绍了自 2008 年以来，这十年间英特尔发布的所有处理器都运行了一个修改版的 MINIX 3，它被称之为“管理引擎（ME）”。这个由计算机科学教授 Andrew Tanenbaum （对，就是那位早期曾经和 Linus Torvalds 论战过的教授）作为一个教育工具开发的类 Unix 操作系统内置于每一款新英特尔处理器内。

MINIX 运行在你的 CPU 的 “Ring -3”（负数 3） 层上，虽然是你的 CPU，但是你无权访问它。你能够实际访问的最低的 “Ring” 是 “Ring 0”，你的操作系统（比如 Linux）内核就运行在这一层，而大多数用户程序则运行在 “Ring 3”（正数 3）上。

这个运行在 “Ring -3” 的 MINIX 包括如下功能：

完整网络堆栈、文件系统、许多驱动程序 (包括 USB、网络等)，以及一个 Web 服务器！

没错，Web 服务器。在你的 CPU 里面有一个秘密的 Web 服务器，您是不允许访问的，而且，显然，英特尔并不希望你知道。我们不知道这个 Web 服务器究竟有什么用途，也许是 CPU 厂商会用它来访问一些数据或者进行一些控制。但是这一切，你都不知道。

据称，Google 正在积极从其内部服务器上移除这个管理引擎，显然 Google 对其安全风险感到忧虑。

这个事情有两个有趣或者说疯狂的地方：

首先，由于英特尔 CPU 的流行，所以，世界上最流行的操作系统恐怕不是 Windows，也不是 Linux，而是这个 MINIX——我们都是 MINIX 用户！

其次，由于你根本没权限访问到“Ring -3”，而这个 MINIX 却能够完全访问你的整个系统——这就是一个巨大的安全风险，运行权限极大，但是从不更新。

### MINIX 作者表态

在过去几天多家媒体[报道](https://www.networkworld.com/article/3236064/servers/minix-the-most-popular-os-in-the-world-thanks-to-intel.html)了这一消息，以至于惊动了 Andrew 本人。他在个人网站上[发表了公开信](http://www.cs.vu.nl/~ast/intel/)，强调自己并没有直接参与这个项目，如果这个系统有后门的话，这与他无关（他对此并没有明说只是暗示）。

Andrew Tanenbaum 称，MINIX 3 在 2000 年决定采用 BSD 授权，原因是企业不喜欢 GPL 许可证，认为 GPL 会让他们花费许多时间精力金钱去修改代码，然后免费提供给竞争对手。他说，英特尔的工程团队在几年前接触了他，询问了 MINIX 3 大量的技术问题，要求他对 MINIX 3 进行大量改变，减少内存占用，选择性的关闭不需要的功能。

在短暂的活跃之后双方进入了静默，直到现在媒体报道英特尔处理器都运行了 MINIX 3 他才知道。他对此感到吃惊，但并不在意，因为该操作系统是 BSD 授权，英特尔不需要付钱给他。他只是希望英特尔在部署了 MINIX 3 之后能通知他一下，这只是礼貌问题。

参考：[solidot](http://www.solidot.org/story?sid=54408)，[networkworld](https://www.networkworld.com/article/3236064/servers/minix-the-most-popular-os-in-the-world-thanks-to-intel.html)


不，Linux 桌面版并没有突然流行起来

Summary: 有一些传闻说，Linux 桌面版的市场占有率从通常的 1.5% - 3% 翻了一番，达到 5%。真正的解释要简单的多。这似乎只是一个错误。

>
> 最近流传着这样一个传闻，Linux 桌面版已经突然流行起来了，并且使用者超过了 macOS。其实，并不是的。
>
>
>

![](/data/attachment/album/201711/08/215822fxu3iix3uep6xuuq.jpg)

有一些传闻说，Linux 桌面版的市场占有率从通常的 1.5% - 3% 翻了一番，达到 5%。那些报道是基于 [NetMarketShare](https://www.netmarketshare.com/) 的桌面操作系统分析报告而来的，据其显示，在七月份，Linux 桌面版的市场占有率从 2.5% 飙升，在九月份几乎达到 5%。但对 Linux 爱好者来说，很不幸，它并不是真的。

它也不是因为加入了谷歌推出的 Chrome OS，它在 [NetMarketShare](https://www.netmarketshare.com/) 和 [StatCounter](https://statcounter.com/) 的桌面操作系统的数据中被低估，它被认为是 Linux。但请注意，那是公正的，因为 [Chrome OS 是基于 Linux 的](http://www.zdnet.com/article/the-secret-origins-of-googles-chrome-os/)。

真正的解释要简单的多。这似乎只是一个错误。NetMarketShare 的市场营销高管 Vince Vizzaccaro 告诉我，“Linux 份额是不正确的。我们意识到这个问题，目前正在调查此事”。（LCTT 译注：已经修复该错误）

如果这听起来很奇怪，那是因为你可能认为，NetMarketShare 和 StatCounter 只是计算用户数量。但他们不是这样的。相反，他们都使用自己的秘密的方法去统计这些操作系统的数据。

NetMarketShare 的方法是对 “[从网站访问者的浏览器中收集数据](http://www.netmarketshare.com/faq.aspx#Methodology)到我们专用的请求式 HitsLink 分析网络中和 SharePost 客户端。该网络包括超过 4 万个网站，遍布全球。我们‘计数’访问我们的网络站点的唯一访客，并且一个唯一访客每天每个网络站点只计数一次。”

然后，公司按国家对数据进行加权。“我们将我们的流量与 CIA 互联网流量按国家进行比较，并相应地对我们的数据进行加权。例如，如果我们的全球数据显示巴西占我们网络流量的 2%，而 CIA 的数据显示巴西占全球互联网流量的 4%，那么我们将统计每一个来自巴西的唯一访客两次。”

他们究竟如何 “权衡” 每天访问一个站点的数据？我们不知道。

StatCounter 也有自己的方法。它使用 “[在全球超过 200 万个站点上安装的跟踪代码](http://gs.statcounter.com/faq#methodology)。这些网站涵盖了各种类型和不同的地理位置。每个月，我们都会记录在这些站点上的数十亿页的页面访问。对于每个页面访问，我们分析其使用的浏览器/操作系统/屏幕分辨率（如果页面访问来自移动设备）。 ... 我们统计了所有这些数据以获取我们的全球统计信息。

我们为互联网使用趋势提供独立的、公正的统计数据。我们不与任何其他信息源核对我们的统计数据，也 [没有使用人为加权](http://gs.statcounter.com/faq#no-weighting)。”

他们如何汇总他们的数据？你猜猜看？其它我们也不知道。

因此，无论何时，你看到的他们这些经常被引用的操作系统或浏览器的数字，使用它们要有很大的保留余地。

对于更精确的，以美国为对象的操作系统和浏览器数量，我更喜欢使用联邦政府的 [数字分析计划（DAP）](https://www.digitalgov.gov/services/dap/)。

与其它的不同， DAP 的数字来自在过去的 90 天访问过 [400 个美国政府行政机构域名](https://analytics.usa.gov/data/live/second-level-domains.csv) 的数十亿访问者。那里有 [大概 5000 个网站](https://analytics.usa.gov/data/live/sites.csv)，并且包含每个内阁部门。 DAP 从一个谷歌分析帐户中得到原始数据。 DAP [开源了它在这个网站上显示其数据的代码](https://github.com/GSA/analytics.usa.gov) 以及它的 [数据收集代码](https://github.com/18F/analytics-reporter)。最重要的是，与其它的不同，你可以以 [JSON](http://json.org/) 格式下载它的数据，这样你就可以自己分析原始数据了。

在 [美国分析](https://analytics.usa.gov/) 网站上，它汇总了 DAP 的数据，你可以找到 Linux 桌面版，和往常一样，它仍以 1.5% 列在 “其它” 中。Windows 仍然是高达 45.9%，接下来是 Apple iOS，占 25.5%，Android 占 18.6%，而 macOS 占 8.5%。

对不起，伙计们，我也希望它更高，但是，这就是事实。没有人，即使是 DAP，似乎都无法很好地将基于 Linux 的 Chrome OS 数据单列出来。尽管如此，Linux 桌面版仍然是 Linux 高手、软件开发者、系统管理员和工程师的专利。Linux 爱好者们还只能对其它所有的计算机设备 —— 服务器、云、超级计算机等等的（Linux）操作系统表示自豪。

---

via: <http://www.zdnet.com/article/no-the-linux-desktop-hasnt-jumped-in-popularity/>

作者：[Steven J. Vaughan-Nichols](http://www.zdnet.com/meet-the-team/us/steven-j-vaughan-nichols/)  译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


明确 GPL 版本号，避免许可证混淆

>
> 明确是避免许可歧义的关键所在。
>
>
>

![](/data/attachment/album/201711/08/235505stg54ff5vfumj6vg.png)

在许可证的过去、当前和未来版本如何适用于软件程序方面，[GPL 系列许可证](https://www.gnu.org/licenses/licenses.html)在开源许可证中可谓独树一帜。如果不能完全理解其中独有的许可证特性，开源软件开发人员可能会无意中造成混淆。

GPL 许可证在其许可证的条款和条件中阐明了许可证版本如何适用于该程序。[GPL v2](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html#SEC3)（第 9 条）写到：

>
> “每个版本都有一个独特的版本号，如果该程序指定了其适用的许可证的版本号以及‘任何更新的版本’，则可以选择遵循由<ruby> 自由软件基金会 <rp>  （ </rp> <rt>  Free Software Foundation </rt> <rp>  ） </rp></ruby>发布的该版本或之后任何更新版本的条款和条件。如果该程序未指定许可证的版本号，则可以选择自由软件基金会以前发布的任何版本。”
>
>
>

[GPL v3](https://www.gnu.org/licenses/gpl.html) 第 14 条与 GPL v2 中的上述条款非常相似。

多年以来，我看到很多开源项目表示遵循 GPL 许可证，但却没有明确指出版本号，同时也没有将整个 GPL 许可证（例如，v2 或 v3）副本囊括在程序内。取决于您是许可人还是被许可人等因素，这其中造成的含混不清可能对您有益或有害。

### 许可证的模糊如何产生影响

例如，假设应用程序的许可证声明：“本程序遵循 GPL 许可证”，并且包含整个 GPL v3 许可证的副本。由于该项目没有明确说明适用该许可证的哪个版本号，所以合理的解释是自由软件基金会发布的所有版本 GPL 许可证都适用——v3、v2 甚至 v1！

依据 GPL v3 第 14 条的下述文本可以合乎情理地做出上述理解。

>
> “如果该程序未指定 GNU GPL 的版本号，则可以选择由自由软件基金会发布的任何版本。”
>
>
>

另一方面，将 GPL 特定版本的完整副本（还可能包括许可证标题块中的 GPL 版本号）包含在程序中，可以被解释为在实质上传递了特定版本的许可证。在这个例子中，那就是 GPL v3 版本并且只有 GPL v3 版本，因为 v3 中没有“任何更新的版本”的条款。

### 如何避免许可歧义

为了避免这种许可歧义，您应该写得非常明确。如果您只想适用 GPL v3，应该明确地声明：“本程序仅遵循 GPL v3”，并提供整个 GPL v3 许可证副本。或者，如果您希望适用 GPL v3 或之后更新的版本，请明确声明：“本程序遵循 GPL v3 或其之后更新的版本”。最后，如果您真的想要适用任何版本的 GPL 许可证，您可以提供 GPL v3 许可证，并表示：“本程序遵循由自由软件基金会发布的任何版本的 GPL 许可证”。

无论您选择哪种授权方式，都应该非常明确，让每个人都能理解您的真正意图。

---

作者简介：Jeffrey R. Kaufman是全球领先的开源软件解决方案供应商Red Hat公司的开源知识产权律师，还担任托马斯杰斐逊法学院（Thomas Jefferson School of Law）的兼职教授。在任职Red Hat之前，Jeffrey曾担任高通公司（Qualcomm Incorporated）的专利顾问，为首席科学家办公室（Office of the Chief Scientist）提供开源事务咨询。

译者简介：薛亮，集慧智佳知识产权咨询公司高级咨询师，擅长专利检索、专利分析、竞争对手跟踪、FTO分析、开源软件知识产权风险分析，致力于为互联网企业、高科技公司提供知识产权咨询服务。

---

via: <https://opensource.com/article/17/11/avoiding-gpl-confusion>

作者：[Jeffrey Robert Kaufman](https://opensource.com/users/jkaufman) 译者：薛亮 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](/article-9041-1.html) 荣誉推出


GitHub 2017 章鱼猫观察报告

Summary: 又是一年，GitHub 例行发布了 2017 年度的章鱼猫观察报告。以下我们撷取此报告中一些有趣的结果分享给大家。

![](/data/attachment/album/201711/09/000609ifqg33f4xngtwgr9.jpg)

又是一年，GitHub 例行发布了 [2017 年度的章鱼猫观察报告](https://octoverse.github.com/)。以下我们撷取此报告中一些有趣的结果分享给大家。

数百万的开发人员使用 GitHub 来共享代码和构建业务。在这里你可以完成你的工作、打造新的技术、贡献给开源项目等等。历史已经证明，当好奇的人聚集到一起工作，一些美好的事情就会随之而来：工作进行得更快、新的想法涌现，从根本上改变了我们建立软件的方式。

为了庆祝这贡献和辉煌的一年, 让我们回顾一下 2017 年的项目、人员和团队。

### 十年千万，遍及全球

过去这十年，GitHub 各项数据已经超过了百万级，甚至千万级。在 2017 年，GitHub 社区有 2400 万开发者工作于 6700 万个仓库上，就连这些开发者组成的组织都达到了 150 万个。

而这些开发者遍及全球：亚洲 710 万，北美 590 万，欧洲 530 万，等等。

![](/data/attachment/album/201711/09/001536b203gkvokvpg8rin.jpg)

### 这一年，忙碌的一年

人们在 2500 万个公开仓库上分享代码。从 2016 年 9 月到现在的一年间：

* 公开仓库的提交数达到了 **1 亿个**！
* 活跃仓库有 2530 万个（“活跃”指该仓库有公开的活动，比如提交、星标、讨论等）
* 活跃<ruby> 工单 <rp>  （ </rp> <rt>  issue </rt> <rp>  ） </rp></ruby>有 1250 万个，关闭（解决）了 6880 万个工单，对工单进行了 140 万次讨论
	+ 这其中包括 [Kent 的约聊求助工单](https://github.com/kentcdodds/ama/issues/295) :D
* 新 PR （<ruby> 拉取请求 <rp>  （ </rp> <rt>  pull request </rt> <rp>  ） </rp></ruby>）有 130 万个，
	+ 这其中包括了 [Linus Torvalds 的第一个 GitHub PR](https://github.com/Subsurface-divelog/subsurface/pull/155) ！
* **第 1 亿个 PR** 被合并，这是一个 [OpenShift 的文档更新](https://github.com/openshift/openshift-docs/pull/4509)
* 对代码进行了 62 万次审查
* 最流行的表情符是：点赞（720 万）
* 新加入 670 万开发者，其有 100 万的开发者来自美国，69 万来自中国
	+ 这些新加入的开发者发起了 120 万个 PR，410 万人创建了其第一个仓库，
	+ 19 万人没有提交任何代码而只是复刻和星标了仓库
* 创建了 45 万个组织
	+ 这其中包括 [Python](https://github.com/python) 的开发也迁移到了 GitHub

### 编程语言，各就其位

通过 PR 所使用的语言，可以发现最流行的语言是——**JavaScript**！而 Python 取代了 Java 成为了第二名。很高兴 Ruby 和 PHP 分别能取得第四、第五名。其余的名次和[去年](/article-7776-1.html)相差不大。

![](/data/attachment/album/201711/09/003148sdflxzl5s9d5yfbb.jpg)

### 项目排名，众望所归

从这些活跃的仓库中，我们找出了 10 大<ruby> 复刻 <rp>  （ </rp> <rt>  fork </rt> <rp>  ） </rp></ruby>数最多的仓库。人工智能方向的 [TensorFlow](https://github.com/tensorflow/tensorflow) 项目夺得桂冠，前端方向的 [BootStrap](https://github.com/twbs/bootstrap) 是第二。尤雨溪的 [vuejs](https://github.com/vuejs/vue) 排名第六，恰恰比排名第七的 Facebook 的 [react](https://github.com/facebook/react) 的复刻数高一点，很难说这与今年 Facebook 对 react 的[许可证问题](/article-8733-1.html)有没有关系。而 Linus 的 [Linux](https://github.com/torvalds/linux) 项目敬陪末座，作为这样庞大的一个项目，已经相当了不起了。

![](/data/attachment/album/201711/09/080451o0rj98k0dijn838e.jpg)

（这里没有包括 MOOC 课程，[一个 Coursera 的 R 语言课程](https://github.com/rdpeng/ProgrammingAssignment2)有数千的复刻数，以此判断，至少有十万学生开始学习该课程了）

而以贡献者来说，微软的 [vscode](https://github.com/Microsoft/vscode) 项目的贡献者最多，几乎是排在第二名的 [react-native](https://github.com/facebook/react-native) 的两倍。这一方面证明了社区对 vscode 的喜爱，另外一方面也证明了微软在开源方面的重注投入。

![](/data/attachment/album/201711/09/081117xyjj2qyrlsr3lqqj.jpg)

得到最多代码评议的项目是 Typescript 的一个类型定义库 [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped)，第二名才是炙手可热的 [Kubernetes](https://github.com/kubernetes/kubernetes)。

![](/data/attachment/album/201711/09/081215thgm4gzjihim4ttg.jpg)

当然，已经赢得了容器编排系统之战的 [Kerbernetes](https://github.com/kubernetes/kubernetes) 取得讨论最多的排名一点也不令人意外，它的讨论数量的零头就和第二名 [origin](https://github.com/openshift/origin) 差不多，而这个 OpenShift 下的 Origin 项目，也是一个 Kubernetes 项目——面向开发者的企业版 Kubernetes 发行版。

![](/data/attachment/album/201711/09/081628zw5ucu8t1qouw85e.jpg)

### 企业版，大公司多用

GitHub 虽然对个人的公开使用提供免费的服务，当然，如果你想放私有仓库，是要交费的。而 GitHub 对于或大或小的企业来说，更适用的是其企业版。

* 美国前一百个最大的公司（按收入）有一半在使用 GitHub 企业版
* 虽然美国是使用 GitHub 企业版最多的国家，但是也有 1/4 的客户来自其它国家
* 不仅仅是软件和互联网行业在使用 GitHub 企业版（占 22%），金融服务、商业服务也占比较高

感谢你，让我们期待 2018 年的章鱼猫报告！


如何分析博客中最流行的编程语言

>
> 摘要：这篇文章我们将对一些各种各样的博客的流行度相对于他们在谷歌上的排名进行一个分析。所有代码可以在 [github](https://github.com/Databrawl/blog_analysis) 上找到。
>
>
>

![](/data/attachment/album/201711/09/180208dcnhwgbjbx1c1sc1.jpg)

### 想法来源

我一直在想，各种各样的博客每天到底都有多少页面浏览量，以及在博客阅读受众中最受欢迎的是什么编程语言。我也很感兴趣的是，它们在谷歌的网站排名是否与它们的受欢迎程度直接相关。

为了回答这些问题，我决定做一个 Scrapy 项目，它将收集一些数据，然后对所获得的信息执行特定的数据分析和数据可视化。

### 第一部分：Scrapy

我们将使用 [Scrapy](https://scrapy.org/) 为我们的工作，因为它为抓取和对该请求处理后的反馈进行管理提供了干净和健壮的框架。我们还将使用 [Splash](https://github.com/scrapinghub/splash) 来解析需要处理的 Javascript 页面。Splash 使用自己的 Web 服务器充当代理，并处理 Javascript 响应，然后再将其重定向到我们的爬虫进程。

>
> 我这里没有描述 Scrapy 的设置，也没有描述 Splash 的集成。你可以在[这里](https://docs.scrapy.org/en/latest/intro/tutorial.html)找到 Scrapy 的示例，而[这里](https://blog.scrapinghub.com/2015/03/02/handling-javascript-in-scrapy-with-splash/)还有 Scrapy+Splash 指南。
>
>
>

#### 获得相关的博客

第一步显然是获取数据。我们需要关于编程博客的谷歌搜索结果。你看，如果我们开始仅仅用谷歌自己来搜索，比如说查询 “Python”，除了博客，我们还会得到很多其它的东西。我们需要的是做一些过滤，只留下特定的博客。幸运的是，有一种叫做 [Google 自定义搜索引擎（CSE）](https://en.wikipedia.org/wiki/Google_Custom_Search)的东西，它能做到这一点。还有一个网站 [www.blogsearchengine.org](http://www.blogsearchengine.org/)，它正好可以满足我们需要，它会将用户请求委托给 CSE，这样我们就可以查看它的查询并重复利用它们。

所以，我们要做的是到 [www.blogsearchengine.org](http://www.blogsearchengine.org/) 网站，搜索 “python”，并在一侧打开 Chrome 开发者工具中的网络标签页。这截图是我们将要看到的：

![](/data/attachment/album/201711/09/181024bgawp0jaw3atqkga.png)

突出显示的是 blogsearchengine 向谷歌委派的一个搜索请求，所以我们将复制它，并在我们的 scraper 中使用。

这个博客抓取爬行器类会是如下这样的:

```
class BlogsSpider(scrapy.Spider):
    name = 'blogs'
    allowed_domains = ['cse.google.com']

    def __init__(self, queries):
        super(BlogsSpider, self).__init__()
        self.queries = queries

```

与典型的 Scrapy 爬虫不同，我们的方法覆盖了 `__init__` 方法，它接受额外的参数 `queries`，它指定了我们想要执行的查询列表。

现在，最重要的部分是构建和执行这个实际的查询。这个过程放在 `start_requests` 爬虫的方法里面执行，我们愉快地覆盖它：

```
    def start_requests(self):
        params_dict = {
            'cx': ['partner-pub-9634067433254658:5laonibews6'],
            'cof': ['FORID:10'],
            'ie': ['ISO-8859-1'],
            'q': ['query'],
            'sa.x': ['0'],
            'sa.y': ['0'],
            'sa': ['Search'],
            'ad': ['n9'],
            'num': ['10'],
            'rurl': [
                'http://www.blogsearchengine.org/search.html?cx=partner-pub'
                '-9634067433254658%3A5laonibews6&cof=FORID%3A10&ie=ISO-8859-1&'
                'q=query&sa.x=0&sa.y=0&sa=Search'
            ],
            'siteurl': ['http://www.blogsearchengine.org/']
        }

        params = urllib.parse.urlencode(params_dict, doseq=True)
        url_template = urllib.parse.urlunparse(
            ['https', self.allowed_domains[0], '/cse',
             '', params, 'gsc.tab=0&gsc.q=query&gsc.page=page_num'])
        for query in self.queries:
            for page_num in range(1, 11):
                url = url_template.replace('query', urllib.parse.quote(query))
                url = url.replace('page_num', str(page_num))
                yield SplashRequest(url, self.parse, endpoint='render.html',
                                    args={'wait': 0.5})

```

在这里你可以看到相当复杂的 `params_dict` 字典，它控制所有我们之前找到的 Google CSE URL 的参数。然后我们准备好 `url_template` 里的一切，除了已经填好的查询和页码。我们对每种编程语言请求 10 页，每一页包含 10 个链接，所以是每种语言有 100 个不同的博客用来分析。

在 `42-43` 行，我使用一个特殊的类 `SplashRequest` 来代替 Scrapy 自带的 Request 类。它封装了 Splash 库内部的重定向逻辑，所以我们无需为此担心。十分整洁。

最后，这是解析程序：

```
    def parse(self, response):
        urls = response.css('div.gs-title.gsc-table-cell-thumbnail') \
            .xpath('./a/@href').extract()
        gsc_fragment = urllib.parse.urlparse(response.url).fragment
        fragment_dict = urllib.parse.parse_qs(gsc_fragment)
        page_num = int(fragment_dict['gsc.page'][0])
        query = fragment_dict['gsc.q'][0]
        page_size = len(urls)
        for i, url in enumerate(urls):
            parsed_url = urllib.parse.urlparse(url)
            rank = (page_num - 1) * page_size + i
            yield {
                'rank': rank,
                'url': parsed_url.netloc,
                'query': query
            }

```

所有 Scraper 的核心和灵魂就是解析器逻辑。可以有多种方法来理解响应页面的结构并构建 XPath 查询字符串。您可以使用 [Scrapy shell](https://doc.scrapy.org/en/latest/topics/shell.html) 尝试并随时调整你的 XPath 查询，而不用运行爬虫。不过我更喜欢可视化的方法。它需要再次用到谷歌 Chrome 开发人员控制台。只需右键单击你想要用在你的爬虫里的元素，然后按下 Inspect。它将打开控制台，并定位到你指定位置的 HTML 源代码。在本例中，我们想要得到实际的搜索结果链接。他们的源代码定位是这样的:

![](/data/attachment/album/201711/09/181052l5g5svh3hqosogyy.png)

在查看这个元素的描述后我们看到所找的 `<div>` 有一个 `.gsc-table-cell-thumbnail` CSS 类，它是 `.gs-title` `<div>` 的子元素，所以我们把它放到响应对象的 `css` 方法（`46` 行）。然后，我们只需要得到博客文章的 URL。它很容易通过`'./a/@href'` XPath 字符串来获得，它能从我们的 `<div>` 直接子元素的 `href` 属性找到。（LCTT 译注：此处图文对不上）

#### 寻找流量数据

下一个任务是估测每个博客每天得到的页面浏览量。得到这样的数据有[各种方式](https://www.labnol.org/internet/find-website-traffic-hits/8008/)，有免费的，也有付费的。在快速搜索之后，我决定基于简单且免费的原因使用网站 [www.statshow.com](http://www.statshow.com/) 来做。爬虫将抓取这个网站，我们在前一步获得的博客的 URL 将作为这个网站的输入参数，获得它们的流量信息。爬虫的初始化是这样的:

```
class TrafficSpider(scrapy.Spider):
    name = 'traffic'
    allowed_domains = ['www.statshow.com']

    def __init__(self, blogs_data):
        super(TrafficSpider, self).__init__()
        self.blogs_data = blogs_data

```

`blogs_data` 应该是以下格式的词典列表：`{"rank": 70, "url": "www.stat.washington.edu"， "query": "Python"}`。

请求构建函数如下：

```
    def start_requests(self):
        url_template = urllib.parse.urlunparse(
            ['http', self.allowed_domains[0], '/www/{path}', '', '', ''])
        for blog in self.blogs_data:
            url = url_template.format(path=blog['url'])
            request = SplashRequest(url, endpoint='render.html',
                                    args={'wait': 0.5}, meta={'blog': blog})
            yield request

```

它相当的简单，我们只是把字符串 `/www/web-site-url/` 添加到 `'www.statshow.com'` URL 中。

现在让我们看一下语法解析器是什么样子的：

```
    def parse(self, response):
        site_data = response.xpath('//div[@id="box_1"]/span/text()').extract()
        views_data = list(filter(lambda r: '$' not in r, site_data))
        if views_data:
            blog_data = response.meta.get('blog')
            traffic_data = {
                'daily_page_views': int(views_data[0].translate({ord(','): None})),
                'daily_visitors': int(views_data[1].translate({ord(','): None}))
            }
            blog_data.update(traffic_data)
            yield blog_data

```

与博客解析程序类似，我们只是通过 StatShow 示例的返回页面，然后找到包含每日页面浏览量和每日访问者的元素。这两个参数都确定了网站的受欢迎程度，对于我们的分析只需要使用页面浏览量即可 。

### 第二部分：分析

这部分是分析我们搜集到的所有数据。然后，我们用名为 [Bokeh](https://bokeh.pydata.org/en/latest/) 的库来可视化准备好的数据集。我在这里没有给出运行器和可视化的代码，但是它可以在 [GitHub repo](https://github.com/Databrawl/blog_analysis) 中找到，包括你在这篇文章中看到的和其他一切东西。

>
> 最初的结果集含有少许偏离过大的数据，（如 google.com、linkedin.com、Oracle.com 等）。它们显然不应该被考虑。即使其中有些有博客，它们也不是针对特定语言的。这就是为什么我们基于这个 [StackOverflow 回答](https://stackoverflow.com/a/16562028/1573766) 中所建议的方法来过滤异常值。
>
>
>

#### 语言流行度比较

首先，让我们对所有的语言进行直接的比较，看看哪一种语言在前 100 个博客中有最多的浏览量。

这是能进行这个任务的函数：

```
def get_languages_popularity(data):
    query_sorted_data = sorted(data, key=itemgetter('query'))
    result = {'languages': [], 'views': []}
    popularity = []
    for k, group in groupby(query_sorted_data, key=itemgetter('query')):
        group = list(group)
        daily_page_views = map(lambda r: int(r['daily_page_views']), group)
        total_page_views = sum(daily_page_views)
        popularity.append((group[0]['query'], total_page_views))
    sorted_popularity = sorted(popularity, key=itemgetter(1), reverse=True)
    languages, views = zip(*sorted_popularity)
    result['languages'] = languages
    result['views'] = views
    return result

```

在这里，我们首先按语言（词典中的关键字“query”）来分组我们的数据，然后使用 python 的 `groupby` 函数，这是一个从 SQL 中借来的奇妙函数，从我们的数据列表中生成一组条目，每个条目都表示一些编程语言。然后，在第 `14` 行我们计算每一种语言的总页面浏览量，然后添加 `('Language', rank)` 形式的元组到 `popularity` 列表中。在循环之后，我们根据总浏览量对流行度数据进行排序，并将这些元组展开到两个单独的列表中，然后在 `result` 变量中返回它们。

>
> 最初的数据集有很大的偏差。我检查了到底发生了什么，并意识到如果我在 [blogsearchengine.org](http://blogsearchengine.org/) 上查询“C”，我就会得到很多无关的链接，其中包含了 “C” 的字母。因此，我必须将 C 排除在分析之外。这种情况几乎不会在 “R” 和其他类似 C 的名称中出现：“C++”、“C”。
>
>
>

因此，如果我们将 C 从考虑中移除并查看其他语言，我们可以看到如下图:

![](/data/attachment/album/201711/09/181118vdtga5053aa9z9ta.png)

评估结论：Java 每天有超过 400 万的浏览量，PHP 和 Go 有超过 200 万，R 和 JavaScript 也突破了百万大关。

#### 每日网页浏览量与谷歌排名

现在让我们来看看每日访问量和谷歌的博客排名之间的联系。从逻辑上来说，不那么受欢迎的博客应该排名靠后，但这并没那么简单，因为其他因素也会影响排名，例如，如果在人气较低的博客上的文章更新一些，那么它很可能会首先出现。

数据准备工作以下列方式进行：

```
def get_languages_popularity(data):
    query_sorted_data = sorted(data, key=itemgetter('query'))
    result = {'languages': [], 'views': []}
    popularity = []
    for k, group in groupby(query_sorted_data, key=itemgetter('query')):
        group = list(group)
        daily_page_views = map(lambda r: int(r['daily_page_views']), group)
        total_page_views = sum(daily_page_views)
        popularity.append((group[0]['query'], total_page_views))
    sorted_popularity = sorted(popularity, key=itemgetter(1), reverse=True)
    languages, views = zip(*sorted_popularity)
    result['languages'] = languages
    result['views'] = views
    return result

```

该函数接受爬取到的数据和需要考虑的语言列表。我们对这些数据以语言的流行程度进行排序。后来，在类似的语言分组循环中，我们构建了 `(rank, views_number)` 元组（从 1 开始的排名）被转换为 2 个单独的列表。然后将这一对列表写入到生成的字典中。

前 8 位 GitHub 语言（除了 C）是如下这些：

![](/data/attachment/album/201711/09/181142lofi8ol3awlzugsz.png)

![](/data/attachment/album/201711/09/181205envskfnfnxzc3v3t.png)

评估结论：我们看到，所有图的 [PCC （皮尔逊相关系数）](https://en.wikipedia.org/wiki/Pearson_correlation_coefficient)都远离 1/-1，这表示每日浏览量与排名之间缺乏相关性。值得注意的是，在大多数图表（8 个中的 7 个）中，相关性是负的，这意味着排名的降低会导致浏览量的减少。

### 结论

因此，根据我们的分析，Java 是目前最流行的编程语言，其次是 PHP、Go、R 和 JavaScript。在日常浏览量和谷歌排名上，排名前 8 的语言都没有很强的相关性，所以即使你刚刚开始写博客，你也可以在搜索结果中获得很高的评价。不过，成为热门博客究竟需要什么，可以留待下次讨论。

>
> 这些结果是相当有偏差的，如果没有更多的分析，就不能过分的考虑这些结果。首先，在较长的一段时间内收集更多的流量信息，然后分析每日浏览量和排名的平均值（中值）值是一个好主意。也许我以后还会再回来讨论这个。
>
>
>

### 引用

1. 抓取：
	1. [blog.scrapinghub.com: Handling Javascript In Scrapy With Splash](https://blog.scrapinghub.com/2015/03/02/handling-javascript-in-scrapy-with-splash/)
	2. [BlogSearchEngine.org](http://www.blogsearchengine.org/)
	3. [twingly.com: Twingly Real-Time Blog Search](https://www.twingly.com/)
	4. [searchblogspot.com: finding blogs on blogspot platform](http://www.searchblogspot.com/)
2. 流量评估：
	1. [labnol.org: Find Out How Much Traffic a Website Gets](https://www.labnol.org/internet/find-website-traffic-hits/8008/)
	2. [quora.com: What are the best free tools that estimate visitor traffic…](https://www.quora.com/What-are-the-best-free-tools-that-estimate-visitor-traffic-for-a-given-page-on-a-particular-website-that-you-do-not-own-or-operate-3rd-party-sites)
	3. [StatShow.com: The Stats Maker](http://www.statshow.com/)

---

via: <https://www.databrawl.com/2017/10/08/blog-analysis/>

作者：[Serge Mosin](https://www.databrawl.com/author/svmosingmail-com/) 译者：[Chao-zhi](https://github.com/Chao-zhi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


为什么 Ubuntu 放弃 Unity？创始人如是说

Summary: 答案可能会出乎意料……或许不会，因为答案也在情理之中。简而言之就是他把驱逐 Unity 作为节约成本的一部分，旨在使 Canonical 走上 IPO 的道路。是的，投资者来了。

![Mark Shuttleworth](/data/attachment/album/201711/10/101920lvghu2vgh95h4lek.jpg)

Mark Shuttleworth 是 Ubuntu 的创始人

Ubuntu 之前[在 4 月份](/article-8428-1.html)宣布决定放弃 Unity 让包括我在内的所有人都大感意外。

现在，Ubuntu 的创始人 [Mark Shuttleworth](https://en.wikipedia.org/wiki/Mark_Shuttleworth) 分享了关于 Ubuntu 为什么会选择放弃 Unity 的更多细节。

答案可能会出乎意料……

或许不会，因为答案也在情理之中。

### 为什么 Ubuntu 放弃 Unity？

上周（10 月 20 日）[Ubuntu 17.10](/article-8980-1.html) 已经发布，这是自 [2011 年引入](http://www.omgubuntu.co.uk/2010/10/ubuntu-11-04-unity-default-desktop) Unity 以来，Ubuntu 第一次没有带 Unity 桌面发布。

当然，主流媒体对 Unity 的未来感到好奇，因此 Mark Shuttleworth [向 eWeek](http://www.eweek.com/enterprise-apps/canonical-on-path-to-ipo-as-ubuntu-unity-linux-desktop-gets-ditched) 详细介绍了他决定在 Ubuntu 路线图中抛弃 Unity 的原因。

简而言之就是他把驱逐 Unity 作为节约成本的一部分，旨在使 Canonical 走上 IPO 的道路。

是的，投资者来了。

但是完整采访提供了更多关于这个决定的更多内容，并且披露了放弃曾经悉心培养的桌面对他而言是多么艰难。

### “Ubuntu 已经进入主流”

Mark Shuttleworth 和 [Sean Michael Kerner](https://twitter.com/TechJournalist) 的谈话，首先提醒了我们 Ubuntu 有多么伟大：

>
> “Ubuntu 的美妙之处在于，我们创造了一个对终端用户免费，并围绕其提供商业服务的平台，在这个梦想中，我们可以用各种不同的方式定义未来。
>
>
> 我们确实已经看到，Ubuntu 在很多领域已经进入了主流。”
>
>
>

但是受欢迎并不意味着盈利，Mark 指出：

>
> “我们现在所做的一些事情很明显在商业上是不可能永远持续的，而另外一些事情无疑商业上是可持续发展的，或者已经在商业上可持续。
>
>
> 只要我们还是一个纯粹的私人公司，我们就有完全的自由裁量权来决定是否支持那些商业上不可持续的事情。”
>
>
>

Shuttleworth 说，他和 Canonical 的其他“领导”通过协商一致认为，他们应该让公司走上成为上市公司的道路。

为了吸引潜在的投资者，公司必须把重点放在盈利领域 —— 而 Unity、Ubuntu 电话、Unity 8 以及<ruby> 融合 <rt>  convergence </rt></ruby>不属于这个部分：

>
> “[这个决定]意味着我们不能让我们的名册中拥有那些根本没有商业前景实际上却非常重大的项目。
>
>
> 这并不意味着我们会考虑改变 Ubuntu 的条款，因为它是我们所做的一切的基础。而且实际上，我们也没有必要。”
>
>
>

### “Ubuntu 本身现在完全可持续发展”

钱可能意味着 Unity 的消亡，但会让更广泛的 Ubuntu 项目健康发展。正如 Shuttleworth 解释说的：

>
> “我最为自豪的事情之一就是在过去的 7 年中，Ubuntu 本身变得完全可持续发展。即使明天我被车撞倒，而 Ubuntu 也可以继续发展下去。
>
>
> 这很神奇吧？对吧？这是一个世界级的企业平台，它不仅完全免费，而且是可持续的。
>
>
> 这主要要感谢 Jane Silber。” （LCTT 译注：Canonical 公司的 CEO）
>
>
>

虽然桌面用户都会关注桌面，但比起我们期待的每 6 个月发布的版本，对 Canonical 公司的关注显然要多得多。

失去 Unity 对桌面用户可能是一个沉重打击，但它有助于平衡公司的其他部分：

>
> “除此之外，我们在企业中还有巨大的可能性，比如在真正定义云基础设施是如何构建的方面，云应用程序是如何操作的等等。而且，在物联网中，看看下一波的可能性，那些创新者们正在基于物联网创造的东西。
>
>
> 所有这些都足以让我们在这方面进行 IPO。”
>
>
>

然而，对于 Mark 来说，放弃 Unity 并不容易，

>
> “我们在 Unity 上做了很多工作，我真的很喜欢它。
>
>
> 我认为 Unity 8 工程非常棒，而且如何将这些不同形式的要素结合在一起的深层理念是非常迷人的。”
>
>
> “但是，如果我们要走上 IPO 的道路，我不能再为将它留在 Canonical 来争论了。
>
>
> 在某个阶段你们应该会看到，我想我们很快就会宣布，没有 Unity， 我们实际上已经几乎打破了我们在商业上所做的所有事情。”
>
>
>

在这之后不久，他说公司可能会进行第一轮用于增长的投资，以此作为转变为正式上市公司前的过渡。

但 Mark 并不想让任何人认为投资者会 “毁了派对”：

>
> “我们还没沦落到需要根据风投的指示来行动的地步。我们清楚地看到了我们的客户喜欢什么，我们已经找到了适用于云和物联网的很好的市场着力点和产品。”
>
>
>

Mark 补充到，Canonical 公司的团队对这个决定 “无疑很兴奋”。

>
> “在情感上，我不想再经历这样的过程。我对 Unity 做了一些误判。我曾天真的认为业界会支持一个独立自由平台的想法。
>
>
> 但我也不后悔做过这件事。很多人会抱怨他们的选择，而不去创造其他选择。
>
>
> 事实证明，这需要一点勇气以及相当多的钱去尝试和创造这些选择。”
>
>
>

### OMG! IPO? NO!

在对 Canonical（可能）成为一家上市公司的观念进行争辩之前，我们要记住，**RedHat 已经是一家 20 年之久的上市公司了**。GNOME 桌面和 Fedora 在没有任何 “赚钱” 措施的干预下也都活得很不错。

Canonical 的 IPO 不太可能对 Ubuntu 产生突然的引人注目的的改变，因为就像 Shuttleworth 自己所说的那样，这是其它所有东西得以成立的基础。

Ubuntu 是已被认可的。这是云上的头号操作系统。它是世界上最受欢迎的 Linux 发行版（除了 [Distrowatch 排名](http://distrowatch.com/table.php?distribution=ubuntu)）。并且它似乎在物联网上也有巨大的应用前景。

Mark 说 Ubuntu 现在是完全可持续发展的。

随着一个[迎接 Ubuntu 17.10 到来的热烈招待会](http://www.omgubuntu.co.uk/2017/10/ubuntu-17-10-review-roundup)，以及一个新的 LTS 将要发布，事情看起来相当不错……

---

via: <http://www.omgubuntu.co.uk/2017/10/why-did-ubuntu-drop-unity-mark-shuttleworth-explains>

作者：[JOEY SNEDDON](https://plus.google.com/117485690627814051450/?rel=author) 译者：[Snapcrafter](https://github.com/Snapcrafter)，[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


在 GitLab 我们是如何扩展数据库的

![](/data/attachment/album/201711/11/141056bqycq96dnyy9qumh.jpg)

>
> 在扩展 GitLab 数据库和我们应用的解决方案，去帮助解决我们的数据库设置中的问题时，我们深入分析了所面临的挑战。
>
>
>

很长时间以来 GitLab.com 使用了一个单个的 PostgreSQL 数据库服务器和一个用于灾难恢复的单个复制。在 GitLab.com 最初的几年，它工作的还是很好的，但是，随着时间的推移，我们看到这种设置的很多问题，在这篇文章中，我们将带你了解我们在帮助解决 GitLab.com 和 GitLab 实例所在的主机时都做了些什么。

例如，数据库长久处于重压之下， CPU 使用率几乎所有时间都处于 70% 左右。并不是因为我们以最好的方式使用了全部的可用资源，而是因为我们使用了太多的（未经优化的）查询去“冲击”服务器。我们意识到需要去优化设置，这样我们就可以平衡负载，使 GitLab.com 能够更灵活地应对可能出现在主数据库服务器上的任何问题。

在我们使用 PostgreSQL 去跟踪这些问题时，使用了以下的四种技术：

1. 优化你的应用程序代码，以使查询更加高效（并且理论上使用了很少的资源）。
2. 使用一个连接池去减少必需的数据库连接数量（及相关的资源）。
3. 跨多个数据库服务器去平衡负载。
4. 分片你的数据库

在过去的两年里，我们一直在积极地优化应用程序代码，但它不是一个完美的解决方案，甚至，如果你改善了性能，当流量也增加时，你还需要去应用其它的几种技术。出于本文的目的，我们将跳过优化应用代码这个特定主题，而专注于其它技术。

### 连接池

在 PostgreSQL 中，一个连接是通过启动一个操作系统进程来处理的，这反过来又需要大量的资源，更多的连接（及这些进程）将使用你的数据库上的更多的资源。 PostgreSQL 也在 [max\_connections](https://www.postgresql.org/docs/9.6/static/runtime-config-connection.html#GUC-MAX-CONNECTIONS) 设置中定义了一个强制的最大连接数量。一旦达到这个限制，PostgreSQL 将拒绝新的连接， 比如，下面的图表示的设置：

![](/data/attachment/album/201711/11/141546l6n67tu6jz642x69.jpg)

这里我们的客户端直接连接到 PostgreSQL，这样每个客户端请求一个连接。

通过连接池，我们可以有多个客户端侧的连接重复使用一个 PostgreSQL 连接。例如，没有连接池时，我们需要 100 个 PostgreSQL 连接去处理 100 个客户端连接；使用连接池后，我们仅需要 10 个，或者依据我们配置的 PostgreSQL 连接。这意味着我们的连接图表将变成下面看到的那样：

![](/data/attachment/album/201711/11/141554m6m5sucrsf93f3ef.jpg)

这里我们展示了一个示例，四个客户端连接到 pgbouncer，但不是使用了四个 PostgreSQL 连接，而是仅需要两个。

对于 PostgreSQL 有两个最常用的连接池：

* [pgbouncer](https://pgbouncer.github.io/)
* [pgpool-II](http://pgpool.net/mediawiki/index.php/Main_Page)

pgpool 有一点特殊，因为它不仅仅是连接池：它有一个内置的查询缓存机制，可以跨多个数据库负载均衡、管理复制等等。

另一个 pgbouncer 是很简单的：它就是一个连接池。

### 数据库负载均衡

数据库级的负载均衡一般是使用 PostgreSQL 的 “<ruby> <a href="https://www.postgresql.org/docs/9.6/static/hot-standby.html">  热备机 </a> <rt>  hot-standby </rt></ruby>” 特性来实现的。 热备机是允许你去运行只读 SQL 查询的 PostgreSQL 副本，与不允许运行任何 SQL 查询的普通<ruby> 备用机 <rt>  standby </rt></ruby>相反。要使用负载均衡，你需要设置一个或多个热备服务器，并且以某些方式去平衡这些跨主机的只读查询，同时将其它操作发送到主服务器上。扩展这样的一个设置是很容易的：（如果需要的话）简单地增加多个热备机以增加只读流量。

这种方法的另一个好处是拥有一个更具弹性的数据库集群。即使主服务器出现问题，仅使用次级服务器也可以继续处理 Web 请求；当然，如果这些请求最终使用主服务器，你可能仍然会遇到错误。

然而，这种方法很难实现。例如，一旦它们包含写操作，事务显然需要在主服务器上运行。此外，在写操作完成之后，我们希望继续使用主服务器一会儿，因为在使用异步复制的时候，热备机服务器上可能还没有这些更改。

### 分片

分片是水平分割你的数据的行为。这意味着数据保存在特定的服务器上并且使用一个分片键检索。例如，你可以按项目分片数据并且使用项目 ID 做为分片键。当你的写负载很高时，分片数据库是很有用的（除了一个多主设置外，均衡写操作没有其它的简单方法），或者当你有*大量*的数据并且你不再使用传统方式保存它也是有用的（比如，你不能把它简单地全部放进一个单个磁盘中）。

不幸的是，设置分片数据库是一个任务量很大的过程，甚至，在我们使用诸如 [Citus](https://www.citusdata.com/) 的软件时也是这样。你不仅需要设置基础设施 （不同的复杂程序取决于是你运行在你自己的数据中心还是托管主机的解决方案），你还得需要调整你的应用程序中很大的一部分去支持分片。

#### 反对分片的案例

在 GitLab.com 上一般情况下写负载是非常低的，同时大多数的查询都是只读查询。在极端情况下，尖峰值达到每秒 1500 元组写入，但是，在大多数情况下不超过每秒 200 元组写入。另一方面，我们可以在任何给定的次级服务器上轻松达到每秒 1000 万元组读取。

存储方面，我们也不使用太多的数据：大约 800 GB。这些数据中的很大一部分是在后台迁移的，这些数据一经迁移后，我们的数据库收缩的相当多。

接下来的工作量就是调整应用程序，以便于所有查询都可以正确地使用分片键。 我们的一些查询包含了一个项目 ID，它是我们使用的分片键，也有许多查询没有包含这个分片键。分片也会影响提交到 GitLab 的改变内容的过程，每个提交者现在必须确保在他们的查询中包含分片键。

最后，是完成这些工作所需要的基础设施。服务器已经完成设置，监视也添加了、工程师们必须培训，以便于他们熟悉上面列出的这些新的设置。虽然托管解决方案可能不需要你自己管理服务器，但它不能解决所有问题。工程师们仍然需要去培训（很可能非常昂贵）并需要为此支付账单。在 GitLab 上，我们也非常乐意提供我们用过的这些工具，这样社区就可以使用它们。这意味着如果我们去分片数据库， 我们将在我们的 Omnibus 包中提供它（或至少是其中的一部分）。确保你提供的服务的唯一方法就是你自己去管理它，这意味着我们不能使用主机托管的解决方案。

最终，我们决定不使用数据库分片，因为它是昂贵的、费时的、复杂的解决方案。

### GitLab 的连接池

对于连接池我们有两个主要的诉求：

1. 它必须工作的很好（很显然这是必需的）。
2. 它必须易于在我们的 Omnibus 包中运用，以便于我们的用户也可以从连接池中得到好处。

用下面两步去评估这两个解决方案（pgpool 和 pgbouncer）：

1. 执行各种技术测试（是否有效，配置是否容易，等等）。
2. 找出使用这个解决方案的其它用户的经验，他们遇到了什么问题？怎么去解决的？等等。

pgpool 是我们考察的第一个解决方案，主要是因为它提供的很多特性看起来很有吸引力。我们其中的一些测试数据可以在 [这里](https://gitlab.com/gitlab-com/infrastructure/issues/259#note_23464570) 找到。

最终，基于多个因素，我们决定不使用 pgpool 。例如， pgpool 不支持<ruby> 粘连接 <rt>  sticky connection </rt></ruby>。 当执行一个写入并（尝试）立即显示结果时，它会出现问题。想像一下，创建一个<ruby> 工单 <rt>  issue </rt></ruby>并立即重定向到这个页面， 没有想到会出现 HTTP 404，这是因为任何用于只读查询的服务器还没有收到数据。针对这种情况的一种解决办法是使用同步复制，但这会给表带来更多的其它问题，而我们希望避免这些问题。

另一个问题是， pgpool 的负载均衡逻辑与你的应用程序是不相干的，是通过解析 SQL 查询并将它们发送到正确的服务器。因为这发生在你的应用程序之外，你几乎无法控制查询运行在哪里。这实际上对某些人也可能是有好处的， 因为你不需要额外的应用程序逻辑。但是，它也妨碍了你在需要的情况下调整路由逻辑。

由于配置选项非常多，配置 pgpool 也是很困难的。或许促使我们最终决定不使用它的原因是我们从过去使用过它的那些人中得到的反馈。即使是在大多数的案例都不是很详细的情况下，我们收到的反馈对 pgpool 通常都持有负面的观点。虽然出现的报怨大多数都与早期版本的 pgpool 有关，但仍然让我们怀疑使用它是否是个正确的选择。

结合上面描述的问题和反馈，最终我们决定不使用 pgpool 而是使用 pgbouncer 。我们用 pgbouncer 执行了一套类似的测试，并且对它的结果是非常满意的。它非常容易配置（而且一开始不需要很多的配置），运用相对容易，仅专注于连接池（而且它真的很好），而且没有明显的负载开销（如果有的话）。也许我唯一的报怨是，pgbouncer 的网站有点难以导航。

使用 pgbouncer 后，通过使用<ruby> 事务池 <rt>  transaction pooling </rt></ruby>我们可以将活动的 PostgreSQL 连接数从几百个降到仅 10 - 20 个。我们选择事务池是因为 Rails 数据库连接是持久的。这个设置中，使用<ruby> 会话池 <rt>  session pooling </rt></ruby>不能让我们降低 PostgreSQL 连接数，从而受益（如果有的话）。通过使用事务池，我们可以调低 PostgreSQL 的 `max_connections` 的设置值，从 3000 （这个特定值的原因我们也不清楚) 到 300 。这样配置的 pgbouncer ，即使在尖峰时，我们也仅需要 200 个连接，这为我们提供了一些额外连接的空间，如 `psql` 控制台和维护任务。

对于使用事务池的负面影响方面，你不能使用预处理语句，因为 `PREPARE` 和 `EXECUTE` 命令也许最终在不同的连接中运行，从而产生错误的结果。 幸运的是，当我们禁用了预处理语句时，并没有测量到任何响应时间的增加，但是我们 *确定* 测量到在我们的数据库服务器上内存使用减少了大约 20 GB。

为确保我们的 web 请求和后台作业都有可用连接，我们设置了两个独立的池： 一个有 150 个连接的后台进程连接池，和一个有 50 个连接的 web 请求连接池。对于 web 连接需要的请求，我们很少超过 20 个，但是，对于后台进程，由于在 GitLab.com 上后台运行着大量的进程，我们的尖峰值可以很容易达到 100 个连接。

今天，我们提供 pgbouncer 作为 GitLab EE 高可用包的一部分。对于更多的信息，你可以参考 “[Omnibus GitLab PostgreSQL High Availability](https://docs.gitlab.com/ee/administration/high_availability/alpha_database.html)”。

### GitLab 上的数据库负载均衡

使用 pgpool 和它的负载均衡特性，我们需要一些其它的东西去分发负载到多个热备服务器上。

对于（但不限于） Rails 应用程序，它有一个叫 [Makara](https://github.com/taskrabbit/makara) 的库，它实现了负载均衡的逻辑并包含了一个 ActiveRecord 的缺省实现。然而，Makara 也有一些我们认为是有些遗憾的问题。例如，它支持的粘连接是非常有限的：当你使用一个 cookie 和一个固定的 TTL 去执行一个写操作时，连接将粘到主服务器。这意味着，如果复制极大地滞后于 TTL，最终你可能会发现，你的查询运行在一个没有你需要的数据的主机上。

Makara 也需要你做很多配置，如所有的数据库主机和它们的角色，没有服务发现机制（我们当前的解决方案也不支持它们，即使它是将来计划的）。 Makara 也 [似乎不是线程安全的](https://github.com/taskrabbit/makara/issues/151)，这是有问题的，因为 Sidekiq （我们使用的后台进程）是多线程的。 最终，我们希望尽可能地去控制负载均衡的逻辑。

除了 Makara 之外 ，还有一个 [Octopus](https://github.com/thiagopradi/octopus) ，它也是内置的负载均衡机制。但是 Octopus 是面向分片数据库的，而不仅是均衡只读查询的。因此，最终我们不考虑使用 Octopus。

最终，我们直接在 GitLab EE构建了自己的解决方案。 添加初始实现的<ruby> 合并请求 <rt>  merge request </rt></ruby>可以在 [这里](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1283)找到，尽管一些更改、提升和修复是以后增加的。

我们的解决方案本质上是通过用一个处理查询的路由的代理对象替换 `ActiveRecord::Base.connection` 。这可以让我们均衡负载尽可能多的查询，甚至，包括不是直接来自我们的代码中的查询。这个代理对象基于调用方式去决定将查询转发到哪个主机， 消除了解析 SQL 查询的需要。

#### 粘连接

粘连接是通过在执行写入时，将当前 PostgreSQL WAL 位置存储到一个指针中实现支持的。在请求即将结束时，指针短期保存在 Redis 中。每个用户提供他自己的 key，因此，一个用户的动作不会导致其他的用户受到影响。下次请求时，我们取得指针，并且与所有的次级服务器进行比较。如果所有的次级服务器都有一个超过我们的指针的 WAL 指针，那么我们知道它们是同步的，我们可以为我们的只读查询安全地使用次级服务器。如果一个或多个次级服务器没有同步，我们将继续使用主服务器直到它们同步。如果 30 秒内没有写入操作，并且所有的次级服务器还没有同步，我们将恢复使用次级服务器，这是为了防止有些人的查询永远运行在主服务器上。

检查一个次级服务器是否就绪十分简单，它在如下的 `Gitlab::Database::LoadBalancing::Host#caught_up?` 中实现：

```
def caught_up?(location)
  string = connection.quote(location)

  query = "SELECT NOT pg_is_in_recovery() OR " \
    "pg_xlog_location_diff(pg_last_xlog_replay_location(), #{string}) >= 0 AS result"

  row = connection.select_all(query).first

  row && row['result'] == 't'
ensure
  release_connection
end

```

这里的大部分代码是运行原生查询（raw queries）和获取结果的标准的 Rails 代码，查询的最有趣的部分如下：

```
SELECT NOT pg_is_in_recovery()
OR pg_xlog_location_diff(pg_last_xlog_replay_location(), WAL-POINTER) >= 0 AS result"

```

这里 `WAL-POINTER` 是 WAL 指针，通过 PostgreSQL 函数 `pg_current_xlog_insert_location()` 返回的，它是在主服务器上执行的。在上面的代码片断中，该指针作为一个参数传递，然后它被引用或转义，并传递给查询。

使用函数 `pg_last_xlog_replay_location()` 我们可以取得次级服务器的 WAL 指针，然后，我们可以通过函数 `pg_xlog_location_diff()` 与我们的主服务器上的指针进行比较。如果结果大于 0 ，我们就可以知道次级服务器是同步的。

当一个次级服务器被提升为主服务器，并且我们的 GitLab 进程还不知道这一点的时候，添加检查 `NOT pg_is_in_recovery()` 以确保查询不会失败。在这个案例中，主服务器总是与它自己是同步的，所以它简单返回一个 `true`。

#### 后台进程

我们的后台进程代码 *总是* 使用主服务器，因为在后台执行的大部分工作都是写入。此外，我们不能可靠地使用一个热备机，因为我们无法知道作业是否在主服务器执行，也因为许多作业并没有直接绑定到用户上。

#### 连接错误

要处理连接错误，比如负载均衡器不会使用一个视作离线的次级服务器，会增加主机上（包括主服务器）的连接错误，将会导致负载均衡器多次重试。这是确保，在遇到偶发的小问题或数据库失败事件时，不会立即显示一个错误页面。当我们在负载均衡器级别上处理 [热备机冲突](https://www.postgresql.org/docs/current/static/hot-standby.html#HOT-STANDBY-CONFLICT) 的问题时，我们最终在次级服务器上启用了 `hot_standby_feedback` ，这样就解决了热备机冲突的所有问题，而不会对表膨胀造成任何负面影响。

我们使用的过程很简单：对于次级服务器，我们在它们之间用无延迟试了几次。对于主服务器，我们通过使用越来越快的回退尝试几次。

更多信息你可以查看 GitLab EE 上的源代码：

* <https://gitlab.com/gitlab-org/gitlab-ee/tree/master/lib/gitlab/database/load_balancing.rb>
* <https://gitlab.com/gitlab-org/gitlab-ee/tree/master/lib/gitlab/database/load_balancing>

数据库负载均衡首次引入是在 GitLab 9.0 中，并且 *仅* 支持 PostgreSQL。更多信息可以在 [9.0 release post](https://about.gitlab.com/2017/03/22/gitlab-9-0-released/) 和 [documentation](https://docs.gitlab.com/ee/administration/database_load_balancing.html) 中找到。

### Crunchy Data

我们与 [Crunchy Data](https://www.crunchydata.com/) 一起协同工作来部署连接池和负载均衡。不久之前我还是唯一的 [数据库专家](https://about.gitlab.com/handbook/infrastructure/database/)，它意味着我有很多工作要做。此外，我对 PostgreSQL 的内部细节的和它大量的设置所知有限 (或者至少现在是)，这意味着我能做的也有限。因为这些原因，我们雇用了 Crunchy 去帮我们找出问题、研究慢查询、建议模式优化、优化 PostgreSQL 设置等等。

在合作期间，大部分工作都是在相互信任的基础上完成的，因此，我们共享私人数据，比如日志。在合作结束时，我们从一些资料和公开的内容中删除了敏感数据，主要的资料在 [gitlab-com/infrastructure#1448](https://gitlab.com/gitlab-com/infrastructure/issues/1448)，这又反过来导致产生和解决了许多分立的问题。

这次合作的好处是巨大的，它帮助我们发现并解决了许多的问题，如果必须我们自己来做的话，我们可能需要花上几个月的时间来识别和解决它。

幸运的是，最近我们成功地雇佣了我们的 [第二个数据库专家](https://gitlab.com/_stark) 并且我们希望以后我们的团队能够发展壮大。

### 整合连接池和数据库负载均衡

整合连接池和数据库负载均衡可以让我们去大幅减少运行数据库集群所需要的资源和在分发到热备机上的负载。例如，以前我们的主服务器 CPU 使用率一直徘徊在 70%，现在它一般在 10% 到 20% 之间，而我们的两台热备机服务器则大部分时间在 20% 左右：

![CPU Percentage](/data/attachment/album/201711/11/141057u8e1omgqle3q5ge1.png)

在这里， `db3.cluster.gitlab.com` 是我们的主服务器，而其它的两台是我们的次级服务器。

其它的负载相关的因素，如平均负载、磁盘使用、内存使用也大为改善。例如，主服务器现在的平均负载几乎不会超过 10，而不像以前它一直徘徊在 20 左右：

![CPU Percentage](/data/attachment/album/201711/11/141057oezcimd2d77k8wpu.png)

在业务繁忙期间，我们的次级服务器每秒事务数在 12000 左右（大约为每分钟 740000），而主服务器每秒事务数在 6000 左右（大约每分钟 340000）：

![Transactions Per Second](/data/attachment/album/201711/11/141058o7zm773axqfqxa8m.png)

可惜的是，在部署 pgbouncer 和我们的数据库负载均衡器之前，我们没有关于事务速率的任何数据。

我们的 PostgreSQL 的最新统计数据的摘要可以在我们的 [public Grafana dashboard](http://monitor.gitlab.net/dashboard/db/postgres-stats?refresh=5m&orgId=1) 上找到。

我们的其中一些 pgbouncer 的设置如下：

| 设置 | 值 |
| --- | --- |
| `default_pool_size` | 100 |
| `reserve_pool_size` | 5 |
| `reserve_pool_timeout` | 3 |
| `max_client_conn` | 2048 |
| `pool_mode` | transaction |
| `server_idle_timeout` | 30 |

除了前面所说的这些外，还有一些工作要作，比如： 部署服务发现（[#2042](https://gitlab.com/gitlab-org/gitlab-ee/issues/2042)）， 持续改善如何检查次级服务器是否可用（[#2866](https://gitlab.com/gitlab-org/gitlab-ee/issues/2866)），和忽略落后于主服务器太多的次级服务器 （[#2197](https://gitlab.com/gitlab-org/gitlab-ee/issues/2197)）。

值得一提的是，到目前为止，我们还没有任何计划将我们的负载均衡解决方案，独立打包成一个你可以在 GitLab 之外使用的库，相反，我们的重点是为 GitLab EE 提供一个可靠的负载均衡解决方案。

如果你对它感兴趣，并喜欢使用数据库、改善应用程序性能、给 GitLab上增加数据库相关的特性（比如： [服务发现](https://gitlab.com/gitlab-org/gitlab-ee/issues/2042)），你一定要去查看一下我们的 [招聘职位](https://about.gitlab.com/jobs/specialist/database/) 和 [数据库专家手册](https://about.gitlab.com/handbook/infrastructure/database/) 去获取更多信息。

---

via: <https://about.gitlab.com/2017/10/02/scaling-the-gitlab-database/>

作者：[Yorick Peterse](https://about.gitlab.com/team/#yorickpeterse) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


由 KRACK 攻击想到的确保网络安全的小贴士

Summary: 最近的 KRACK （密钥重装攻击）漏洞攻击的目标是位于你的设备和 Wi-Fi 访问点之间的链路，这个访问点或许是在你家里、办公室中、或你喜欢的咖啡吧中的任何一台路由器。这些提示能帮你提升你的连接的安全性。

bdmarket.blogspot.com

>
> 最近的 KRACK （密钥重装攻击，这是一个安全漏洞名称或该漏洞利用攻击行为的名称）漏洞攻击的目标是位于你的设备和 Wi-Fi 访问点之间的链路，这个访问点或许是在你家里、办公室中、或你喜欢的咖啡吧中的任何一台路由器。这些提示能帮你提升你的连接的安全性。
>
>
>

![](/data/attachment/album/201711/11/235930sejrxs44dzvej4nm.jpg)

[KRACK 漏洞攻击](https://www.krackattacks.com/) 出现已经一段时间了，并且已经在 [相关技术网站](https://blog.cryptographyengineering.com/2017/10/16/falling-through-the-kracks/) 上有很多详细的讨论，因此，我将不在这里重复攻击的技术细节。攻击方式的总结如下：

* 在 WPA2 无线握手协议中的一个缺陷允许攻击者在你的设备和 wi-fi 访问点之间嗅探或操纵通讯。
* 这个问题在 Linux 和 Android 设备上尤其严重，由于在 WPA2 标准中的措辞含糊不清，也或许是在实现它时的错误理解，事实上，在底层的操作系统打完补丁以前，该漏洞一直可以强制无线流量以无加密方式通讯。
* 还好这个漏洞可以在客户端上修补，因此，天并没有塌下来，而且，WPA2 加密标准并没有像 WEP 标准那样被淘汰（不要通过切换到 WEP 加密的方式去“修复”这个问题）。
* 大多数流行的 Linux 发行版都已经通过升级修复了这个客户端上的漏洞，因此，老老实实地去更新它吧。
* Android 也很快修复了这个漏洞。如果你的设备在接收 Android 安全补丁，你会很快修复这个漏洞。如果你的设备不再接收这些更新，那么，这个特别的漏洞将是你停止使用你的旧设备的一个理由。

即使如此，从我的观点来看， Wi-Fi 是不可信任的基础设施链中的另一个环节，并且，我们应该完全避免将其视为可信任的通信通道。

### Wi-Fi 是不受信任的基础设备

如果从你的笔记本电脑或移动设备中读到这篇文章，那么，你的通信链路看起来应该是这样：

![](/data/attachment/album/201711/12/000056tuq2ae77752z7y7u.png)

KRACK 攻击目标是在你的设备和 Wi-Fi 访问点之间的链接，访问点或许是在你家里、办公室中、或你喜欢的咖啡吧中的任何一台路由器。

![](/data/attachment/album/201711/12/000128yjiibyc4ympmofoh.png)

实际上，这个图示应该看起来像这样：

![](/data/attachment/album/201711/12/000154ejq6qovjqqgqvinv.png)

Wi-Fi 仅仅是在我们所不应该信任的信道上的长长的通信链的第一个链路。让我来猜猜，你使用的 Wi-Fi 路由器或许从开始使用的第一天气就没有得到过一个安全更新，并且，更糟糕的是，它或许使用了一个从未被更改过的、缺省的、易猜出的管理凭据（用户名和密码）。除非你自己安装并配置你的路由器，并且你能记得你上次更新的它的固件的时间，否则，你应该假设现在它已经被一些人控制并不能信任的。

在 Wi-Fi 路由器之后，我们的通讯进入一般意义上的常见不信任区域 —— 这要根据你的猜疑水平。这里有上游的 ISP 和接入提供商，其中的很多已经被指认监视、更改、分析和销售我们的流量数据，试图从我们的浏览习惯中挣更多的钱。通常他们的安全补丁计划辜负了我们的期望，最终让我们的流量暴露在一些恶意者眼中。

一般来说，在互联网上，我们还必须担心强大的国家级的参与者能够操纵核心网络协议，以执行大规模的网络监视和状态级的流量过滤。

### HTTPS 协议

值的庆幸的是，我们有一个基于不信任的介质进行安全通讯的解决方案，并且，我们可以每天都能使用它 —— 这就是 HTTPS 协议，它加密你的点对点的互联网通讯，并且确保我们可以信任站点与我们之间的通讯。

Linux 基金会的一些措施，比如像 [Let’s Encrypt](https://letsencrypt.org/) 使世界各地的网站所有者都可以很容易地提供端到端的加密，这有助于确保我们的个人设备与我们试图访问的网站之间的任何有安全隐患的设备不再是个问题。

![](/data/attachment/album/201711/12/000307rteezzv501h4n1yz.png)

是的... 基本没关系。

### DNS —— 剩下的一个问题

虽然，我们可以尽量使用 HTTPS 去创建一个可信的通信信道，但是，这里仍然有一个攻击者可以访问我们的路由器或修改我们的 Wi-Fi 流量的机会 —— 在使用 KRACK 的这个案例中 —— 可以欺骗我们的通讯进入一个错误的网站。他们可以利用我们仍然非常依赖 DNS 的这一事实 —— 这是一个未加密的、易受欺骗的 [诞生自上世纪 80 年代的协议](https://en.wikipedia.org/wiki/Domain_Name_System#History)。

![](/data/attachment/album/201711/12/000333jrga8jza0mmmmd3l.png)

DNS 是一个将像 “linux.com” 这样人类友好的域名，转换成计算机可以用于和其它计算机通讯的 IP 地址的一个系统。要转换一个域名到一个 IP 地址，计算机将会查询解析器软件 —— 它通常运行在 Wi-Fi 路由器或一个系统上。解析器软件将查询一个分布式的“根”域名服务器网络，去找到在互联网上哪个系统有 “linux.com” 域名所对应的 IP 地址的“权威”信息。

麻烦就在于，所有发生的这些通讯都是未经认证的、[易于欺骗的](https://en.wikipedia.org/wiki/DNS_spoofing)、明文协议、并且响应可以很容易地被攻击者修改，去返回一个不正确的数据。如果有人去欺骗一个 DNS 查询并且返回错误的 IP 地址，他们可以操纵我们的系统最终发送 HTTP 请求到那里。

幸运的是，HTTPS 有一些内置的保护措施去确保它不会很容易地被其它人诱导至其它假冒站点。恶意服务器上的 TLS 凭据必须与你请求的 DNS 名字匹配 —— 并且它必须由一个你的浏览器认可的信誉良好的 [认证机构（CA）](https://en.wikipedia.org/wiki/Certificate_authority) 所签发。如果不是这种情况，你的浏览器将在你试图去与他们告诉你的地址进行通讯时出现一个很大的警告。如果你看到这样的警告，在选择不理会警告之前，请你格外小心，因为，它有可能会把你的秘密泄露给那些可能会对付你的人。

如果攻击者完全控制了路由器，他们可以在一开始时，通过拦截来自服务器指示你建立一个安全连接的响应，以阻止你使用 HTTPS 连接（这被称为 “[SSL 脱衣攻击](https://en.wikipedia.org/wiki/Moxie_Marlinspike#Notable_research)”）。 为了帮助你防护这种类型的攻击，网站可以增加一个 [特殊响应头（HSTS）](https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) 去告诉你的浏览器以后与它通讯时总是使用 HTTPS 协议，但是，这仅仅是在你首次访问之后的事。对于一些非常流行的站点，浏览器现在包含一个 [硬编码的域名列表](https://hstspreload.org/)，即使是首次连接，它也将总是使用 HTTPS 协议访问。

现在已经有了 DNS 欺骗的解决方案，它被称为 [DNSSEC](https://en.wikipedia.org/wiki/Domain_Name_System_Security_Extensions)，由于有重大的障碍 —— 真实和可感知的（LCTT 译注，指的是要求实名认证），它看起来接受程度很慢。在 DNSSEC 被普遍使用之前，我们必须假设，我们接收到的 DNS 信息是不能完全信任的。

### 使用 VPN 去解决“最后一公里”的安全问题

因此，如果你不能信任固件太旧的 Wi-Fi 和/或无线路由器，我们能做些什么来确保发生在你的设备与常说的互联网之间的“最后一公里”通讯的完整性呢？

一个可接受的解决方案是去使用信誉好的 VPN 供应商的服务，它将在你的系统和他们的基础设施之间建立一条安全的通讯链路。这里有一个期望，就是它比你的路由器提供者和你的当前互联网供应商更注重安全，因为，他们处于一个更好的位置去确保你的流量不会受到恶意的攻击或欺骗。在你的工作站和移动设备之间使用 VPN，可以确保免受像 KRACK 这样的漏洞攻击，不安全的路由器不会影响你与外界通讯的完整性。

![](/data/attachment/album/201711/12/000401m1jhht6j11s9h586.png)

这有一个很重要的警告是，当你选择一个 VPN 供应商时，你必须确信他们的信用；否则，你将被一拨恶意的人出卖给其它人。远离任何人提供的所谓“免费 VPN”，因为，它们可以通过监视你和向市场营销公司销售你的流量来赚钱。 [这个网站](https://www.vpnmentor.com/bestvpns/overall/) 是一个很好的资源，你可以去比较他们提供的各种 VPN，去看他们是怎么互相竞争的。

注意，你所有的设备都应该在它上面安装 VPN，那些你每天使用的网站，你的私人信息，尤其是任何与你的钱和你的身份（政府、银行网站、社交网络、等等）有关的东西都必须得到保护。VPN 并不是对付所有网络级漏洞的万能药，但是，当你在机场使用无法保证的 Wi-Fi 时，或者下次发现类似 KRACK 的漏洞时，它肯定会保护你。

---

via: <https://www.linux.com/blog/2017/10/tips-secure-your-network-wake-krack>

作者：[KONSTANTIN RYABITSEV](https://www.linux.com/users/mricon) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


瞬间提升命令行的生产力 100%

![](/data/attachment/album/201711/12/010750vk5tkm5ipkxxi59v.png)

关于生产力的话题总是让人充满兴趣的。

这里有许多方式提升你的生产力。今天，我共享一些命令行的小技巧，以及让你的人生更轻松的小秘诀。

### TL;DR

在本文中讨论的内容的全部设置及更多的信息，可以查看： <https://github.com/sobolevn/dotfiles> 。

### Shell

使用一个好用的，并且稳定的 shell 对你的命令行生产力是非常关键的。这儿有很多选择，我喜欢 `zsh` 和 `oh-my-zsh`。它是非常神奇的，理由如下：

* 自动补完几乎所有的东西
* 大量的插件
* 确实有用且能定制化的“提示符”

你可以通过下列的步骤去安装它：

1. 安装 `zsh`： <https://github.com/robbyrussell/oh-my-zsh/wiki/Installing-ZSH>
2. 安装 `oh-my-zsh`： <http://ohmyz.sh/>
3. 选择对你有用的插件： <https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins>

你也可以调整你的设置以 [关闭自动补完的大小写敏感](https://github.com/sobolevn/dotfiles/blob/master/zshrc#L12) ，或改变你的 [命令行历史的工作方式](https://github.com/sobolevn/dotfiles/blob/master/zshrc#L24)。

就是这样。你将立马获得 +50% 的生产力提升。现在你可以打开足够多的选项卡（tab）了！（LCTT 译注：指多选项卡的命令行窗口）

### 主题

选择主题也很重要，因为你从头到尾都在看它。它必须是有用且漂亮的。我也喜欢简约的主题，因为它不包含一些视觉噪音和没用的信息。

你的主题将为你展示：

* 当前文件夹
* 当前的版本分支
* 当前版本库状态：干净或脏的（LCTT 译注：指是否有未提交版本库的内容）
* 任何的错误返回码（如果有）（LCTT 译注：Linux 命令如果执行错误，会返回错误码）

我也喜欢我的主题可以在新起的一行输入新命令，这样就有足够的空间去阅读和书写命令了。

我个人使用 [`sobole`](https://github.com/sobolevn/sobole-zsh-theme) 主题。它看起来非常棒，它有两种模式。

亮色的：

[![sobole.zsh-theme](/data/attachment/album/201711/12/010801wbcyg01ayi2qjb0m.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--Lz_uthoR--/c_limit,f_auto,fl_progressive,q_auto,w_880/https://raw.githubusercontent.com/sobolevn/sobole-zsh-theme/master/showcases/env-and-user.png)

以及暗色的：

[![sobole.zsh-theme](/data/attachment/album/201711/12/010802eyryor5apmsbt3m6.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--4o6hZwL9--/c_limit,f_auto,fl_progressive,q_auto,w_880/https://raw.githubusercontent.com/sobolevn/sobole-zsh-theme/master/showcases/dark-mode.png)

你得到了另外 +15% 的提升，以及一个看起来很漂亮的主题。

### 语法高亮

对我来说，从我的 shell 中得到足够的可视信息对做出正确的判断是非常重要的。比如 “这个命令有没有拼写错误？” 或者 “这个命令有相应的作用域吗？” 这样的提示。我经常会有拼写错误。

因此， [`zsh-syntax-highlighting`](https://github.com/zsh-users/zsh-syntax-highlighting) 对我是非常有用的。 它有合适的默认值，当然你可以 [改变任何你想要的设置](https://github.com/zsh-users/zsh-syntax-highlighting/blob/master/docs/highlighters.md)。

这个步骤可以带给我们额外的 +5% 的提升。

### 文件处理

我在我的目录中经常遍历许多文件，至少看起来很多。我经常做这些事情：

* 来回导航
* 列出文件和目录
* 显示文件内容

我喜欢去使用 [`z`](https://github.com/rupa/z) 导航到我已经去过的文件夹。这个工具是非常棒的。 它使用“<ruby> 近常 <rt>  frecency </rt></ruby>” 方法来把你输入的 `.dot TAB` 转换成 `~/dev/shell/config/.dotfiles`。真的很不错！

当你显示文件时，你通常要了解如下几个内容：

* 文件名
* 权限
* 所有者
* 这个文件的 git 版本状态
* 修改日期
* 人类可读形式的文件大小

你也或许希望缺省展示隐藏文件。因此，我使用 [`exa`](https://github.com/ogham/exa) 来替代标准的 `ls`。为什么呢？因为它缺省启用了很多的东西：

[![exa](/data/attachment/album/201711/12/010808pz0d222j277770j3.png)](https://res.cloudinary.com/practicaldev/image/fetch/s--n_YCO9Hj--/c_limit,f_auto,fl_progressive,q_auto,w_880/https://raw.githubusercontent.com/ogham/exa/master/screenshots.png)

要显示文件内容，我使用标准的 `cat`，或者，如果我希望看到语法高亮，我使用一个定制的别名：

```
# exa:
alias la="exa -abghl --git --color=automatic"

# `cat` with beautiful colors. requires: pip install -U Pygments
alias c='pygmentize -O style=borland -f console256 -g'

```

现在，你已经掌握了导航。它使你的生产力提升 +15% 。

### 搜索

当你在应用程序的源代码中搜索时，你不会想在你的搜索结果中缺省包含像 `node_modules` 或 `bower_components` 这样的文件夹。或者，当你想搜索执行的更快更流畅时。

这里有一个比内置的搜索方式更好的替代： [`the_silver_searcher`](https://github.com/ggreer/the_silver_searcher)。

它是用纯 `C` 写成的，并且使用了很多智能化的逻辑让它工作的更快。

在命令行 `history` 中，使用 `ctrl` + `R` 进行 [反向搜索](https://unix.stackexchange.com/questions/73498/how-to-cycle-through-reverse-i-search-in-bash) 是非常有用的。但是，你有没有发现你自己甚至不能完全记住一个命令呢？如果有一个工具可以模糊搜索而且用户界面更好呢？

这里确实有这样一个工具。它叫做 `fzf`：

![fzf](/data/attachment/album/201711/12/004508bhx4ll28gzzgnyvv.png)

它可以被用于任何模糊查询，而不仅是在命令行历史中，但它需要 [一些配置](https://github.com/sobolevn/dotfiles/blob/master/shell/.external#L19)。

你现在有了一个搜索工具，可以额外提升 +15% 的生产力。

### 延伸阅读

更好地使用命令行： <https://dev.to/sobolevn/using-better-clis-6o8>。

### 总结

通过这些简单的步骤，你可以显著提升你的命令行的生产力 +100% 以上（数字是估计的）。

这里还有其它的工具和技巧，我将在下一篇文章中介绍。

你喜欢阅读软件开发方面的最新趋势吗？在这里订阅我们的愽客吧 <https://medium.com/wemake-services>。

---

via: <https://dev.to/sobolevn/instant-100-command-line-productivity-boost>

作者：[Nikita Sobolev](https://dev.to/sobolevn) 译者：[qhwdw](https://github.com/qhwdw) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


开源软件对于商业机构的六个好处

>
> 这就是为什么商业机构应该选择开源模式的原因
>
>
>

![](/data/attachment/album/201711/12/130405rp72ztz7p0022qtv.jpeg)

在相同的基础上，开源软件要优于专有软件。想知道为什么？这里有六个商业机构及政府部门可以从开源技术中获得好处的原因。

### 1、 让供应商审核更简单

在你投入工程和金融资源将一个产品整合到你的基础设施之前，你需要知道你选择了正确的产品。你想要一个处于积极开发的产品，它有定期的安全更新和漏洞修复，同时在你有需求时，产品能有相应的更新。这最后一点也许比你想的还要重要：没错，解决方案一定是满足你的需求的。但是产品的需求随着市场的成熟以及你商业的发展在变化。如果该产品随之改变，在未来你需要花费很大的代价来进行迁移。

你怎么才能知道你没有正在把你的时间和资金投入到一个正在消亡的产品？在开源的世界里，你可以不选择一个只有卖家有话语权的供应商。你可以通过考虑[发展速度以及社区健康程度](https://nextcloud.com/blog/nextcloud-the-most-active-open-source-file-sync-and-share-project/)来比较供应商。一到两年之后，一个更活跃、多样性和健康的社区将开发出一个更好的产品，这是一个重要的考虑因素。当然，就像这篇 [关于企业开源软件的博文](http://www.redhat-cloudstrategy.com/open-source-for-business-people/) 指出的，供应商必须有能力处理由于项目发展创新带来的不稳定性。寻找一个有长支持周期的供应商来避免混乱的更新。

### 2、 来自独立性的长寿

福布斯杂志指出 [90%的初创公司是失败的](http://www.forbes.com/sites/neilpatel/2015/01/16/90-of-startups-will-fail-heres-what-you-need-to-know-about-the-10/) ，而他们中不到一半的中小型公司能存活超过五年。当你不得不迁移到一个新的供应商时，你花费的代价是昂贵的。所以最好避免一些只有一个供应商支持的产品。

开源使得社区成员能够协同编写软件。例如 OpenStack [是由许多公司以及个人志愿者一起编写的](http://stackalytics.com/)，这给客户提供了一个保证，不管任何一个独立供应商发生问题，也总会有一个供应商能提供支持。随着软件的开源，企业会长期投入开发团队，以实现产品开发。能够使用开源代码可以确保你总是能从贡献者中雇佣到人来保持你的开发活跃。当然，如果没有一个大的、活跃的社区，也就只有少量的贡献者能被雇佣，所以活跃贡献者的数量是重要的。

### 3、 安全性

安全是一件复杂的事情。这就是为什么开源开发是构建安全解决方案的关键因素和先决条件。同时每一天安全都在变得更重要。当开发以开源方式进行，你能直接的校验供应商是否积极的在追求安全，以及看到供应商是如何对待安全问题的。研究代码和执行独立代码审计的能力可以让供应商尽可能早的发现和修复漏洞。一些供应商给社区提供上千的美金的[漏洞奖金](https://hackerone.com/nextcloud)作为额外的奖励来鼓励开发者发现他们产品的安全漏洞，这同时也展示了供应商对于自己产品的信任。

除了代码，开源开发同样意味着开源过程，所以你能检查和看到供应商是否遵循 ISO27001、 [云安全准则](https://www.ncsc.gov.uk/guidance/implementing-cloud-security-principles) 及其他标准所推荐的工业级的开发过程。当然，一个可信组织外部检查提供了额外的保障，就像我们在 Nextcloud 与 [NCC小组](https://nextcloud.com/secure)合作的一样。

### 4、 更多的顾客导向

由于用户和顾客能直接看到和参与到产品的开发中，开源项目比那些只关注于营销团队回应的闭源软件更加的贴合用户的需求。你可以注意到开源软件项目趋向于以“宽松”方式发展。一个商业供应商也许关注在某个特定的事情方面，而一个社区则有许多要做的事情并致力于开发更多的功能，而这些全都是公司或个人贡献者中的某人或某些人所感兴趣的。这导致更少的为了销售而发布的版本，因为各种改进混搭在一起根本就不是一回事。但是它创造了许多对用户更有价值的产品。

### 5、 更好的支持

专有供应商通常是你遇到问题时唯一能给你提供帮助的一方。但如果他们不提供你所需要的服务，或者对调整你的商务需求收取额外昂贵的费用，那真是不好运。对专有软件提供的支持是一个典型的 “[柠檬市场](https://en.wikipedia.org/wiki/The_Market_for_Lemons)”。 随着软件的开源，供应商要么提供更大的支持，要么就有其它人来填补空白——这是自由市场的最佳选择，这可以确保你总是能得到最好的服务支持。

### 6、 更佳的许可

典型的软件许可证[充斥着令人不愉快的条款](http://boingboing.net/2016/11/01/why-are-license-agreements.html)，通常都是强制套利，你甚至不会有机会起诉供应商的不当行为。其中一个问题是你仅仅被授予了软件的使用权，这通常完全由供应商自行决定。如果软件不运行或者停止运行或者如果供应商要求支付更多的费用，你得不到软件的所有权或其他的权利。像 GPL 一类的开源许可证是为保护客户专门设计的，而不是保护供应商，它确保你可以如你所需的使用软件，而没有专制限制，只要你喜欢就行。

由于它们的广泛使用，GPL 的含义及其衍生出来的其他许可证得到了广泛的理解。例如，你能确保该许可允许你现存的基础设施（开源或闭源）通过设定好的 API 去进行连接，其没有时间或者是用户人数上的限制，同时也不会强迫你公开软件架构或者是知识产权（例如公司商标）。

这也让合规变得更加的容易；使用专有软件意味着你面临着苛刻的法规遵从性条款和高额的罚款。更糟糕的是一些<ruby> 开源内核 <rt>  open core </rt></ruby>的产品在混合了 GPL 软件和专有软件。这[违反了许可证规定](https://www.gnu.org/licenses/gpl-faq.en.html#GPLPluginsInNF)并将顾客置于风险中。同时 Gartner 指出，开源内核模式意味着你[不能从开源中获利](http://blogs.gartner.com/brian_prentice/2010/03/31/open-core-the-emperors-new-clothes/)。纯净的开源许可的产品避免了所有这些问题。取而代之，你只需要遵从一个规则：如果你对代码做出了修改（不包括配置、商标或其他类似的东西），你必须将这些与你的软件分发随同分享，如果他们要求的话。

显然开源软件是更好的选择。它易于选择正确的供应商（不会被供应商锁定），加之你也可以受益于更安全、对客户更加关注和更好的支持。而最后，你将处于法律上的安全地位。

---

作者简介：

一个善于与人打交道的技术爱好者和开源传播者。Nextcloud 的销售主管，曾是 ownCloud 和 SUSE 的社区经理，同时还是一个有多年经验的 KDE 销售人员。喜欢骑自行车穿越柏林和为家人朋友做饭。[点击这里找到我的博客](http://blog.jospoortvliet.com/)。

---

via: <https://opensource.com/article/17/10/6-reasons-choose-open-source-software>

作者：[Jos Poortvliet Feed](https://opensource.com/users/jospoortvliet) 译者：[ZH1122](https://github.com/ZH1122) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


容器和微服务是如何改变了安全性

>
> 云原生程序和基础架构需要完全不同的安全方式。牢记这些最佳实践
>
>
>

![](/data/attachment/album/201711/13/154225idzjqsdz5v1ld1de.jpeg)

如今，大大小小的组织正在探索云原生技术的采用。“<ruby> 云原生 <rt>  Cloud-native </rt></ruby>”是指将软件打包到被称为容器的标准化单元中的方法，这些单元组织成微服务，它们必须对接以形成程序，并确保正在运行的应用程序完全自动化以实现更高的速度、灵活性和可伸缩性。

由于这种方法从根本上改变了软件的构建、部署和运行方式，它也从根本上改变了软件需要保护的方式。云原生程序和基础架构为安全专业人员带来了若干新的挑战，他们需要建立新的安全计划来支持其组织对云原生技术的使用。

让我们来看看这些挑战，然后我们将讨论安全团队应该采取的哪些最佳实践来解决这些挑战。首先挑战是：

* **传统的安全基础设施缺乏容器可视性。** 大多数现有的基于主机和网络的安全工具不具备监视或捕获容器活动的能力。这些工具是为了保护单个操作系统或主机之间的流量，而不是其上运行的应用程序，从而导致容器事件、系统交互和容器间流量的可视性缺乏。
* **攻击面可以快速更改。**云原生应用程序由许多较小的组件组成，这些组件称为微服务，它们是高度分布式的，每个都应该分别进行审计和保护。因为这些应用程序的设计是通过编排系统进行配置和调整的，所以其攻击面也在不断变化，而且比传统的独石应用程序要快得多。
* **分布式数据流需要持续监控。**容器和微服务被设计为轻量级的，并且以可编程方式与对方或外部云服务进行互连。这会在整个环境中产生大量的快速移动数据，需要进行持续监控，以便应对攻击和危害指标以及未经授权的数据访问或渗透。
* **检测、预防和响应必须自动化。** 容器生成的事件的速度和容量压倒了当前的安全操作流程。容器的短暂寿命也成为难以捕获、分析和确定事故的根本原因。有效的威胁保护意味着自动化数据收集、过滤、关联和分析，以便能够对新事件作出足够快速的反应。

面对这些新的挑战，安全专业人员将需要建立新的安全计划以支持其组织对云原生技术的使用。自然地，你的安全计划应该解决云原生程序的整个生命周期的问题，这些应用程序可以分为两个不同的阶段：构建和部署阶段以及运行时阶段。每个阶段都有不同的安全考虑因素，必须全部加以解决才能形成一个全面的安全计划。

### 确保容器的构建和部署

构建和部署阶段的安全性侧重于将控制应用于开发人员工作流程和持续集成和部署管道，以降低容器启动后可能出现的安全问题的风险。这些控制可以包含以下准则和最佳实践：

* **保持镜像尽可能小。**容器镜像是一个轻量级的可执行文件，用于打包应用程序代码及其依赖项。将每个镜像限制为软件运行所必需的内容, 从而最小化从镜像启动的每个容器的攻击面。从最小的操作系统基础镜像（如 Alpine Linux）开始，可以减少镜像大小，并使镜像更易于管理。
* **扫描镜像的已知问题。**当镜像构建后，应该检查已知的漏洞披露。可以扫描构成镜像的每个文件系统层，并将结果与定期更新的常见漏洞披露数据库（CVE）进行比较。然后开发和安全团队可以在镜像被用来启动容器之前解决发现的漏洞。
* **数字签名的镜像。**一旦建立镜像，应在部署之前验证它们的完整性。某些镜像格式使用被称为摘要的唯一标识符，可用于检测镜像内容何时发生变化。使用私钥签名镜像提供了加密的保证，以确保每个用于启动容器的镜像都是由可信方创建的。
* **强化并限制对主机操作系统的访问。**由于在主机上运行的容器共享相同的操作系统，因此必须确保它们以适当限制的功能集启动。这可以通过使用内核安全功能和 Seccomp、AppArmor 和 SELinux 等模块来实现。
* **指定应用程序级别的分割策略。**微服务之间的网络流量可以被分割，以限制它们彼此之间的连接。但是，这需要根据应用级属性（如标签和选择器）进行配置，从而消除了处理传统网络详细信息（如 IP 地址）的复杂性。分割带来的挑战是，必须事先定义策略来限制通信，而不会影响容器在环境内部和环境之间进行通信的能力，这是正常活动的一部分。
* **保护容器所使用的秘密信息。**微服务彼此相互之间频繁交换敏感数据，如密码、令牌和密钥，这称之为<ruby> 秘密信息 <rt>  secret </rt></ruby>。如果将这些秘密信息存储在镜像或环境变量中，则可能会意外暴露这些。因此，像 Docker 和 Kubernetes 这样的多个编排平台都集成了秘密信息管理，确保只有在需要的时候才将秘密信息分发给使用它们的容器。

来自诸如 Docker、Red Hat 和 CoreOS 等公司的几个领先的容器平台和工具提供了部分或全部这些功能。开始使用这些方法之一是在构建和部署阶段确保强大安全性的最简单方法。

但是，构建和部署阶段控制仍然不足以确保全面的安全计划。提前解决容器开始运行之前的所有安全事件是不可能的，原因如下：首先，漏洞永远不会被完全消除，新的漏洞会一直出现。其次，声明式的容器元数据和网络分段策略不能完全预见高度分布式环境中的所有合法应用程序活动。第三，运行时控制使用起来很复杂，而且往往配置错误，就会使应用程序容易受到威胁。

### 在运行时保护容器

运行时阶段的安全性包括所有功能（可见性、检测、响应和预防），这些功能是发现和阻止容器运行后发生的攻击和策略违规所必需的。安全团队需要对安全事件的根源进行分类、调查和确定，以便对其进行全面补救。以下是成功的运行时阶段安全性的关键方面：

* **检测整个环境以得到持续可见性。**能够检测攻击和违规行为始于能够实时捕获正在运行的容器中的所有活动，以提供可操作的“真相源”。捕获不同类型的容器相关数据有各种检测框架。选择一个能够处理容器的容量和速度的方案至关重要。
* **关联分布式威胁指标。** 容器设计为基于资源可用性以跨计算基础架构而分布。由于应用程序可能由数百或数千个容器组成，因此危害指标可能分布在大量主机上，使得难以确定那些与主动威胁相关的相关指标。需要大规模，快速的相关性来确定哪些指标构成特定攻击的基础。
* **分析容器和微服务行为。**微服务和容器使得应用程序可以分解为执行特定功能的最小组件，并被设计为不可变的。这使得比传统的应用环境更容易理解预期行为的正常模式。偏离这些行为基准可能反映恶意行为，可用于更准确地检测威胁。
* **通过机器学习增强威胁检测。**容器环境中生成的数据量和速度超过了传统的检测技术。自动化和机器学习可以实现更有效的行为建模、模式识别和分类，从而以更高的保真度和更少的误报来检测威胁。注意使用机器学习的解决方案只是为了生成静态白名单，用于警报异常，这可能会导致严重的警报噪音和疲劳。
* **拦截并阻止未经授权的容器引擎命令。**发送到容器引擎（例如 Docker）的命令用于创建、启动和终止容器以及在正在运行的容器中运行命令。这些命令可以反映危害容器的意图，这意味着可以禁止任何未经授权的命令。
* **自动响应和取证。**容器的短暂寿命意味着它们往往只能提供很少的事件信息，以用于事件响应和取证。此外，云原生架构通常将基础设施视为不可变的，自动将受影响的系统替换为新系统，这意味着在调查时的容器可能会消失。自动化可以确保足够快地捕获、分析和升级信息，以减轻攻击和违规的影响。

基于容器技术和微服务架构的云原生软件正在迅速实现应用程序和基础架构的现代化。这种模式转变迫使安全专业人员重新考虑有效保护其组织所需的计划。随着容器的构建、部署和运行，云原生软件的全面安全计划将解决整个应用程序生命周期问题。通过使用上述指导方针实施计划，组织可以为容器基础设施以及运行在上面的应用程序和服务构建安全的基础。

---

作者：WeLien Dang 是 StackRox 的产品副总裁，StackRox 是一家为容器提供自适应威胁保护的安全公司。此前，他曾担任 CoreOS 产品负责人，并在亚马逊、Splunk 和 Bracket Computing 担任安全和云基础架构的高级产品管理职位。

---

via: <https://www.infoworld.com/article/3233139/cloud-computing/how-cloud-native-applications-change-security.html>

作者：[Wei Lien Dang](https://www.infoworld.com/blog/new-tech-forum/) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


使用 mlocate 查找文件

Summary: 以前你可能已经发现 find 命令非常不听话，让你经常抓耳挠腮。你知道，丢失了一个分号或一个没有正确转义的特殊的字符就会这样。现在让我们离开这个复杂的 find 命令，放松一下，看一下这个聪明的小命令。

在这一系列的文章中，我们将来看下 `mlocate`，来看看如何快速、轻松地满足你的需求。

![](/data/attachment/album/201711/14/121507ebabmpj9amg733gy.jpg)

对于一个系统管理员来说，草中寻针一样的查找文件的事情并不少见。在一台拥挤的机器上，文件系统中可能存在数十万个文件。当你需要确定一个特定的配置文件是最新的，但是你不记得它在哪里时怎么办？

如果你使用过一些类 Unix 机器，那么你肯定用过 `find` 命令。毫无疑问，它是非常复杂和功能强大的。以下是一个只搜索目录中的符号链接，而忽略文件的例子：

```
# find . -lname "*"

```

你可以用 `find` 命令做几乎无尽的事情，这是不容否认的。`find` 命令好用的时候是很好且简洁的，但是它也可以很复杂。这不一定是因为 `find` 命令本身的原因，而是它与 `xargs` 结合，你可以传递各种选项来调整你的输出，并删除你找到的那些文件。

### 位置、位置，让人沮丧

然而，通常情况下简单是最好的选择，特别是当一个脾气暴躁的老板搭着你的肩膀，闲聊着时间的重要性时。你还在模糊地猜测这个你从来没有见过的文件的路径，而你的老板却肯定它在拥挤的 /var 分区的某处。

进一步看下 `mlocate`。你也许注意过它的一个近亲：`slocate`，它安全地（注意前缀字母 s 代表安全）记录了相关的文件权限，以防止非特权用户看到特权文件。此外，还有它们所起源的一个更老的，原始 `locate` 命令。

`mlocate` 与其家族的其他成员（至少包括 `slocate`）的不同之处在于，在扫描文件系统时，`mlocate` 不需要持续重新扫描所有的文件系统。相反，它将其发现的文件（注意前面的 m 代表合并）与现有的文件列表合并在一起，使其可以借助系统缓存从而性能更高、更轻量级。

在本系列文章中，我们将更仔细地了解 `mlocate` （由于其流行，所以也简称其为 `locate`），并研究如何快速轻松地将其调整到你心中所想的方式。

### 小巧和 紧凑

除非你经常重复使用复杂的命令，否则就会像我一样，最终会忘记它们而需要在用的时候寻找它们。`locate` 命令的优点是可以快速查询整个文件系统，而不用担心你处于顶层目录、根目录和所在路径，只需要简单地使用 `locate` 命令。

以前你可能已经发现 `find` 命令非常不听话，让你经常抓耳挠腮。你知道，丢失了一个分号或一个没有正确转义的特殊的字符就会这样。现在让我们离开这个复杂的 `find` 命令，放松一下，看一下这个聪明的小命令。

你可能需要首先通过运行以下命令来检查它是否在你的系统上：

对于 Red Hat 家族：

```
# yum install mlocate

```

对于 Debian 家族：

```
# apt-get install mlocate

```

发行版之间不应该有任何区别，但版本之间几乎肯定有细微差别。小心。

接下来，我们将介绍 `locate` 命令的一个关键组件，名为 `updatedb`。正如你可能猜到的那样，这是**更新** `locate` 命令的**数据库**的命令。这名字非常符合直觉。

这个数据库是我之前提到的 `locate` 命令的文件列表。该列表被保存在一个相对简单而高效的数据库中。`updatedb` 通过 cron 任务定期运行，通常在一天中的安静时间运行。在下面的清单 1 中，我们可以看到文件 `/etc/cron.daily/mlocate.cron` 的内部（该文件的路径及其内容可能因发行版不同）。

```
#!/bin/sh

nodevs=$(< /proc/filesystems awk '$1 == "nodev" { print $2 }')

renice +19 -p $$ >/dev/null 2>&1

ionice -c2 -n7 -p $$ >/dev/null 2>&1

/usr/bin/updatedb -f "$nodevs"

```

**清单 1：** 每天如何触发 “updatedb” 命令。

如你所见，`mlocate.cron` 脚本使用了优秀的 `nice` 命令来尽可能少地影响系统性能。我还没有明确指出这个命令每天都在设定的时间运行（但如果我没有记错的话，原始的 `locate` 命令与你在午夜时的计算机减速有关）。这是因为，在一些 “cron” 版本上，延迟现在被引入到隔夜开始时间。

这可能是因为所谓的 “<ruby> 河马之惊群 <rt>  Thundering Herd of Hippos </rt></ruby>”问题。想象许多计算机（或饥饿的动物）同时醒来从单一或有限的来源要求资源（或食物）。当所有的“河马”都使用 NTP 设置它们的手表时，这可能会发生（好吧，这个寓言扯多了，但请忍受一下）。想象一下，正好每五分钟（就像一个 “cron 任务”），它们都要求获得食物或其他东西。

如果你不相信我，请看下配置文件 - 清单 2 中名为 `anacron` 的 cron 版本，这是文件 `/etc/anacrontab` 的内容。

```
# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh

PATH=/sbin:/bin:/usr/sbin:/usr/bin

MAILTO=root

# the maximal random delay added to the base delay of the jobs

RANDOM_DELAY=45

# the jobs will be started during the following hours only

START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command

1       5       cron.daily              nice run-parts /etc/cron.daily

7       25      cron.weekly             nice run-parts /etc/cron.weekly

@monthly 45     cron.monthly            nice run-parts /etc/cron.monthly

```

**清单 2：** 运行 “cron” 任务时延迟是怎样被带入的。

从清单 2 可以看到 `RANDOM_DELAY` 和 “delay in minutes” 列。如果你不熟悉 cron 这个方面，那么你可以在这找到更多的东西：

```
# man anacrontab

```

否则，如果你愿意，你可以自己延迟一下。有个[很棒的网页](http://www.moundalexis.com/archives/000076.php)（现在已有十多年的历史）以非常合理的方式讨论了这个问题。本网站讨论如何使用 `sleep` 来引入一个随机性，如清单 3 所示。

```
#!/bin/sh

# Grab a random value between 0-240.
value=$RANDOM
while [ $value -gt 240 ] ; do
 value=$RANDOM
done

# Sleep for that time.
sleep $value

# Syncronize.
/usr/bin/rsync -aqzC --delete --delete-after masterhost::master /some/dir/

```

**清单 3：**在触发事件之前引入随机延迟的 shell 脚本，以避免[河马之惊群](http://www.moundalexis.com/archives/000076.php)。

在提到这些（可能令人惊讶的）延迟时，是指 `/etc/crontab` 或 root 用户自己的 crontab 文件。如果你想改变 `locate` 命令运行的时间，特别是由于磁盘访问速度减慢时，那么它不是太棘手。实现它可能会有更优雅的方式，但是你也可以把文件 `/etc/cron.daily/mlocate.cron` 移到别的地方（我使用 `/usr/local/etc` 目录），使用 root 用户添加一条记录到 root 用户的 crontab，粘贴以下内容：

```
# crontab -e

33 3 * * * /usr/local/etc/mlocate.cron

```

使用 anacron，而不是通过 `/var/log/cron` 以及它的旧的、轮转的版本，你可以快速地告诉它上次 cron.daily 任务被触发的时间：

```
# ls -hal /var/spool/anacron

```

下一次，我们会看更多的使用 `locate`、`updatedb` 和其他工具来查找文件的方法。

---

via: <https://www.linux.com/blog/learn/intro-to-linux/2017/11/finding-files-mlocate>

作者：[CHRIS BINNIE](https://www.linux.com/users/chrisbinnie) 译者：[geekpi](https://github.com/geekpi) 校对：[wxy](https://github.com/wxy)

本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译，[Linux中国](https://linux.cn/) 荣誉推出


PostgreSQL 的哈希索引现在很酷

Summary: 现在似乎是对过去 18 个月左右所做的工作进行简要回顾的好时机。

现在似乎是对过去 18 个月左右所做的工作进行简要回顾的好时机。

![](/data/attachment/album/201711/14/174704dbtn0tb2tpbz30jv.png)

由于我刚刚提交了最后一个改进 PostgreSQL 11 哈希索引的补丁，并且大部分哈希索引的改进都致力于预计下周发布的 PostgreSQL 10（LCTT 译注：已发布），因此现在似乎是对过去 18 个月左右所做的工作进行简要回顾的好时机。在版本 10 之前，哈希索引在并发性能方面表现不佳，缺少预写日志记录，因此在宕机或复制时都是不安全的，并且还有其他二等公民。在 PostgreSQL 10 中，这在很大程度上被修复了。

虽然我参与了一些设计，但改进哈希索引的首要功劳来自我的同事 Amit Kapila，[他在这个话题下的博客值得一读](http://amitkapila16.blogspot.jp/2017/03/hash-indexes-are-faster-than-btree.html)。哈希索引的问题不仅在于没有人打算写预写日志记录的代码，还在于代码没有以某种方式进行结构化，使其可以添加实际上正常工作的预写日志记录。要拆分一个桶，系统将锁定已有的桶（使用一种十分低效的锁定机制），将半个元组移动到新的桶中，压缩已有的桶，然后松开锁。即使记录了个别更改，在错误的时刻发生崩溃也会使索引处于损坏状态。因此，Aimt 首先做的是重新设计锁定机制。[新的机制](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=6d46f4783efe457f74816a75173eb23ed8930020)在某种程度上允许扫描和拆分并行进行，并且允许稍后完成那些因报错或崩溃而被中断的拆分。完成了一系列漏洞的修复和一些重构工作，Aimt 就打了另一个补丁，[添加了支持哈希索引的预写日志记录](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=c11453ce0aeaa377cbbcc9a3fc418acb94629330)。

与此同时，我们发现哈希索引已经错过了许多已应用于 B 树索引多年的相当明显的性能改进。因为哈希索引不支持预写日志记录，以及旧的锁定机制十分笨重，所以没有太多的动机去提升其他的性能。而这意味着如果哈希索引会成为一个非常有用的技术，那么需要做的事只是添加预写日志记录而已。PostgreSQL 索引存取方法的抽象层允许索引保留有关其信息的后端专用缓存，避免了重复查询索引本身来获取相关的元数据。B 树和 SQLite 的索引正在使用这种机制，但哈希索引没有，所以我的同事 Mithun Cy 写了一个补丁来[使用此机制缓存哈希索引的元页](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=293e24e507838733aba4748b514536af2d39d7f2)。同样，B 树索引有一个称为“单页回收”的优化，它巧妙地从索引页移除没用的索引指针，从而防止了大量索引膨胀。我的同事 Ashutosh Sharma 打了一个补丁将[这个逻辑移植到哈希索引上](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=6977b8b7f4dfb40896ff5e2175cad7fdbda862eb)，也大大减少了索引的膨胀。最后，B 树索引[自 2006 年以来](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=7c75ef571579a3ad7a1d3ee909f11dba5e0b9440)就有了一个功能，可以避免重复锁定和解锁同一个索引页——所有元组都在页中一次性删除，然后一次返回一个。Ashutosh Sharma 也[将此逻辑移植到了哈希索引中](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=a507b86900f695aacc8d52b7d2cfcb65f58862a2)，但是由于缺少时间，这个优化没有在版本 10 中完成。在这个博客提到的所有内容中，这是唯一一个直到版本 11 才会出现的改进。

关于哈希索引的工作有一个更有趣的地方是，很难确定行为是否真的正确。锁定行为的更改只可能在繁重的并发状态下失败，而预写日志记录中的错误可能仅在崩溃恢复的情况下显示出来。除此之外，在每种情况下，问题可能是微妙的。没有东西崩溃还不够；它们还必须在所有情况下产生正确的答案，并且这似乎很难去验证。为了协助这项工作，我的同事 Kuntal Ghosh 先后跟进了最初由 Heikki Linnakangas 和 Michael Paquier 开始的工作，并且制作了一个 WAL 一致性检查器，它不仅可以作为开发人员测试的专用补丁，还能真正[提交到 PostgreSQL](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=7403561c0f6a8c62b79b6ddf0364ae6c01719068)。在提交之前，我们对哈希索引的预写日志代码使用此工具进行了广泛的测试，并十分成功地查找到了一些漏洞。这个工具并不仅限于哈希索引，相反：它也可用于其他模块的预写日志记录代码，包括堆，当今的所有 AM 索引，以及一些以后开发的其他东西。事实上，它已经成功地[在 BRIN 中找到了一个漏洞](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=08bf6e529587e1e9075d013d859af2649c32a511)。

虽然 WAL 一致性检查是主要的开发者工具——尽管它也适合用户使用，如果怀疑有错误——也可以升级到专为数据库管理人员提供的几种工具。Jesper Pedersen 写了一个补丁来[升级 pageinspect contrib 模块来支持哈希索引](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=e759854a09d49725a9519c48a0d71a32bab05a01)，Ashutosh Sharma 做了进一步的工作，Peter Eisentraut 提供了测试用例（这是一个很好的办法，因为这些测试用例迅速失败，引发了几轮漏洞修复）。多亏了 Ashutosh Sharma 的工作，pgstattuple contrib 模块[也支持哈希索引了](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=ea69a0dead5128c421140dc53fac165ba4af8520)。

一路走来，也有一些其他性能的改进。我一开始没有意识到的是，当一个哈希索引开始新一轮的桶拆分时，磁盘上的大小会突然加倍，这对于 1MB 的索引来说并不是一个问题，但是如果你碰巧有一个 64GB 的索引，那就有些不幸了。Mithun Cy 通过编写一个补丁，把加倍过程[分为四个阶段](https://www.postgresql.org/message-id/20170704105728.mwb72jebfmok2nm2@zip.com.au)在某个程度上解决了这一问题，这意味着我们将从 64GB 到 80GB 到 96GB 到 112GB 到 128GB，而不是一次性从 64GB 到 128GB。这个问题可以进一步改进，但需要对磁盘格式进行更深入的重构，并且需要仔细考虑对查找性能的影响。

七月时，一份[来自于“AP”测试人员](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=ff98a5e1e49de061600feb6b4de5ce0a22d386af)的报告使我们感到需要做进一步的调整。AP 发现，若试图将 20 亿行数据插入到新创建的哈希索引中会导致错误。为了解决这个问题，Amit 修改了拆分桶的代码，[使得在每次拆分之后清理旧的桶](https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=ff98a5e1e49de061600feb6b4de5ce0a22d386af)，大大减少了溢出页的累积。为了得以确保，Aimt 和我也[增加了四倍的位图页的最大数量](https://www.postgresql.org/message-id/CA%2BTgmoax6DhnKsuE_gzY5qkvmPEok77JAP1h8wOTbf%2Bdg2Ycrw%40mail.gmail.com)，用于跟踪溢出页分配。

虽然还是有更多的事情要做，但我觉得，我和我的同事们——以及在 PostgreSQL 团队中的其他人的帮助下——已经完成了我们的目标，使哈希索引成为一个一流的功能，而不是被严重忽视的半成品。不过，你或许会问，这个功能可能有哪些应用场景。我在文章开头提到的（以及链接中的）Amit 的博客内容表明，即使是 pgbench 的工作负载，哈希索引页也可能在低级和高级并发方面优于 B 树。然而，从某种意义上说，这确实是最坏的情况。哈希索引的卖点之一是，索引存储的是字段的哈希值，而不是原始值——所以，我希望像 UUID 或者长字符串的宽键将有更大的改进。它们可能会在读取繁重的工作负载时做得更好。我们没有像优化读取那种程度来优化写入，但我鼓励任何对此技术感兴趣的人去尝试并将结果发到邮件列表（或发私人电子邮件），因为对于开发一个功能而言，真正关键的并不是一些开发人员去思考在实验室中会发生什么，而是在实际中发生了什么。

最后，我要感谢 Jeff Janes 和 Jesper Pedersen 为这个项目及其相关所做的宝贵的测试工作。这样一个规模适当的项目并不易得，以及有一群坚持不懈的测试人员，他们勇于打破任何废旧的东西的决心起了莫大的帮助。除了以上提到的人之外，其他人同样在测试，审查以及各种各样的日常帮助方面值得赞扬，其中包括 Andreas Seltenreich，Dilip Kumar，Tushar Ahuja，Alvaro Herrera，Micheal Paquier，Mark Kirkwood，Tom Lane，Kyotaro Horiguchi。谢谢你们，也同样感谢那些本该被提及却被我无意中忽略的所有朋友。

---

via：<https://rhaas.blogspot.jp/2017/09/postgresqls-hash-indexes-are-now-cool.html>

作者：[Robert Haas](http://rhaas.blogspot.jp) 译者：[polebug](https://github.com/polebug) 校对：[wxy](https://github.com/wxy)

本文由[LCTT]（[https://github.com/LCTT/TranslateProject）原创编译，[Linux中国]（https://linux.cn/）荣誉推出](https://github.com/LCTT/TranslateProject%EF%BC%89%E5%8E%9F%E5%88%9B%E7%BC%96%E8%AF%91%EF%BC%8C%5BLinux%E4%B8%AD%E5%9B%BD%5D%EF%BC%88https://linux.cn/%EF%BC%89%E8%8D%A3%E8%AA%89%E6%8E%A8%E5%87%BA)