我的编程空间,编程开发者的网络收藏夹
学习永远不晚

分布式 PostgreSQL 集群(Citus),分布式表中的分布列选择优秀实践

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

分布式 PostgreSQL 集群(Citus),分布式表中的分布列选择优秀实践

大致上有两种应用程序在 Citus 上运行良好。数据建模的第一步是确定哪些应用程序类型更接近您的应用程序。

概览

多租户应用

实时应用

有时 schema 中有几十个或数百个表

表数量少

一次与一个租户(公司/商店)相关的查询

具有聚合的相对简单的分析查询

用于服务 Web 客户端的 OLTP 工作负载

摄取大量几乎不可变的数据

为每个租户分析查询提供服务的 OLAP 工作负载

通常围绕着一个大的事件表

示例和特征

多租户应用

这些通常是为其他公司、帐户或组织服务的 SaaS 应用程序。大多数 SaaS 应用程序本质上是关系型的。它们具有跨节点分布数据的自然维度:只需按 tenant_id 分片。

Citus 使您能够将数据库扩展到数百万租户,而无需重新构建应用程序。您可以保留所需的关系语义,例如 联接、外键约束、事务、ACID 和一致性。

  • 示例:为其他企业托管店面的网站,例如数字营销解决方案或销售自动化工具。
  • 特征:与单个租户相关的查询,而不是跨租户加入信息。这包括为 Web 客户端提供服务的 OLTP 工作负载,以及为每个租户提供分析查询的 OLAP 工作负载。在您的数据库模式中拥有数十或数百个表也是多租户数据模型的一个指标。

使用 Citus 扩展多租户应用程序还需要对应用程序代码进行最少的更改。我们支持流行的框架,如 Ruby on Rails 和 Django。

实时分析应用

需要大规模并行性、协调数百个内核以快速获得数值、统计或计数查询结果的应用程序。通过跨多个节点对 SQL 查询进行分片和并行化,Citus 可以在一秒钟内对数十亿条记录执行实时查询。

示例: 需要亚秒级响应时间的面向客户的分析仪表板。

特征: 几张表,通常以设备、站点或用户事件的大表为中心,并且需要大量摄取大部分不可变的数据。涉及多个聚合和 GROUP BY 的相对简单(但计算量大)的分析查询。

如果您的情况类似于上述任何一种情况,那么下一步就是决定如何在 Citus 集群中对数据进行分片。如概念部分所述,Citus 根据表分布列的哈希值将表行分配给分片。数据库管理员对分布列的选择需要与典型查询的访问模式相匹配,以确保性能。

选择分布列

Citus 使用分布式表中的分布列将表行分配给分片。为每个表选择分布列是最重要的建模决策之一,因为它决定了数据如何跨节点分布。

如果正确选择了分布列,那么相关数据将在相同的物理节点上组合在一起,从而使查询快速并添加对所有 SQL 功能的支持。如果列选择不正确,系统将不必要地缓慢运行,并且无法支持跨节点的所有 SQL 功能。

本节提供两种最常见的 Citus 方案的分布列提示。最后,它深入探讨了 共置(co-location),即节点上理想的数据分组。

多租户应用

多租户架构使用一种分层数据库建模形式在分布式集群中的节点之间分布查询。数据层次结构的顶部称为 tenant id,需要存储在每个表的列中。Citus 检查查询以查看它们涉及的 tenant id,并将查询路由到单个 worker 节点进行处理,特别是保存与 tenant id 关联的数据分片的节点。运行将所有相关数据放置在同一节点上的查询称为 Table Co-Location。

下图说明了多租户数据模型中的共置(co-location)。它包含两个表,Accounts 和 Campaigns,每个表都由 account_id 分配。阴影框代表分片,每个分片的颜色代表哪个 worker 节点包含它。绿色分片一起存储在一个 worker 节点上,蓝色分片存储在另一个节点上。请注意,当将两个表限制为相同的 account_id 时,Accounts 和 Campaigns 之间的 join 查询如何将所有必要的数据放在一个节点上。

要在您自己的 schema 中应用此设计,第一步是确定在您的应用程序中构成租户的内容。常见实例包括公司(company)、帐户(account)、组织(organization)或客户(customer)。列名称类似于 company_id 或 customer_id。检查您的每个查询并问自己:如果它有额外的 WHERE 子句将所有涉及的表限制为具有相同 tenant id 的行,它会起作用吗?多租户模型中的查询通常以租户为范围,例如销售或库存查询将在某个商店内进行。

最佳实践

