我想将数据从sql文件导入到ElasticSearch。我知道通过JBDC和Logstash的方式,但是它首先需要将数据加载到mysql中。由于sql文件很大,因此我想跳过这一部分并直接导入。有可能这样做吗?
编辑: 我偶然发现了这个解决方案,但也许有一个更简单的方法: Link
答案 0 :(得分:1)
我要在此处发布the blog post content,是我在2015年写的,因此可能有点过时了(特别是elasticsearch部分-映射和logstash elasticsearch输出:是为Elasticsearch 1.7设计的),但在该方法中仍然有效。
最近,我有一个数据库MySQL转储,当时我正在考虑将其导入Elasticsearch。
首先出现的主意是:
好吧。我发现确实不需要某些步骤。
我实际上可以使用Elastic stack并创建一个简单的配方,该配方可用于导入SQL转储脚本,而无需实际将数据加载到数据库中然后从数据库中再次读取。
我从拥有的MySQL示例数据库中导出了一些数据。您可以下载相同的data。
我们的对象被拆分到3个表上,但是我们不在这里进行联接。我们将仅从Person
表中导入数据。
让我们看一下脚本的重要内容:
--
-- Table structure for table `Person`
--
DROP TABLE IF EXISTS `Person`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `Person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`children` int(11) DEFAULT NULL,
`dateOfBirth` datetime DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`reference` varchar(255) DEFAULT NULL,
`address_id` int(11) DEFAULT NULL,
`marketing_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `FK_tagx64iglr1dxpalbgothv83r` (`address_id`),
KEY `FK_j4ifv49erkwul9jruu15o40r4` (`marketing_id`),
CONSTRAINT `FK_j4ifv49erkwul9jruu15o40r4` FOREIGN KEY (`marketing_id`) REFERENCES `Marketing` (`id`),
CONSTRAINT `FK_tagx64iglr1dxpalbgothv83r` FOREIGN KEY (`address_id`) REFERENCES `Address` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10001 DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `Person`
--
LOCK TABLES `Person` WRITE;
/*!40000 ALTER TABLE `Person` DISABLE KEYS */;
INSERT INTO `Person` VALUES (1,4,'1944-07-21 00:00:00','male','Joe Smith','0',1,1),...,(10000,0,'2009-09-10 00:00:00','female','Stephanie Rebecca','9999',10000,10000);
/*!40000 ALTER TABLE `Person` ENABLE KEYS */;
此文件中的两个重要部分:
CREATE TABLE
为我们提供了所有列标题INSERT INTO
是我们的数据在真正的备份中,数据要多得多,您可能会有多条INSERT INTO
行。
首先,我们基本上需要在这里忽略备份的前108行。
然后让我们看第一行:
cat person.sql | head -109 | tail -1
礼物:
INSERT INTO `Person` VALUES (1,4,'1944-07-21 00:00:00','male','Joe Smith','0',1,1),...,(10000,0,'2009-09-10 00:00:00','female','Stephanie Rebecca','9999',10000,10000);
我们在这里需要做的是用以下模式分割每一行:
INSERT INTO `Person` VALUES (DATA),(DATA),***,(DATA);
我们可以为logstash创建一个mysql.conf
来解析它。像往常一样,从“空”开始:
input { stdin {} }
filter {
}
output { stdout { codec => rubydebug } }
然后,让我们忽略INSERT INTO ...
部分,并将数据提取到名为extracted_sql
的新字段中。为此,我们使用grok filter:
grok {
match => {
"message" => "INSERT INTO \`Person\` VALUES (%{GREEDYDATA:extracted_sql})"
}
remove_field => "message"
}
执行它:
cat person.sql | head -109 | tail -1 | bin/logstash -f mysql.conf
它给出了这样的内容:
{
"@version" => "1",
"@timestamp" => "2015-09-14T07:32:43.495Z",
"host" => "MacBook-Air-de-David.local",
"extracted_sql" => "(..),..(..);
}
我们现在需要将extracted_sql
分成多个事件。让我们添加一个split filter:
split {
terminator => "),("
field => "extracted_sql"
}
再次启动,现在它在每个表行中给出一个事件:
{
"@version" => "1",
"@timestamp" => "2015-09-14T07:38:34.489Z",
"host" => "MacBook-Air-de-David.local",
"extracted_sql" => "1,4,'1944-07-21 00:00:00','male','Joe Smith','0',1,1"
}
// ...
{
"@version" => "1",
"@timestamp" => "2015-09-14T07:37:25.729Z",
"host" => "MacBook-Air-de-David.local",
"extracted_sql" => "8906,3,'1958-12-17 00:00:00','male','Gautier Titouan','8905',8906,8906"
}
// ...
{
"@version" => "1",
"@timestamp" => "2015-09-14T07:38:34.489Z",
"host" => "MacBook-Air-de-David.local",
"extracted_sql" => "10000,0,'2009-09-10 00:00:00','female','Stephanie Rebecca','9999',10000,10000"
}
听起来像现在有了CSV结构...我们可以使用CSV filter或GROK filter。
Grok提供了更大的灵活性,因为它有助于为每个字段定义所需的正确数据类型。 CSV过滤器无法直接执行at the moment。 Grok可以做到这一点,但是它基于正则表达式,并且比优化分析CSV内容的CSV过滤器要慢得多。因此,我在这里进行交易的灵活性和易于使用的性能。
csv {
source => "extracted_sql"
quote_char => "'"
columns => [ "id",
"children", "dateOfBirth", "gender", "name",
"reference", "address_id", "marketing_id" ]
remove_field => "extracted_sql"
}
如果必须处理NULL
值,只需在CSV过滤器之前添加:
mutate {
gsub => [
"extracted_sql", "NULL", ""
]
}
我们还有一些日期,格式多样:
"@timestamp" => "2015-09-14T07:38:34.489Z",
"dateOfBirth" => "2009-09-10 00:00:00"
dateOfBirth
显然是创建日期。 @timestamp
始终是内部logstash时间戳。我们希望dateOfBirth
成为我们的活动日期。
date {
match => [ "dateOfBirth", "YYYY-MM-DD HH:mm:ss" ]
remove_field => "dateOfBirth"
}
到目前为止很好。但是标题部分呢?
好吧,我们有第一个grok
模式,该模式尝试解析INSERT ...
,因此如果失败,它将生成一个_grokparsefailure
标签。我们可以删除包含以下内容的每一行:
# Just after the grok filter
if "_grokparsefailure" in [tags] {
drop { }
}
我们现在可以在完整文件上运行logstash配置:
cat person.sql | bin/logstash -f mysql.conf
我们现在输出:
{
"@version" => "1",
"@timestamp" => "1967-01-17T23:00:00.000Z",
"host" => "MacBook-Air-de-David.local",
"id" => "9999",
"children" => "1",
"gender" => "female",
"name" => "Laetitia Lois",
"reference" => "9998",
"address_id" => "9999",
"marketing_id" => "9999"
}
我们不需要保留@version
和host
字段:
mutate {
remove_field => [ "@version", "host" ]
}
它给出:
{
"@timestamp" => "1967-01-17T23:00:00.000Z",
"id" => "9999",
"children" => "1",
"gender" => "female",
"name" => "Laetitia Lois",
"reference" => "9998",
"address_id" => "9999",
"marketing_id" => "9999"
}
不是最难的部分。但这可能是因为我从事弹性搜索已经有将近5年了:)!
对于新来者,您必须:
tar xzf elasticsearch-1.7.1.tar.gz
bin/plugin install elasticsearch/marvel/latest
bin/elasticsearch
并连接logstash ...
output {
elasticsearch {
host => "localhost"
port => "9200"
protocol => "http"
index => "persons-%{+YYYY}"
document_type => "person"
document_id => "%{id}"
template => "person.json"
template_name => "person"
}
stdout {
codec => "dots"
}
}
请注意,我们使用类型persons-YEAR4DIGITS
和原始person
作为文档id
,在名为_id
的索引中按年份分组,发送文档。
person.json
文件包含我们的模板。我们定义使用1个单个分片,不需要_all
字段和其他一些设置:
{
"template": "persons-*",
"order": 1,
"settings": {
"number_of_shards": 1
},
"mappings": {
"_default_" : {
"_all" : {"enabled" : false},
"dynamic_templates" : [ {
"string_fields" : {
"match" : "*",
"match_mapping_type" : "string",
"mapping" : {
"type" : "string", "index" : "analyzed", "omit_norms" : true,
"fields" : {
"raw" : {"type": "string", "index" : "not_analyzed", "ignore_above" : 256}
}
}
}
} ]
},
"person": {
"properties": {
"id": {
"type": "long",
"index": "no"
}
}
}
}
}
启动!
cat person.sql | bin/logstash -f mysql.conf
如果要提高注入速度,只需在logstash中添加更多工人:
cat person.sql | bin/logstash -f mysql.conf -w 2