DataStax Bulk Loader - dsbulk是在DSE 6 引入的一种新的批量加载方法。(点击这里下载DataStax Bulk Loader)。

它提供了将数据加载(load)到DataStax Enterprise和Apache Cassandra,以及将数据从DSE和Apache Cassandra中卸载(unload)并对数据进行计数的更有效率的方案,不需要编写任何自定义的代码或是有其他组件,如Apache Spark。除了含有批量加载 (Bulk Load)以及批量卸载(bulk unload)的功能,dsbulk 也可以帮助将数据迁移到新的数据模型和在DSE系统或者其他数据系统之间的数据迁移。

dsbulk 有以下优点:

  • 容易使用

  • 支持常见的输入数据的格式

  • 将数据导出为常见的输出数据格式

  • 支持多种字段的格式,像是日期和时间

  • 支持所有的DSE数据类型,包括了用户自定义的类型

  • 支持高级的安全配置

  • 能妥善处理解析错误的数据和数据库插入错误

  • 能报告加载任务的状态和完成情况,包括总结统计信息(如加载率 load rate)

  • 高效且快捷

我比较喜欢通过范例来学习,在本系列的博客中,我将展示一些运用dsbulk完成一些常见的任务的方法。

$ cqlsh -e "SELECT COUNT(*) FROM dsbulkblog.iris_with_id;"

count
-------
150 (1 rows) Warnings :
Aggregation query used without partition key

设置(Setting)

在开始这些范例之前,让我们先开始用cqlsh创建一些要用到的表格:

将被用到的数据文件存放在这个公开的Gist里。您需要单独将文件下载到你本地机器。为了简单起见,我们假设您将他们下载到了/tmp/dsbulkblog,您将看到以下这些文件:

$ ls -lh /tmp/dsbulkblog/*
-rw-rw-r-- 1 digger digger 5.0K Jun 13 14:55 /tmp/dsbulkblog/iris.csv
-rw-rw-r-- 1 digger digger 5.0K Jun 13 14:55 /tmp/dsbulkblog/iris_no_header.csv
-rw-rw-r-- 1 digger digger 5.0K Jun 13 14:56 /tmp/dsbulkblog/iris_with_comment.csv
-rw-rw-r-- 1 digger digger 3.1K Jun 13 14:56 /tmp/dsbulkblog/iris_with_nulls.csv
-rw-rw-r-- 1 digger digger 3.7K Jun 13 14:56 /tmp/dsbulkblog/iris_with_null_string.csv
-rw-rw-r-- 1 digger digger 4.5K Jun 13 14:57 /tmp/dsbulkblog/iris_without_id.csv
-rw-rw-r-- 1 digger digger 289 Jun 13 14:57 /tmp/dsbulkblog/president_birthdates.psv
-rw-rw-r-- 1 digger digger 562 Jun 13 14:57 /tmp/dsbulkblog/sportsteams.csv
$ wc /tmp/dsbulkblog/* 151 151 5101 /tmp/dsbulkblog/iris.csv
150 150 5040 /tmp/dsbulkblog/iris_no_header.csv
152 155 5120 /tmp/dsbulkblog/iris_with_comment.csv
151 151 3101 /tmp/dsbulkblog/iris_with_nulls.csv
151 151 3701 /tmp/dsbulkblog/iris_with_null_string.csv
151 151 4608 /tmp/dsbulkblog/iris_without_id.csv
7 26 289 /tmp/dsbulkblog/president_birthdates.psv
6 61 562 /tmp/dsbulkblog/sportsteams.csv
919 996 27522 total

现在我们已经准备好开始展示范例了!

加载范例 (Loading Examples)

范例1.1 简易的加载

第一个范例是最简单的。让我们将iris.csv 的数据载入dsbulkblog.iris_with_id 表格。

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id

在我们看输出和结果之前,我们将这条命令行拆解成以下几部分:

  • load:  在dsbulk后面,我们有load。dsbulk现在有三种模式(dsbulk 1.2)中,加载(load),卸载(unload)和 计数(count)。就正如您可能猜测的,加载是将数据从文件中导入Cassandra,卸载是将数据从Cassandra导出到文件中。计数会给Cassandra中的数据进行计数以及报告各种指标。

  • -url: 接下来的是 -url 参数。它加载的是一个文件名或者文件的位置(或者如 HTML URL 的资源连接)。它可以是一个单独的文件,一个一个文件的地址,一个URL或者标准输入stdin(默认值)。在本范例中,这指向我们下载的 /tmp/dsbulkblog/iris.csv 文件。

  • -k: 接下来的是-k参数,这是使用的键空间(keyspace)名。

  • -t: 接着是-t参数,这是使用的表(table)名。

  • 我们将 -k 设为 dsbulkblog,将 -t设为 iris_with_id 意味着我们将把数据加载到  dsbulkblog.iris_with_id 的表中。

以下是我们执行此命令的输出:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id
Operation directory: /tmp/logs/LOAD_20190314-161940-640593
total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches
150 | 0 | 329 | 0.02 | 0.05 | 9.84 | 39.58 | 45.88 | 1.00
Operation LOAD_20190314-161940-640593 completed successfully in 0 seconds.
Last processed positions can be found in positions.txt

我们可以看到 dsbulk 已经将一些信息写入了stderr。让我回顾一下最终的统计表,该表表明总共有150条记录而且所有记录都成功。failed的列是未成功加载的行数。其余的统计信息的是load的吞吐量和延迟指标。

我们可以确认数据库有150条记录包含了一个COUNT 的查询(备注:我们在这里通过cqlsh 进行COUNT 的查询是为了展示的目的。我们会在之后的文章中进一步讨论dsbulk 的计数操作)。

同时,在这消息里也包含了很多其他的信息,包括了日志目录的信息。我在 /tmp 的目录下运行里这条指令,dsbulk在默认情况下会在运行命令的目录下创建一个叫做 logs 的子目录, 每当一个dsbulk指令运行时,会在本子目录下再创建一个日志目录。

加载的指令会有一个LOAD_  开头的日志目录,卸载会有一个UNLOAD_ 开头的目录,计数会有一个COUNT_ 开头的。从输出我们可以看到:

操作地址: /tmp/logs/LOAD_20190314-155621-436426.

敏锐的眼睛会注意到,dsbulk 运行产生的文件名包含是的年、月、日、小时、分钟、秒。这个日志目录包含了两封文件: operation.log 和 positions.txt。

operation.log 是主要的日志文件,包含了使用的设置以及主要操作的日志。position.txt用于记录上次停止运行的地方并继续开始加载数据。如果在dsbulk运行中有其他的错误(我们之后会看到一些), 我们将会在本目录看到其他文件。

范例2.1 从标准输入Stdin 加载(Loading From Stdin)

Dsbulk旨在像其他命令行工具一样工作,并将数据从标准输入加载到数据库以及将数据卸载到标准输出stdout。事实上,默认的 -url 是标准输入来加载而标准输出则适用于unload。

例如:

$ cat /tmp/dsbulkblog/iris.csv | sed 's/Iris-//g' | dsbulk load -k dsbulkblog -t iris_with_id

我们可以快速通过一个SELECT 查询来检查数据并没有Iris- 前缀。

$ cqlsh -e "SELECT * FROM dsbulkblog.iris_with_id LIMIT 3;"
id | petal_length | petal_width | sepal_length | sepal_width | species
-----+--------------+-------------+--------------+-------------+------------
23 | 1.7 | 0.5 | 5.1 | 3.3 | setosa
114 | 5.1 | 2.4 | 5.8 | 2.8 | virginica
53 | 4 | 1.3 | 5.5 | 2.3 | versicolor
(3 rows)

我们也可以向没有标题的数据加一个标题行。比如,iris_no_header.csv没有一个标题行(header),但是与iris.csv相同,我们可以用awk来自定义一个标题行,如:

$ cat /tmp/dsbulkblog/iris_no_header.csv | awk 'BEGIN{print "sepal_length,sepal_width,petal_length,petal_width,species,id"} {print $0}' | dsbulk load -k dsbulkblog -t iris_with_id

这是一种方式,还有其他方法。

我们也可以用命令行添加缺失的列。比如,iris_without_id.csv 文件就像 iris.csv,但没有id列。因为id是主键列(Primary Key)的一部分,我们需要创建此列,awk 可以帮我们完成。

$ cat /tmp/dsbulkblog/iris_without_id.csv | awk 'BEGIN{id=-1} {if (id == -1)
{printf("%s,id\n", $0);} else {printf("%s,%d\n", $0, id);} id++;}' | dsbulk load -k dsbulkblog -t iris_with_id

所以很明显我喜欢awk,sed还有cut。但是你可以用任何你想用的命令行工具:perl, python, 等。或你可以用任何你喜欢的编程语言编写程序写入stdout 如,Java, C 等。

范例2.2: 从一个URL加载(Loading From A URL)

Dsbulk 也将可以从一个URL加载,如使通过iris.csv 的HTTP 地址:

$ dsbulk load -k dsbulkblog -t iris_with_id -url https://gist.github.com/brianmhess/8864cf0cb0ce9ea1fd64e579e9f41100/raw/522a1f564a381d7eacf6955c490bb6331d4369b2/iris.csv

范例2.3 从一个文件目录加载(Loading A Directory Of Files)

Dsbulk 也可以从一个文件目录加载数据。让我们创建一个目录,并把iris.csv加入本目录:

$ mkdir /tmp/dsbulkblog/iris $ cp /tmp/dsbulkblog/iris.csv /tmp/dsbulkblog/iris

现在让我们用以下命令行将/tmp/dsbulkblog/iris 目录中的文件导入:

$ dsbulk load -url /tmp/dsbulkblog/iris -k dsbulkblog -t iris_with_id

范例2.4:在目录中只加载一些文件(Loading Only Some Files In A Directory)

我们也可以在目录中只加载一些文件。让我们将president_birthdates.psv 放在 /tmp/dsbulkblog/iris 目录下,同时告诉dsbulk 只加载 .csv 类型文件。

$ cp /tmp/dsbulkblog/president_birthdates.psv /tmp/dsbulkblog/iris

我们会通过指定--connector.csv.fileNamePattern 参数告诉dsbulk只加载* .csv 文件(**/*.csv 文件是CSV文件的默认值,但我们在这里只是为了展示):

