分片技术
分片是一种将数据库数据分布在多个服务器上或在一个进程中将数据库拆分为多个实例(“分片”)的方法。对于多个服务器,分片是通过 McoDistributedSqlEngine 实现的。在一个进程中创建多个分片则通过 SqlAggregator 类进行管理。xSQL 可用于在共享内存中管理数据库分片。例如,可以在两个不同的控制台窗口中创建两个分片,如下所示:
Console 1:
./xsql -shared -dbname xsqldb0 -size 100M -i
Console 2:
./xsql -shared -dbname xsqldb1 -size 100M -i
然后,这两个分片可以由通过命令行选项 -shards 启动的 xSQL 的第三个实例来管理。例如:
Console 3:
./xsql xsqldb%d -shards 2
配置文件参数 sql_n_shards 也可用于启动 xSQL 来管理数据库分片。当创建数据库并在不同的网络节点上创建不同的分片时,McoDistributedSqlEngine 会向其中一个网络节点发送查询,或者向所有节点广播查询并相应地合并结果。为了演示,假设我们有数据库模式:
create table Orders ( orderId int primary key,
product string,
customer string,
price double,
volume double )
以及 CSV 格式的数据:
orderId|product|customer|price|volume
1|A|james|10.0|100
2|B|bob|50.0|200
3|A|paul|11.0|300
4|C|paul|100.0|150
5|B|bob|52.0|100
6|B|bob|49.0|500
7|A|james|11.0|100
8|C|paul|105.0|300
9|A|bob|12.0|400
10|C|james|90.0|200
首先,我们需要创建几个数据库,并在这些数据库上以服务器模式运行 xSQL。我们将通过使用以下命令行参数来实现这一点:
xsql -size 10m -p 10001
这会创建一个大小为 10 兆字节的内存数据库,并启动 xSQL 作为服务器监听端口 10001。然后我们在端口 10002 和 10003 启动另外两个服务器实例:
xsql -size 10m -p 10002
xsql -size 10m -p 10003
现在,我们可以通过将这些服务器的地址作为命令行选项启动 xSQL,使用 McoDistributedSqlEngine 以客户端身份连接到所有三个服务器:
xsql @127.0.0.1:10001 @127.0.0.1:10002 @127.0.0.1:10003
或者这些地址可以存储在一个配置文件中:
File client.cfg:
{
remote_client : [ "127.0.0.1:10001", "127.0.0.1:10002","127.0.0.1:10003"]
}
然后我们只需使用以下命令启动 xSQL 即可:
xsql -c client.cfg
连接到服务器后,xSQL 进入交互模式。首先,我们使用以下命令在所有节点上创建一个表:
XSQL>create table Orders (orderId int primary key, product string,
customer string, price double, volume double);
默认情况下,分布式 SQL 引擎会将查询发送到所有节点。因此,创建表的语句将在所有三个节点上执行。然后,我们可以使用以下 SQL 语句在服务器之间分配数据:
XSQL>insert into Orders select * from foreign table (path='order.csv', skip=1,nostop=1)
as Orders where mod(orderId, %#)=%@;
伪参数“%#”和“%@”分别表示节点的总数和基于零的节点 ID。在我们的例子中,“%#”等于 3,而“%@”对于第一台服务器(运行在端口 10001 上)为 0,对于第二台服务器为 1,对于第三台服务器为 2。例如,在第二台服务器上,该语句将等同于
XSQL>insert into Orders select * from foreign table (path='order.csv', skip=1,nostop=1)
as Orders where mod(orderId, 3)=1;
因此,具有 ID 为 1、4、7 和 10 的订单将被插入到节点 2 中。(请注意,如果您在不同的主机上启动服务器,文件 order.csv 必须在所有主机上均可访问。)为了检查数据,我们从所有节点中选择记录:
XSQL>select * from Orders order by orderId;
orderId product customer price volume
------------------------------------------------------------------------------------
1 A james 10 100
2 B bob 50 200
3 A paul 11 300
4 C paul 100 150
5 B bob 52 100
6 B bob 4
9 500
7 A james 11 100
8 C paul 105 300
9 A bob 12 400
10 C james 90 200
Selected records: 10
并且仅从第二台服务器中选择记录:
XSQL>2:select * from Orders;
orderId product customer price volume
------------------------------------------------------------------------------------
1 A james 10 100
4 C paul 100 150
7 A james 11 100
10 C james 90 200
结果行排序
当数据以这样的方式分布,即合并后的查询结果无需按任何特定顺序排序时,建议在 SQL 语句中使用带有“*:" 前缀的专用语法。例如:
XSQL>*:select * from Orders;
而不是
XSQL>select * from Orders;
“*:" 前缀指示 SQL 引擎不对组合后的结果集进行排序,从性能角度来看这是有益的。这两个语句都会创建一个结果集,其中包含来自第一个分片的结果与来自第二个分片的结果集相连接,依此类推,对每个分片都如此;并且两个最终结果都是正确的。但如果语句要求最终结果进行排序,那么只能使用第一种形式。换句话说,以下的 select 语句会返回一个已排序的结果集:
XSQL>select * from Orders order by orderId;
orderId product customer price volume
------------------------------------------------------------------------------
1 A james 10 100
2 B bob 50 200
3 A paul 11 300
4 C paul 100 150
5 B bob 52 100
6 B bob 49 500
7 A james 11 100
8 C paul 105 300
9 A bob 12 400
10 C james 90 200
Selected records: 10
然而,下面这个选择语句虽然更快,但返回的是无序(错误的!)结果集:
XSQL>*:select * from Orders order by orderId;
orderId product customer price volume
------------------------------------------------------------------------------
3 A paul 11 300
6 B bob 49 500
9 A bob 12 400
1 A james 10 100
4 C paul 100 150
7 A james 11 100
10 C james 90 200
2 B bob 50 200
5 B bob 52 100
8 C paul 105 300
Selected records: 10
另一个例子是select语句:
XSQL>select count(*) from Orders ;
#1
------------------------------------------------------------------------------
10
Selected records: 1
此语句返回包含正确聚合值 10 的单行结果——即所有分片的总行数。SQL 引擎会合并来自每个分片的结果集。但以下未排序形式的语句会返回三行,分别显示每个分片的正确计数:
XSQL>*:select count(*) from Orders;
#1
------------------------------------------------------------------------------
3
4
3
Selected records: 3
在此,远程 SQL 引擎不会合并结果集。
请注意,“* :”前缀只是“N :”分片编号表示法的扩展,它指示 SQL 引擎仅在指定的单个分片上执行语句;当然,* 表示“所有分片”。
更复杂的分布式查询
我们还可以在分布式分片上执行更复杂的查询,使用分组(group by)和排序(order by)子句。例如:
XSQL>select product, sum(price*volume) as s from Orders group by product;
product s
-----------------------------------------------------------------------
A 10200.000000
B 39700.000000
C 64500.000000
Selected records: 3
和:
XSQL>select customer, sum(price*volume) as s from Orders group by customer order by s;
customer s
-----------------------------------------------------------------------
james 20100.000000
bob 44500.000000
paul 49800.000000
Selected records: 3
这里,sum() 函数应用于来自所有分片的合并结果集。