朋友你好 在离开五月假期的第二部分之前,我们将与您分享在
“新关系”发布前夕以
“关系DBMS”的速率翻译的材料。

应用程序开发人员花费大量时间比较多个操作数据库,以选择最适合其预期工作负载的数据库。 需求可能包括简化的数据建模,事务保证,读/写性能,水平缩放和容错能力。 按照传统,选择从数据库类别SQL或NoSQL开始,因为每个类别都提供了一组明确的权衡方法。 就低延迟和高吞吐量而言,高性能通常被视为权衡要求,因此对于样本中的任何数据库都是必需的。
本文的目的是帮助应用程序开发人员在对应用程序数据进行建模的上下文中在SQL和NoSQL之间做出正确的选择。 我们将研究一个SQL数据库(即PostgreSQL)和两个NoSQL数据库(Cassandra和MongoDB),以讨论数据库设计的基础知识,例如创建表,填充表,从表中读取数据并删除它们。 在下一篇文章中,我们一定会研究索引,事务,JOIN,TTL指令和基于JSON的数据库设计。
SQL和NoSQL有什么区别?SQL数据库通过ACID事务保证提高了应用程序的灵活性,并具有在现有规范化关系数据库模型之上以意外方式使用JOIN查询数据的能力。
考虑到它们的整体/单节点体系结构以及使用主从复制模型来实现冗余,传统的SQL数据库没有两个重要功能-记录的线性可伸缩性(即,自动拆分为多个节点)和自动/零数据丢失。 这意味着接收到的数据量不能超过一个节点的最大写吞吐量。 此外,在容错期间(在没有资源共享的体系结构中)应考虑一些临时数据丢失。 在这里,您需要记住,最近的提交尚未反映在从属副本中。 在没有数据库停机的情况下,更新也很难在SQL数据库中实现。
NoSQL数据库通常是自然分布的,即 其中,数据分为多个部分,并分布在多个节点上。 他们需要非规范化。 这意味着输入的数据也必须复制多次,以响应您发送的特定请求。 总体目标是通过减少读取时可用的分片数量来获得高性能。 它遵循以下语句:NoSQL要求您对查询建模,而SQL要求您对数据建模。
NoSQL致力于在分布式集群中实现高性能,这是许多数据库设计权衡的主要理由,其中包括交易损失ACID,JOIN和一致的全局二级索引。
人们相信,尽管NoSQL数据库提供了线性的写可扩展性和高的容错能力,但是事务性保证的丢失使它们不适用于关键任务数据。
下表显示了NoSQL中的数据建模与SQL有何不同。
SQL和NoSQL:为什么都需要?拥有大量用户的真实应用程序(例如Amazon.com,Netflix,Uber和Airbnb)执行复杂的,多种任务。 例如,像Amazon.com这样的电子商务应用程序需要存储轻量,高度关键的数据(例如有关用户,产品,订单,发票的信息)以及沉重但不太敏感的数据(例如产品评论,支持消息) ,用户活动,用户评论和建议。 自然地,这些应用程序依赖于至少一个SQL数据库以及至少一个NoSQL数据库。 在区域间和全球系统中,NoSQL数据库充当地理分布的缓存,用于存储在任何一个区域中的受信任源SQL数据库中存储的数据。
YugaByte DB如何结合SQL和NoSQL?YugaByte DB建立在面向日志的混合存储引擎,自动分片,分片分布式共识复制和ACID分布式事务(受Google Spanner启发)的基础上,是世界上第一个与NoSQL同时兼容的开源数据库(Cassandra和Redis) )和SQL(PostgreSQL)。 如下表所示,YSQL是与Cassandra兼容的YugaByte DB API,它向NoSQL API添加了单键和多键ACID事务以及全局二级索引的概念,从而开启了事务NoSQL数据库的时代。 此外,YSQL是PostgreSQL兼容的YugaByte DB API,它在SQL API中增加了线性记录缩放和自动容错的概念,从而将分布式SQL数据库引入了世界。 由于YugaByte DB数据库本质上是事务性的,因此NoSQL API现在可以在关键任务数据的上下文中使用。