$ dsbulk load -url /tmp/dsbulkblog/iris -k dsbulkblog -t iris_with_id --connector.csv.fileNamePattern "**/*.csv"

范例3: 映射(Mapping)

现在如何让dsbulk 知道将哪个输入域映射到dsbulkblog.iris_with_id 表上的哪一列呢?它会先寻找输入数据第一行或者标题行。/tmp/dsbulkblog/iris.csv 文件的第一行是:

sepal_length,sepal_width,petal_length,petal_width,species,id

这些列名刚好与 dsbulkblog.iris_with_id 的名称完全匹配。dsbulk 默认用这此标题行学习如何将输入字段与列表名称匹配。很幸运的是它们可以匹配。

如果列名与数据的输入值不匹配呢?好吧,那我们可以将输入字段单独映射到列表名字。有几种方法可以做到这一点。首先是列出从文件所具有的内容到表所具有的内容的映射。例如:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "sepal_length = sepal_length, sepal_width = sepal_width, petal_length = petal_length, petal_width = petal_width, species = species, id = id"

不过,这个例子有一点傻,因为标题中的字段标题行与列表名相同。借用上面我们运用到标题行的例子,让我们用一个不同的标题行来进行映射。

$ cat /tmp/dsbulkblog/iris_no_header.csv | awk 'BEGIN{print
"sepal_l,sepal_w,petal_l,petal_w,species_name,id_number"} {print $0}' | dsbulk load -k dsbulkblog -t iris_with_id -m "sepal_l = sepal_length, sepal_w = sepal_width, petal_l = petal_length, petal_w = petal_width, species_name = species, id_number = id"