按公共 tenant_id 列对分布式表进行分区。 例如,在租户是公司的 SaaS 应用程序中,tenant_id 可能是 company_id。

将小型跨租户表转换为引用表。 当多个租户共享一个小信息表时,将其作为参考表分布。

限制按 tenant_id 过滤所有应用程序查询。 每个查询应一次请求一个租户的信息。

阅读多租户应用程序指南,了解构建此类应用程序的详细示例。

实时应用

虽然多租户架构引入了分层结构并使用数据共置(data co-location)来路由每个租户的查询,但实时架构依赖于其数据的特定分布属性来实现高度并行处理。

我们在实时模型中使 “entity id” 作为分布列的术语,而不是多租户模型中的租户 ID。典型的实体是用户(users)、主机(hosts)或设备(devices)。

实时查询通常要求按日期(date)或类别(category)分组的数字聚合。Citus 将这些查询发送到每个分片以获得部分结果,并在 coordinator 节点上组装最终答案。当尽可能多的节点做出贡献并且没有单个节点必须做不成比例的工作时,查询运行速度最快。

最佳实践

  • 选择具有高基数的列作为分布列。 为了比较,订单表上的 status 字段具有 新(new)、已付款(paid) 和 已发货(shipped) 值,是分布列的一个糟糕选择,因为它只假设这几个值。不同值的数量限制了可以保存数据的分片数量以及可以处理数据的节点数量。在具有高基数的列中,最好另外选择那些经常用于 group-by 子句或作为 join 键的列。
  • 选择分布均匀的列。 如果您将表分布在偏向某些常见值的列上,则表中的数据将倾向于在某些分片中累积。持有这些分片的节点最终会比其他节点做更多的工作。
  • 将事实表和维度表分布在它们的公共列上。 您的事实表只能有一个分布 key。在另一个 key 上 join 的表不会与事实表位于同一位置。根据 join 的频率和 join 行的大小,选择一个维度来共同定位。
  • 将一些维度表更改为引用表。 如果维度表不能与事实表共存,您可以通过将维度表的副本以引用表的形式分发到所有节点来提高查询性能。

阅读实时仪表板指南,了解构建此类应用程序的详细示例。

时间序列数据

在时间序列工作负载中,应用程序在归档旧信息的同时查询最近的信息。

在 Citus 中建模时间序列信息的最常见错误是将时间戳本身用作分布列。基于时间的散列分布将看似随机的时间分布到不同的分片中,而不是将时间范围保持在分片中。但是,涉及时间的查询通常会参考时间范围(例如最近的数据),因此这样的哈希分布会导致网络开销。

最佳实践

不要选择时间戳作为分布列。 选择不同的分布列。在多租户应用程序中,使用租户 ID,或在实时应用程序中使用实体 ID。

改为使用 PostgreSQL 表分区。 使用表分区将一个按时间排序的数据大表分解为多个继承表,每个表包含不同的时间范围。在 Citus 中分发 Postgres 分区的表会为继承的表创建分片。

阅读 Timeseries Data 指南,了解构建此类应用程序的详细示例。

表共置

关系数据库因其巨大的灵活性和可靠性而成为许多应用程序的首选数据存储。从历史上看,对关系数据库的一个批评是它们只能在一台机器上运行,当数据存储需要超过服务器改进时,这会产生固有的限制。快速扩展数据库的解决方案是分发它们,但这会产生其自身的性能问题:join 等关系操作需要跨越网络边界。共置(Co-location) 是一种策略性地划分数据的做法,将相关信息保存在同一台机器上以实现高效的关系操作,但利用整个数据集的水平可扩展性。

数据共存的原理是数据库中的所有表都有一个共同的分布列,并以相同的方式跨机器分片,使得具有相同分布列值的行总是在同一台机器上,即使跨不同的表也是如此。只要分布列提供了有意义的数据分组,就可以在组内执行关系操作。

Citus 中用于 hash 分布表的数据共存

PostgreSQL 的 Citus 扩展在能够形成数据库的分布式数据库方面是独一无二的。Citus 集群中的每个节点都是一个功能齐全的 PostgreSQL 数据库,Citus 在顶部添加了单个同构数据库的体验。虽然它没有以分布式方式提供 PostgreSQL 的全部功能,但在许多情况下,它可以通过托管在单台机器上充分利用 PostgreSQL 提供的功能,包括完整的 SQL 支持、事务和外键。

在 Citus 中,如果分布列中值的哈希值落在分片的哈希范围内,则将一行存储在分片中。为了确保共置,即使在重新平衡操作之后,具有相同哈希范围的分片也始终放置在同一个节点上,这样相等的分布列值始终位于跨表的同一个节点上。