如之前的文章
“介绍YSQL:用于YugaByte DB的PostgreSQL兼容的分布式SQL API”所述 ,在
YugaByte DB中选择SQL还是NoSQL完全取决于主要工作负载的特性:
- 如果主要工作量是使用JOIN进行多键操作,那么在选择YSQL时,请了解您的键可以分布在多个节点上,与NoSQL相比,这将导致更高的延迟和/或更低的吞吐量。
- 否则,请选择两个NoSQL API中的任何一个,请记住,由于一次从一个节点提供查询,您将获得更好的性能。 YugaByte DB可以用作真正复杂应用程序的单个操作数据库,在该应用程序中您需要同时管理多个工作负载。
下一部分中的数据建模实验室基于与PostgreSQL和Cassandra API兼容的YugaByte DB数据库,而不是源数据库。 与使用两个不同数据库的完全独立的群集相反,此方法强调与同一数据库群集的两个不同API(在两个不同端口上)进行交互的简便性。
在以下各节中,我们将与数据建模实验室会面,以说明所讨论数据库的差异和一些常见功能。
数据建模实验室数据库安装考虑到强调设计数据模型(而不是复杂的部署架构),我们将数据库安装在本地计算机上的Docker容器中,然后使用它们对应的命令行外壳与它们进行交互。
与PostgreSQL和Cassandra,YugaByte数据库数据库兼容mkdir ~/yugabyte && cd ~/yugabyte wget https://downloads.yugabyte.com/yb-docker-ctl && chmod +x yb-docker-ctl docker pull yugabytedb/yugabyte ./yb-docker-ctl create
Mongodb docker run
命令行访问让我们使用命令行外壳针对相应的API连接到数据库。
PostgreSQL的psql是用于与PostgreSQL交互的命令行外壳。 为了易于使用,YugaByte DB直接在bin文件夹中附带了psql。
docker exec -it yb-postgres-n1 /home/yugabyte/postgres/bin/psql -p 5433 -U postgres
卡桑德拉cqlsh是用于通过CQL(Cassandra查询语言)与Cassandra及其兼容数据库进行交互的命令行外壳。 为了易于使用,YugaByte DB在
bin
随附了
cqlsh
。
请注意,CQL受SQL启发,具有与表,行,列和索引相似的概念。 但是,作为NoSQL语言,它增加了一组限制,我们将在其他文章中介绍其中的大多数限制。
docker exec -it yb-tserver-n1 /home/yugabyte/bin/cqlsh
Mongodbmongo是用于与MongoDB进行交互的命令行外壳。 可以在MongoDB安装的bin目录中找到它。
docker exec -it my-mongo bash cd bin mongo
表格创建现在,我们可以使用命令行与数据库进行交互以执行各种操作。 让我们从创建一个表开始,该表存储有关不同艺术家创作的歌曲的信息。 这些歌曲可能是专辑的一部分。 歌曲的可选属性-发行年份,价格,类型和等级。 我们需要考虑通过“标签”字段将来可能需要的其他属性。 它可以将半结构化数据存储为键值对。
PostgreSQL的 CREATE TABLE Music ( Artist VARCHAR(20) NOT NULL, SongTitle VARCHAR(30) NOT NULL, AlbumTitle VARCHAR(25), Year INT, Price FLOAT, Genre VARCHAR(10), CriticRating FLOAT, Tags TEXT, PRIMARY KEY(Artist, SongTitle) );
卡桑德拉在Cassandra中创建表与PostgreSQL非常相似。
主要区别之一是缺少完整性约束(例如,NOT NULL),但这是应用程序的责任,而不是NoSQL数据库的责任 。 主键由一个区域键(在下面的示例中的“艺术家”列)和一组聚类列(在下面的示例中的“歌曲标题”列)组成。 分区键确定将行放置在哪个分区/分片中,聚类列指示应如何在当前分片内组织数据。
CREATE KEYSPACE myapp; USE myapp; CREATE TABLE Music ( Artist TEXT, SongTitle TEXT, AlbumTitle TEXT, Year INT, Price FLOAT, Genre TEXT, CriticRating FLOAT, Tags TEXT, PRIMARY KEY(Artist, SongTitle) );
MongodbMongoDB将数据组织到数据库(数据库)中(类似于Cassandra中的键空间),那里有集合(Collections)(类似于表),其中包含文档(Documents)(类似于表中的行)。 MongoDB基本上不需要定义原始架构。 下面显示的
“使用数据库”命令在第一次调用时创建数据库的实例,并更改新创建的数据库的上下文。 即使是集合,也不需要显式创建,它们会自动创建,只需在将第一个文档添加到新集合时即可。 请注意,默认情况下,MongoDB使用测试数据库,因此默认情况下将在其中执行任何未指定特定数据库的集合级操作。
use myNewDatabase;
检索PostgreSQL表信息 \d Music Table "public.music" Column | Type | Collation | Nullable | Default
卡桑德拉 DESCRIBE TABLE MUSIC; CREATE TABLE myapp.music ( artist text, songtitle text, albumtitle text, year int, price float, genre text, tags text, PRIMARY KEY (artist, songtitle) ) WITH CLUSTERING ORDER BY (songtitle ASC) AND default_time_to_live = 0 AND transactions = {'enabled': 'false'};
Mongodb use myNewDatabase; show collections;
将数据发布到PostgreSQL表 INSERT INTO Music (Artist, SongTitle, AlbumTitle, Year, Price, Genre, CriticRating, Tags) VALUES( 'No One You Know', 'Call Me Today', 'Somewhat Famous', 2015, 2.14, 'Country', 7.8, '{"Composers": ["Smith", "Jones", "Davis"],"LengthInSeconds": 214}' ); INSERT INTO Music (Artist, SongTitle, AlbumTitle, Price, Genre, CriticRating) VALUES( 'No One You Know', 'My Dog Spot', 'Hey Now', 1.98, 'Country', 8.4 ); INSERT INTO Music (Artist, SongTitle, AlbumTitle, Price, Genre) VALUES( 'The Acme Band', 'Look Out, World', 'The Buck Starts Here', 0.99, 'Rock' ); INSERT INTO Music (Artist, SongTitle, AlbumTitle, Price, Genre, Tags) VALUES( 'The Acme Band', 'Still In Love', 'The Buck Starts Here', 2.47, 'Rock', '{"radioStationsPlaying": ["KHCR", "KBQX", "WTNR", "WJJH"], "tourDates": { "Seattle": "20150625", "Cleveland": "20150630"}, "rotation": Heavy}' );
卡桑德拉通常,Cassandra中的
INSERT
表达式与PostgreSQL中的表达式非常相似。 但是,语义上有一个很大的差异。 在Cassandra中,
INSERT
实际上是
UPSERT
操作,如果字符串已经存在,则将最后一个值添加到字符串中。
数据输入类似于上面的PostgreSQL INSERT
Mongodb尽管MongoDB是像Cassandra一样的NoSQL数据库,但是其插入操作与Cassandra中的语义行为无关。 在MongoDB中,
insert()不具有
UPSERT
功能,这使其看起来像PostgreSQL。 添加不带
_idspecified
默认数据会将新文档添加到集合中。
db.music.insert( {
artist: "No One You Know",
songTitle: "Call Me Today",
albumTitle: "Somewhat Famous",
year: 2015,
price: 2.14,
genre: "Country",
tags: {
Composers: ["Smith", "Jones", "Davis"],
LengthInSeconds: 214
}
}
);
db.music.insert( {
artist: "No One You Know",
songTitle: "My Dog Spot",
albumTitle: "Hey Now",
price: 1.98,
genre: "Country",
criticRating: 8.4
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Look Out, World",
albumTitle:"The Buck Starts Here",
price: 0.99,
genre: "Rock"
}
);
db.music.insert( {
artist: "The Acme Band",
songTitle: "Still In Love",
albumTitle:"The Buck Starts Here",
price: 2.47,
genre: "Rock",
tags: {
radioStationsPlaying:["KHCR", "KBQX", "WTNR", "WJJH"],
tourDates: {
Seattle: "20150625",
Cleveland: "20150630"
},
rotation: "Heavy"
}
}
);
表查询在查询设计方面,SQL和NoSQL之间最重要的区别也许是使用
FROM
和
WHERE
语句。 SQL允许您在
FROM
之后选择多个表,并且
WHERE
可以具有任何复杂性(包括表之间的
JOIN
操作)。 但是,NoSQL倾向于对
FROM
施加严格的限制,并且只能使用一个指定的表,并且在
WHERE
,必须始终指定主键。 这是由于渴望提高NoSQL的性能,这在我们之前已经谈到。 这种愿望导致任何可能的跨表和跨键交互的减少。 响应请求时,这可能导致节点间通信的大量延迟,因此,原则上最好避免这种情况。 例如,Cassandra要求查询仅限于分区键上的某些运算符(仅允许
=, IN, <, >, =>, <=
),除非请求二级索引时(此处仅允许=运算符)。
PostgreSQL的下面将给出三个可以由SQL数据库轻松执行的查询示例。
- 打印艺术家的所有歌曲;
- 打印与名称的第一部分匹配的歌手的所有歌曲;
- 列出歌手中标题中带有特定单词且价格低于1.00的所有歌曲。
SELECT * FROM Music WHERE Artist='No One You Know'; SELECT * FROM Music WHERE Artist='No One You Know' AND SongTitle LIKE 'Call%'; SELECT * FROM Music WHERE Artist='No One You Know' AND SongTitle LIKE '%Today%' AND Price > 1.00;
卡桑德拉在上述PostgreSQL查询中,只有第一个查询可以在Cassandra中保持不变,因为
LIKE
语句无法应用于集群列,例如
SongTitle
。 在这种情况下,仅允许使用
=
和
IN
运算符。
SELECT * FROM Music WHERE Artist='No One You Know'; SELECT * FROM Music WHERE Artist='No One You Know' AND SongTitle IN ('Call Me Today', 'My Dog Spot') AND Price > 1.00;
Mongodb如前面的示例所示,在MongoDB中创建查询的主要方法是
db.collection.find() 。 此方法显式包含集合的名称(在下面的示例中为
music
),因此禁止请求多个集合。
db.music.find( { artist: "No One You Know" } ); db.music.find( { artist: "No One You Know", songTitle: /Call/ } );
读取所有表行读取所有行只是我们前面检查过的查询模板的特例。
PostgreSQL的 SELECT * FROM Music;
卡桑德拉与上面的PostgreSQL中的示例相似。
Mongodb
db.music.find( {} );
在表格中编辑数据PostgreSQL的PostgreSQL提供了一个
UPDATE
来修改数据。 它不具有
UPSERT
功能,因此,如果该行不再存在于数据库中,则该指令的执行将失败。
UPDATE Music SET Genre = 'Disco' WHERE Artist = 'The Acme Band' AND SongTitle = 'Still In Love';
卡桑德拉Cassandra的
UPDATE
类似于PostgreSQL。
UPDATE
UPSERT
INSERT
具有相同的
UPSERT
语义。
与上面的PostgreSQL中的示例相似。
MongodbMongoDB中的
update()操作可以完全更新现有文档或仅更新某些字段。 默认情况下,它仅更新一个
UPSERT
了
UPSERT
语义的文档。 可以通过为操作设置其他标志来应用更新多个文档和类似于
UPSERT
行为。 例如,在下面的示例中,特定歌手的流派由他的歌曲更新。
db.music.update( {"artist": "The Acme Band"}, { $set: { "genre": "Disco" } }, {"multi": true, "upsert": true} );
从表中删除数据PostgreSQL的 DELETE FROM Music WHERE Artist = 'The Acme Band' AND SongTitle = 'Look Out, World';
卡桑德拉与上面的PostgreSQL中的示例相似。
MongodbMongoDB有两种删除文档的操作
-deleteOne() / deleteMany()和
remove() 。 两种类型都删除文档,但是返回不同的结果。
db.music.deleteMany( { artist: "The Acme Band" } );
删除表格PostgreSQL的 DROP TABLE Music;
卡桑德拉与上面的PostgreSQL中的示例相似。
Mongodb db.music.drop();
结论在SQL和NoSQL之间进行选择的争论已经持续了十多年。 这场辩论有两个主要方面:数据库引擎的体系结构(整体式,事务性SQL与分布式,非事务性NoSQL)和数据库设计方法(SQL中的数据建模与NoSQL中的查询建模)。
使用分布式事务数据库(例如YugaByte DB),可以轻松消除有关数据库体系结构的争论。 随着数据量变得大于可写入单个节点的数据量,需要具有自动分片/重新平衡功能以支持记录的线性可伸缩性的完全分布式体系结构。
除了在
Google Cloud文章中说过的以外,与非事务性,最终一致的体系结构相比,现在,更严格地使用事务性,严格一致的体系结构来提供更好的开发灵活性。
回到数据库设计的讨论,可以说两种设计方法(SQL和NoSQL)对于任何复杂的实际应用程序都是必需的。 SQL方法“数据建模”使开发人员可以更轻松地满足不断变化的业务需求,而NoSQL方法“数据建模”则允许相同的开发人员以低延迟和高吞吐量来处理大量数据。 因此,YugaByte DB在通用内核中提供SQL和NoSQL API,而不是推广其中一种方法。 此外,通过确保与流行的数据库语言(包括PostgreSQL和Cassandra)兼容,YugaByte DB确保开发人员不必学习另一种语言即可使用分布式,严格一致的数据库引擎。
在本文中,我们了解了PostgreSQL,Cassandra和MongoDB中数据库设计的基础是如何不同的。 在以下文章中,我们将深入研究高级设计概念,例如索引,事务,JOIN,TTL指令和JSON文档。
祝您周末
愉快 ,并邀请您参加将于5月14日举行的
免费网络研讨会 。