您可以看到, -m包含了从标题行中指定的输入字段标题行到数据库模式中指定的列名称的映射列表。因此,名为sepal_l的输入字段将加载到名为sepal_length的列中。

你也可以在 -m 中写简写, *=*。这表示所有匹配的名称都将会匹配(如果没有提供,则是默认映射)。所以以下两种方法是对等的:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "sepal_length = sepal_length, sepal_width = sepal_width, petal_length = petal_length, petal_width = petal_width, species = species, id = id"
$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "*=*"

我们也可以指定我们应该加载除一个以外所有匹配的列。例如,如果我们不想加载species 列,则可以执行以下操作:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "* = -species"

同时我们如果不想加载species 或者petal_length 列, 则可以:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "* = [-species, -petal_length]"

范例 3.1: 按索引映射

通过索引来映射是除了名字以外的另外一种方法。因此,考虑到 iris_no_header.csv 文件没有标题行,我们则可以映射输入列表的索引:

$ dsbulk load -url /tmp/dsbulkblog/iris_no_header.csv -k dsbulkblog -t iris_with_id -header false -m "0=sepal_length, 1=sepal_width, 2=petal_length, 3=petal_width, 4=species, 5=id"

范例3.2: 映射一个列表

我们也可以告诉dsbulk 忽略原本标题行,而用一个我们想让dsbulk 阅读的特定列表来替换。

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "sepal_length,sepal_width,petal_length,petal_width,species,id"

这将会按顺序映射列表。也就是说,第一个字段映射到sepal_length 列,而第二个字段映射到sepal_width 列, 以此类推。 如果输入文具有field1, field2, 等等的标题行,则等同于:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "field1=sepal_length,field2=sepal_width,field3=petal_length,field4=petal_width,field5=species,field6=id"

类似的,如果输入文件没有标题行,则等效于:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -header false -m "0=sepal_length,1=sepal_width,2=petal_length,3=petal_width,4=species,5=id"

范例 3.3: 跳过记录

请注意我们不会将标题行-header 设置为false。 如果我们这么做,我们则需要跳过第一行,不然我们就会尝试将第一行加载成数据。我们可以通过 -skipRecords 参数来处理:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "sepal_length,sepal_width,petal_length,petal_width,species,id" -header false -skipRecords 1

我们也可以跳过包含标题行的前100行数据,只加载最后50行:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "sepal_length,sepal_width,petal_length,petal_width,species,id" -header false -skipRecords 101

我们可以看到我们只加载了50行:

total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches   

50 |      0 | 122 | 0.01 |   0.05 | 10.13 | 23.33 |  23.33 | 1.00

范例 3.4: 只加载一些的记录

当讨论跳过记录的功能时,我们也应该讨论只加载一些记录的情况。我们可以用-maxRecords 将加载数记为前20条记录。

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -maxRecords 20

我们可以看到我们只加载了20条记录:

total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches 20 | 0 | 53 | 0.00 | 0.05 | 5.86 | 12.26 | 12.26 | 1.00

我们同时也可以跳过和限制加载数据:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -skipRecords 100 -maxRecords 20

范例 3.5: 错误的映射或者错误的解析

现在,假设我们做了了些什么,在之前例子中少加载一列。比如我们忘记了id 列。

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -header false -m "sepal_length,sepal_width,petal_length,petal_width,species"

Operation directory: /tmp/logs/LOAD_20190314-162512-798132

Operation LOAD_20190314-162512-798132 failed: Missing required primary key column id from schema.mapping or schema.query.

Last processed positions can be found in positions.txt

我们看到会产生一个错误,因为id 列是一个主键列,但dsbulk需要有指定所有的主键列。

然而,如果我们包含了所有的列但是调换了列之间的顺序,例如将id 和 species 列调换顺序,我们将会得到一个不同的报错:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -k dsbulkblog -t iris_with_id -m "0=sepal_length,1=sepal_width,2=petal_length,3=petal_width,4=id,5=species"

Operation directory: /tmp/logs/LOAD_20190314-162539-255317