我们发现在实践中运行良好的分布列是多租户应用程序中的租户 ID。例如,SaaS 应用程序通常有许多租户,但它们所做的每个查询都是特定于特定租户的。虽然一种选择是为每个租户提供 database 或 schema,但它通常成本高昂且不切实际,因为可能有许多跨用户的操作(数据加载、迁移、聚合、分析、schema 更改、备份等)。随着租户数量的增加,这变得更难管理。

共置的实际示例

考虑以下表格,这些表格可能是多租户 Web 分析SaaS 的一部分:

CREATE TABLE event (
tenant_id int,
event_id bigint,
page_id int,
payload jsonb,
primary key (tenant_id, event_id)
);

CREATE TABLE page (
tenant_id int,
page_id int,
path text,
primary key (tenant_id, page_id)
);

现在我们要回答可能由面向客户的仪表板发出的查询,例如:“返回租户六中所有以‘/blog’开头的页面在过去一周的访问次数。”

使用常规 PostgreSQL 表

如果我们的数据位于单个 PostgreSQL 节点中,我们可以使用 SQL 提供的丰富的关系操作集轻松地表达我们的查询:

SELECT page_id, count(event_id)
FROM
page
LEFT JOIN (
SELECT * FROM event
WHERE (payload->>'time')::timestamptz >= now() - interval '1 week'
) recent
USING (tenant_id, page_id)
WHERE tenant_id = 6 AND path LIKE '/blog%'
GROUP BY page_id;

只要此查询的工作集适合内存,这是许多应用程序的合适解决方案,因为它提供了最大的灵活性。但是,即使您还不需要扩展,考虑扩展数据模型的影响也会很有用。

按 ID 分布表

随着租户数量和为每个租户存储的数据的增长,查询时间通常会增加,因为工作集不再适合内存或 CPU 成为瓶颈。在这种情况下,我们可以使用 Citus 跨多个节点分片数据。分片时我们需要做出的第一个也是最重要的选择是分布列。让我们从一个天真的选择开始,将 event_id 用于事件表,将 page_id 用于页表:

-- naively use event_id and page_id as distribution columns

SELECT create_distributed_table('event', 'event_id');
SELECT create_distributed_table('page', 'page_id');

鉴于数据分散在不同的 worker 中,我们不能像在单个 PostgreSQL 节点上那样简单地执行 join。相反,我们需要发出两个查询:

跨页表的所有分片(Q1):

SELECT page_id FROM page WHERE path LIKE '/blog%' AND tenant_id = 6;

跨事件表的所有分片(Q2):

SELECT page_id, count(*) AS count
FROM event
WHERE page_id IN ()
AND tenant_id = 6
AND (payload->>'time')::date >= now() - interval '1 week'
GROUP BY page_id ORDER BY count DESC LIMIT 10;

之后,应用程序需要组合这两个步骤的结果。

回答查询所需的数据分散在不同节点上的分片中,每个分片都需要被查询:

在这种情况下,数据分布会产生很大的缺陷:

  • 查询每个分片的开销,运行多个查询
  • Q1 的开销返回许多行给客户端
  • Q2 变得非常大
  • 需要在多个步骤中编写查询,组合结果,需要在应用程序中进行更改

相关数据分散的一个潜在好处是查询可以并行化,Citus 会这样做。但是,这只有在查询的工作量远远大于查询许多分片的开销时才有用。通常最好避免直接从应用程序中进行如此繁重的工作,例如通过预先聚合数据。

按租户分布表

再次查看我们的查询,我们可以看到查询需要的所有行都有一个共同的维度:tenant_id。仪表板只会查询租户自己的数据。这意味着,如果同一租户的数据始终位于单个 PostgreSQL 节点上,那么我们的原始查询可以由该节点通过对 tenant_id 和 page_id 执行 join 来一次性回答。

在 Citus 中,具有相同分布列值的行保证在同一个节点上。分布式表中的每个分片实际上都有一组来自其他分布式表的位于同一位置的分片,这些分片包含相同的分布列值(同一租户的数据)。从头开始,我们可以创建以 tenant_id 作为分布列的表。

-- co-locate tables by using a common distribution column
SELECT create_distributed_table('event', 'tenant_id');
SELECT create_distributed_table('page', 'tenant_id', colocate_with => 'event');

在这种情况下,Citus 可以回答您将在单个 PostgreSQL 节点上运行而无需修改 (Q1) 的相同查询:

SELECT page_id, count(event_id)
FROM
page
LEFT JOIN (
SELECT * FROM event
WHERE (payload->>'time')::timestamptz >= now() - interval '1 week'
) recent
USING (tenant_id, page_id)
WHERE tenant_id = 6 AND path LIKE '/blog%'
GROUP BY page_id;

由于使用了 tenantid 过滤器和 tenantid 上的 join,Citus 知道可以使用包含特定租户数据的一组位于同一位置的分片来回答整个查询,而 PostgreSQL 节点可以在一个步骤中回答该查询,从而支持完整的 SQL 支持。

在某些情况下,查询和表 schema 需要进行少量修改,以确保 tenant_id 始终包含在唯一约束和 join 条件中。但是,这通常是一个简单的更改,并且避免了在没有共置的情况下所需的大量重写。

虽然上面的示例只查询一个节点,因为有一个特定的 tenant_id = 6 过滤器,但共置还允许我们在所有节点上有效地执行对 tenant_id 的分布式 join,尽管存在 SQL 限制。

共置意味着更好的功能支持

Citus 通过共置解锁的功能的完整列表如下:

  • 对一组位于同一位置的分片上的查询的完整 SQL 支持
  • 多语句事务支持对一组位于同一位置的分片进行修改
  • 通过 INSERT..SELECT 聚合
  • 外键
  • 分布式外部联接(outer join)
  • Pushdown CTEs(要求 PostgreSQL >=12 )

数据共置是一种强大的技术,可以为关系数据模型提供水平扩展和支持。使用分布式数据库迁移或构建应用程序的成本(通过共置实现关系操作)通常大大低于迁移到限制性数据模型(例如 NoSQL)的成本,并且与单节点数据库不同,它可以随着规模的大小而横向扩展您的业务。有关迁移现有数据库的更多信息,请参阅过渡到多租户数据模型。

查询性能

Citus 通过将传入查询分解为多个在工作分片上并行运行的片段查询(“任务”)来并行化传入查询。这使 Citus 可以利用集群中所有节点的处理能力以及每个节点上的单个核心的处理能力来进行每个查询。由于这种并行化,您可以获得集群中所有核心的计算能力的累积性能,与单个服务器上的 PostgreSQL 相比,查询时间显着减少。

Citus 在规划 SQL 查询时采用了两阶段优化器。第一阶段涉及将 SQL 查询转换为它们的交换和关联形式,以便它们可以下推并在工作线程上并行运行。如前几节所述,选择正确的分布列和分布方法允许分布式查询规划器对查询应用多种优化。由于网络 I/O 减少,这会对查询性能产生重大影响。

Citus 的分布式执行器然后将这些单独的查询片段发送到 PostgreSQL worker 实例。分布式规划器和执行器都有几个方面可以调整以提高性能。当这些单独的查询片段被发送给 worker 时,查询优化的第二阶段就开始了。worker 只是运行扩展的 PostgreSQL 服务器,他们应用 PostgreSQL 的标准计划和执行逻辑来运行这些片段 SQL 查询。因此,任何有助于 PostgreSQL 的优化也有助于 Citus。PostgreSQL 默认带有保守的资源设置;因此优化这些配置设置可以显着缩短查询时间。

我们在文档的查询性能调优部分讨论了相关的性能调优步骤。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

分布式 PostgreSQL 集群(Citus),分布式表中的分布列选择优秀实践

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

分布式 PostgreSQL 集群(Citus),分布式表中的分布列选择优秀实践

Citus 的分布式执行器然后将这些单独的查询片段发送到 PostgreSQL worker 实例。分布式规划器和执行器都有几个方面可以调整以提高性能。

Citus 分布式 PostgreSQL 集群 - SQL Reference(查询分布式表 SQL)

Citus 以多种方式支持 count(distinct) 聚合。如果 count(distinct) 聚合在分布列上,Citus 可以直接将查询下推给 worker。如果不是,Citus 对每个 worker 运行 select dist

Citus 分布式 PostgreSQL 集群-SQL Reference(创建和修改分布式表 DDL)

如果将现有的 PostgreSQL 数据库转换为 Citus 集群的协调器节点,则其表中的数据可以高效地分布,并且对应用程序的中断最小。

云原生分布式 PostgreSQL+Citus 集群在 Sentry 后端的实践