Operation LOAD_20190314-162539-255317 aborted: Too many errors, the maximum allowed is 100.

total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches  

102 |    101 |  0 | 0.00 |  0.00 | 0.00 |  0.00 | 0.00 |   0.00 Rejected records can be found in the following file(s): mapping.bad

Errors are detailed in the following file(s): mapping-errors.log

Last processed positions can be found in positions.txt

这里我们可以看到产生了100多个报错。 但是这是什么类型的报错呢?为了了解详情,我们需要检查/tmp/logs/LOAD_20190314-162539-255317 文件,特别是mapping-errors.log。 这报错文件的开头看起来像:

Resource: file:/tmp/dsbulkblog/iris.csv
Position: 129
Source: 6.4,2.8,5.6,2.1,Iris-virginica,128\u000a
java.lang.IllegalArgumentException: Could not parse 'Iris-virginica'; accepted formats are: a valid number (e.g. '1234.56'), a valid Java numeric format (e.g. '-123.45e6'), a valid date-time pattern (e.g. '2019-03-14T16:25:41.267Z'), or a valid boolean word
  at com.datastax.dsbulk.engine.internal.codecs.util.CodecUtils.parseNumber(CodecUtils.java:119)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToNumberCodec.parseNumber(StringToNumberCodec.java:72)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToIntegerCodec.externalToInternal(StringToIntegerCodec.java:55)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToIntegerCodec.externalToInternal(StringToIntegerCodec.java:26)
  at com.datastax.dsbulk.engine.internal.codecs.ConvertingCodec.serialize(ConvertingCodec.java:50)
Suppressed: java.time.format.DateTimeParseException: Text 'Iris-virginica' could not be parsed at index 0
  at java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:1949)
  at java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1819)
  at com.datastax.dsbulk.engine.internal.codecs.util.SimpleTemporalFormat.parse(SimpleTemporalFormat.java:41)
  at com.datastax.dsbulk.engine.internal.codecs.util.ZonedTemporalFormat.parse(ZonedTemporalFormat.java:45)
  at com.datastax.dsbulk.engine.internal.codecs.util.CodecUtils.parseNumber(CodecUtils.java:106)
Suppressed: java.lang.NumberFormatException: For input string: "Iris-virginica"
  at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
  at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
  at java.lang.Double.parseDouble(Double.java:538)
  at java.lang.Double.valueOf(Double.java:502)
  at com.datastax.dsbulk.engine.internal.codecs.util.CodecUtils.parseNumber(CodecUtils.java:101)
Suppressed: java.lang.NumberFormatException: null
  at java.math.BigDecimal.(BigDecimal.java:494)
  at java.math.BigDecimal.(BigDecimal.java:383)
  at java.math.BigDecimal.(BigDecimal.java:806)
  at com.datastax.dsbulk.engine.internal.codecs.util.CodecUtils.parseNumber(CodecUtils.java:96)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToNumberCodec.parseNumber(StringToNumberCodec.java:72)
Suppressed: java.text.ParseException: Invalid number format: Iris-virginica
  at com.datastax.dsbulk.engine.internal.codecs.util.CodecUtils.parseNumber(CodecUtils.java:247)
  at com.datastax.dsbulk.engine.internal.codecs.util.CodecUtils.parseNumber(CodecUtils.java:92)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToNumberCodec.parseNumber(StringToNumberCodec.java:72)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToIntegerCodec.externalToInternal(StringToIntegerCodec.java:55)
  at com.datastax.dsbulk.engine.internal.codecs.string.StringToIntegerCodec.externalToInternal(StringToIntegerCodec.java:26)

我们可以看到dsbulk尝试将Iris-setosa 转换成数字。这是因为它认为这个列是id 列,包含INT值。如果我们仔细看文件,我们就会看到一次又一次相同的错误。我们显然搞砸了映射。在100条错误(这个是默认值,但是可以通过设置 -maxErrors参数复写)之后,dsbulk 退出尝试加载的进程。

当dsbulk 找到了一个不能解析的输入值,它会将那行加入mapping.bad 文件。这是dsbulk在导入时看到的输入行。如果一或两行出现乱码或者和其它行有不同的格式,dsbulk会将其加入mapping.bad文件,将错误记录到mapping-errors.log的日志中,但是会继续执行加载直到最大容错数为止。这样,一些坏行不会阻碍整个加载过程,而且用户也可以处理这些坏行,像手动插入它们甚至使用不同参数在mapping.bad文件重新运行dsbulk。

例如,我们可以通过以下方法重新加载这些坏行:

$ dsbulk load -url /tmp/logs/LOAD_20190314-162539-255317/mapping.bad -k dsbulkblog -t iris_with_id -header false -m "0=sepal_length,1=sepal_width,2=petal_length,3=petal_width,4=species, 5=id"

Operation directory: /tmp/logs/LOAD_20190314-163033-590156 total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches  

101 |      0 | 231 | 0.01 |   0.05 | 8.50 | 17.17 |  18.22 | 1.00

Operation LOAD_20190314-163033-590156 completed successfully in 0 seconds.

Last processed positions can be found in positions.txt

范例 3.6: 缺失字段和额外字段

有些时候我们只加载了一些列。当表格的列比输入数据的列多时,dsbulk默认会报错。现在尽管有必要指定所有主键列,但也可以允许其它列保持未定义或者未设置的状态。

例如,我们假设我们没有定义sepal_length 列,我们可以像下面例子用awk模拟这一情况:

$ cat /tmp/dsbulkblog/iris.csv | awk -F, '{printf("%s,%s,%s,%s,%s\n", $2, $3, $4, $5, $6)}' | dsbulk load -k dsbulkblog -t iris_with_id

因为我们没有提供sepal_length列,这最终会产生一个错误:

Operation directory: /tmp/logs/LOAD_20190314-163121-694645

At least 1 record does not match the provided schema.mapping or schema.query. Please check that the connector configuration and the schema configuration are correct.

Operation LOAD_20190314-163121-694645 aborted: Too many errors, the maximum allowed is 100. total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches  

102 |    101 |  0 | 0.00 |  0.00 | 0.00 |  0.00 | 0.00 |   0.00

Rejected records can be found in the following file(s): mapping.bad

Errors are detailed in the following file(s): mapping-errors.log

Last processed positions can be found in positions.txt

为了解决这个问题,我们可以添加:

--schema.allowMissingFields:

$ cat /tmp/dsbulkblog/iris.csv | awk -F, '{printf("%s,%s,%s,%s,%s\n", $2, $3, $4, $5, $6)}' | dsbulk load -k dsbulkblog -t iris_with_id --schema.allowMissingFields true

同理,我们可以允许输入文件有额外列的情况。

$ cat /tmp/dsbulkblog/iris.csv | awk -F, '{printf("%s,%s,%s,%s,%s,%s,extra\n", $1, $2, $3, $4, $5, $6)}' | dsbulk load -k dsbulkblog -t iris_with_id

这样将会正常加载列,但额外字段会被忽视。但是如果我们希望严格限制输入有额外字段的情况,可以让dsbulk报错:

$ cat /tmp/dsbulkblog/iris.csv | awk -F, '{printf("%s,%s,%s,%s,%s,%s,extra\n", $1, $2, $3, $4, $5, $6)}' | dsbulk load -k dsbulkblog -t iris_with_id --schema.allowExtraFields false

这个错误是因为我们没有说明如何映射额外的字段:

Operation directory: /tmp/logs/LOAD_20190314-163305-346514

At least 1 record does not match the provided schema.mapping or schema.query. Please check that the connector configuration and the schema configuration are correct.

Operation LOAD_20190314-163305-346514 aborted: Too many errors, the maximum allowed is 100. total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches  

102 |    101 |  0 | 0.00 |  0.00 | 0.00 |  0.00 | 0.00 |   0.00

Rejected records can be found in the following file(s): mapping.bad

Errors are detailed in the following file(s): mapping-errors.log

Last processed positions can be found in positions.txt

范例 3.7: 空格