优化一个分布式系统的吞吐能力,除了应用本身代码外,很大程度上是在优化它所依赖的中间件集群处理能力。如:kafka/redis/rabbitmq/postgresql​/分布式存储(CephFS,JuiceFS,C urve,Longhorn

分布式 PostgreSQL 集群(Citus)官方示例 -实时仪表盘

我们正在处理的数据是不可变的日志数据流。我们将直接插入 Citus,但这些数据首先通过 Kafka 之类的东西进行路由也很常见。这样做具有通常的优势,并且一旦数据量变得难以管理,就可以更容易地预先聚合数据。

分布式 PostgreSQL 集群(Citus)官方示例-时间序列数据

我们可以将单节点表分区技术与 Citus 的分布式分片相结合,形成一个可扩展的时间序列数据库。这是两全其美的。它在 Postgres 的声明性表分区之上特别优雅。

分布式 PostgreSQL 集群(Citus)官方示例 - 多租户应用程序实战

本指南采用了一个示例多租户应用程序,并描述了如何使用 Citus 对其进行建模以实现可扩展性。在此过程中,我们研究了多租户应用程序的典型挑战。

Elasticsearch分布式集群搭建与管理实践(如何构建并管理一个高效的Elasticsearch分布式集群?)

本文详细讲解了构建和管理高效Elasticsearch分布式集群的实践。涵盖了集群架构、硬件和网络、安装和配置、集群管理、性能优化以及最佳实践等方面。遵循文中指导,可以构建一个可靠且满足搜索和分析需求的Elasticsearch集群。
Elasticsearch分布式集群搭建与管理实践(如何构建并管理一个高效的Elasticsearch分布式集群?)
2024-04-02

分布式图数据库 Nebula Graph 中的集群快照实践

1 概述1.1 需求背景图数据库 Nebula Graph 在生产环境中将拥有庞大的数据量和高频率的业务处理,在实际的运行中将不可避免的发生人为的、硬件或业务处理错误的问题,某些严重错误将导致集群无法正常运行或集群中的数据失效。当集群处于无法启动或数据失效的状
分布式图数据库 Nebula Graph 中的集群快照实践
2016-02-19

MongoDB与云计算的融合实践:从单节点到分布式集群

近年来,云计算技术的快速发展和普及,为企业带来了革命性的数据处理方式和存储方案。而在云计算之中,NoSQL 数据库 MongoDB 也备受青睐,其支持高并发、可伸缩性好等特点,使其广受欢迎。但是,单节点 MongoDB 实例仅能满足小规模应
MongoDB与云计算的融合实践:从单节点到分布式集群
2023-11-02

负载均衡技术全解析:Pulsar 分布式系统的优秀实践

对于无状态的服务来说,理论上我们只需要做好负载算法即可(轮训、一致性哈希、低负载优先等)就可以很好的平衡各个节点之间的负载。而对于有状态的服务来说,负载均衡就是将负载较高节点中的数据转移到负载低的节点中。

分布式系统中 Golang 函数的优化实践总结

优化 go 函数以提高分布式系统应用程序的性能,最佳实践包括:利用 go 协程、使用 channels 进行通信、区分并发性和串行性、进行内存优化、进行基准测试和性能分析。分布式系统中 Go 函数的优化实践Golang 函数的优化对于分布
分布式系统中 Golang 函数的优化实践总结
2024-04-19

TensorFlow在推荐系统中的分布式训练优化实践

本文重点介绍大规模分布式训练优化的工作,希望对大家能够有所帮助或启发。

大语言模型分布式训练的量化分析与优秀实践,以 GPT-175B 为例

本文分享主题为大语言模型分布式训练的相关技术及量化分析,并以GPT-175B 为例,介绍相关技术的最佳实践。

集中式与分布式一体化架构,达梦给企业更好的选择

今年6月,达梦数据成功在科创板上市,成为国产数据库第一股。达梦数据库经过几十年的发展,始终坚持自主研发,原始创新,依托达梦一套代码根,不断满足各行各业客户对数据库的需求,走出了一条中国数据库自主发展之路。
达梦2024-11-29

Golang中使用RabbitMQ实现分布式任务队列的性能优化

在Golang中使用RabbitMQ实现分布式任务队列的性能优化可以从以下几个方面进行优化:1. 消息持久化:RabbitMQ默认情况下消息是内存存储的,如果重启或崩溃,消息将丢失。为了保证消息的持久化,可以将消息标记为持久化,以确保在重启
2023-10-20

Golang中使用RabbitMQ实现分布式任务队列的性能调优技巧

在Golang中使用RabbitMQ实现分布式任务队列时,可以采取以下性能调优技巧:1. 使用持久化队列和消息:通过将队列和消息标记为持久化,可以确保即使在RabbitMQ重启后也不会丢失任务。2. 批量发送消息:将多个消息打包成一个批次进
2023-10-10

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录