有些时候在text string 的开头会有空格。我们有时候想保留这个空格但有时候并不想保留。 这可以通过参数--connector.csv.ignoreLeadingWhitespaces 进行控制,默认为false(空格被保留)。比如:

$ cat /tmp/dsbulkblog/iris.csv | sed "s/Iris/  Iris/g" | dsbulk load -k dsbulkblog -t iris_with_id

我们可以通过dsbulk unload 命令检查被保留的前导空格(我们会在之后的博客文章进行讨论):

$ dsbulk unload -query "SELECT species FROM dsbulkblog.iris_with_id" | head

Operation directory: /tmp/logs/UNLOAD_20190321-144112-777452

total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms

150 | 0 | 392 | 0.01 | 0.02 | 9.49 | 41.68 | 41.68

Operation UNLOAD_20190321-144112-777452 completed successfully in 0 seconds.

species

Iris-setosa

Iris-setosa

Iris-virginica

Iris-versicolor

Iris-virginica

Iris-versicolor

Iris-virginica

Iris-virginica

Iris-virginica

要剥离前导空格,我们将--connector.csv.ignoreLeadingWhitespaces 设为true,再运行一次 load 指令:

$ cat /tmp/dsbulkblog/iris.csv | sed "s/Iris/ Iris/g" | dsbulk load -k dsbulkblog -t iris_with_id --connector.csv.ignoreLeadingWhitespaces true

同理,我们可以通过dsbulk unload 指令来检查:

$ dsbulk unload -query "SELECT species FROM dsbulkblog.iris_with_id" | head

Operation directory: /tmp/logs/UNLOAD_20190321-144510-244786

total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms  

150 |      0 | 416 | 0.01 |   0.01 | 8.16 | 36.96 |  36.96

Operation UNLOAD_20190321-144510-244786 completed successfully in 0 seconds.

species

Iris-setosa

Iris-setosa

Iris-virginica

Iris-versicolor

Iris-virginica

Iris-versicolor

Iris-virginica

Iris-virginica

Iris-virginica

范例 4:提供查询

范例 4.1: 自己提供查询

我们可以实际提供一个INSERT 语句本身来指定映射:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -query "INSERT INTO dsbulkblog.iris_with_id(id,petal_width,petal_length,sepal_width,sepal_length,species) VALUES (:id, :petal_width, :petal_length, :sepal_width, :sepal_length, :species)"

请注意我们这里不需要指定 -k 或者 -t的参数,因为它已经包含在查询语句里了。

范例 4.2: 提供一个含有位置映射的查询

我们可以实际提供一个INSERT 语句本身来指定映射:

$ dsbulk load -url /tmp/dsbulkblog/iris_no_header.csv -query "INSERT INTO dsbulkblog.iris_with_id(petal_width,petal_length,sepal_width,sepal_length,species,id) VALUES (?,?,?,?,?,?)" -header false

请注意我需要将id 列移到列表的最后。因为这里的顺序很重要,而且必须将顺序与输入数据匹配。在我们数据中,id列放在最后。

范例 4.3: 提供一个含有常量的查询

我们也可以指定一个将常量值放入列的映射。例如,让我们把所有species 列设为相同的值:

$ dsbulk load -url /tmp/dsbulkblog/iris.csv -query "INSERT INTO dsbulkblog.iris_with_id(id,petal_width,petal_length,sepal_width,sepal_length,species) VALUES (:id, :petal_width, :petal_length, :sepal_width, :sepal_length, 'some kind of iris')"

案例 4.4: 通过一个查询删除数据

这看起来似乎违反直觉,但是我们可以使用disbulk load 命令删除数据。除了使用INSERT 语句,我们也可以发出DELETE 语句。 例如,让我们删除我们表中与iris.csv 文件对应的前10行 (11 如果您包含了标题行)。

$ head -11 /tmp/dsbulkblog/iris.csv | dsbulk load -query "DELETE FROM dsbulkblog.iris_with_id WHERE id=:id"

Operation directory: /tmp/logs/LOAD_20190320-180959-025572

total | failed | rows/s | mb/s | kb/row | p50ms | p99ms | p999ms | batches   

10 |      0 | 32 | 0.00 |   0.00 | 4.59 | 5.96 |   5.96 | 1.00

Operation LOAD_20190320-180959-025572 completed successfully in 0 seconds.

Last processed positions can be found in positions.txt

我们可以cqlsh里检查,发现这些行已经被删除了:

$ cqlsh -e "SELECT * FROM dsbulkblog.iris_with_id WHERE id IN (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)"

id | petal_length | petal_width | sepal_length | sepal_width | species

----+--------------+-------------+--------------+-------------+-------------

10 |          1.5 | 0.2 |          5.4 | 3.7 | Iris-setosa

11 |          1.6 | 0.2 |          4.8 | 3.4 | Iris-setosa

12 |          1.4 | 0.1 |          4.8 | 3 | Iris-setosa

13 |          1.1 | 0.1 |          4.3 | 3 | Iris-setosa

14 |          1.2 | 0.2 |          5.8 | 4 | Iris-setosa

15 |          1.5 | 0.4 |          5.7 | 4.4 | Iris-setosa

(6 rows)

显然,我们可以在这里做其它所有种类的查询。我们可以做计数更新(counter updates)、集合更新(collection updates)等等。


点击这里下载 DataStax Bulk Loader。

随机推荐

  1. java.lang.NoSuchMethodError: org.apache.tomcat.util.res.StringManager.getManager(Ljava/lang/Class;)Lorg/apache/tomcat/util/res/StringManager

    问题: 使用Springboot打包为war部署于Tomcat7中报错 java.lang.NoSuchMethodError: org.apache.tomcat.util.res.StringMa ...

  2. Android学习笔记之短信验证码的获取和读取

    PS:最近很多事情都拖拖拉拉的..都什么办事效率啊!!! 还得吐槽一下移动运营商,验证码超过五次的时候,直接把我的手机号封闭.真是受够了. 学习笔记: 1.Android之如何获取短信验证码. 2.如 ...

  3. AWS AutoScaling

    origin_from: http://blog.csdn.net/libing_thinking/article/details/48327189 AutoScaling 是 AWS 比较核心的一个 ...

  4. JavaSctipr 兼容、技巧、牛角尖

    关于JavaSctipt的兼容性,最懒的办法就是用jQuery的工具函数.尽量不要用那些什么ECMAScript之类的函数,因为很多浏览器都会报找不到函数的错误.下面列出一些在开发过程中碰到过的jav ...

  5. MongoDB文档基本操作

    一.插入文档 使用insert()或save()方法向集合插入文档 >db.COLLECTION_NAME.insert(document) 详细用法可以参考MongoDB菜鸟教程 二.查找文档 ...

  6. Luogu P2292 [HNOI2004]L语言

    题目链接 \(Click\) \(Here\) 好久没写\(DP\)了真是水平下降不少,一眼把这个题搞成贪心了,然后一发交上只有\(37\)分\(QwQ\) 这个题好像还可以\(AC\)自动机胡搞?不 ...

  7. ANTLR v4 权威参考笔记(目录)

    ANTLR v4是一款强大的语法分析器生成器,可以用来读取.处理.执行和转换结构化文本或二进制文件.通过称为文法的形式化语言描述,ANTLR可以为该语言自动生成词法分析器.生成的语法分析器可以自动构建 ...

  8. WIFEXITED/WEXITSTATUS/WIFSIGNALED

    WIFEXITED/WEXITSTATUS/WIFSIGNALED If the exit status value (*note Program Termination::) of the chil ...

  9. 读懂UML类图

    平时阅读一些远吗分析类文章或是设计应用架构时没少与UML类图打交道.实际上,UML类图中最常用到的元素五分钟就能掌握,下面赶紧来一起认识一下它吧: 一.类的属性的表示方式 在UML类图中,类使用包含类 ...

  10. java 多线程 day08 java5多线程新特性

    /** * Created by chengtao on 17/12/3. */public class Thread0801_java5_Atomaic { /* 三个包: http://tool. ...