elastic search数据建模-对文件系统进行数据建模以及文件搜索实战

本文介绍如何使用Elasticsearch对文件系统进行建模,包括数据构造、映射设置、搜索策略及并发控制。通过path_hierarchy分析器处理路径层级,实现对特定目录下含关键词内容的文件搜索,并探讨了乐观锁与悲观锁两种并发控制策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

需求:对类似文件系统这种的有多层级关系的数据进行建模;

1、文件系统数据构造

PUT /filesystem
{
  "settings": {
    "analysis": {
      "analyzer": {
        "paths": { 
          "tokenizer": "path_hierarchy"
        }
      }
    }
  }
}

其中path_hierarchy tokenizer,对应将路径

/a/b/c/d --> path_hierarchy -> /a/b/c/d, /a/b/c, /a/b, /a

2、设置mapping

PUT /filesystem/_mapping/file
{
  "properties": {
    "name": { 
      "type":  "keyword"
    },
    "path": { 
      "type":  "keyword",
      "fields": {
        "tree": { 
          "type":     "text",
          "analyzer": "paths"
        }
      }
    }
  }
}

3、添加数据

PUT /filesystem/file/1
{
  "name":     "README.txt", 
  "path":     "/workspace/projects/helloworld", 
  "contents": "这是我的第一个elasticsearch程序"
}

4、对文件系统执行搜索

需求1:查找一份,内容包括elasticsearch,在/workspace/projects/hellworld这个目录下的文件

GET /fs/file/_search 
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "contents": "elasticsearch"
          }
        },
        {
          "constant_score": {
            "filter": {
              "term": {
                "path": "/workspace/projects/helloworld"
              }
            }
          }
        }
      ]
    }
  }
}

需求2:搜索/workspace目录下,内容包含elasticsearch的所有的文件

GET /fs/file/_search 
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "contents": "elasticsearch"
          }
        },
        {
          "constant_score": {
            "filter": {
              "term": {
                "path.tree": "/workspace"
              }
            }
          }
        }
      ]
    }
  }
}

5、悲观锁并发控制

乐观锁的概念:首先获取对应数据的版本号,带着当前获取的version去执行修改,如果一旦发现当前version跟之前自己获取的不一样,则必须重新获取新的version号再次尝试修改;

悲观锁的概念:直接给数据加锁,此时就只有你能执行各种各样的操作,其他人不能执行操作;

(1)基于es全局锁实现

PUT /filesystem/lock/global/_create
{}

filesystem: 你要上锁的那个index
lock: 指定的一个对这个index上全局锁的一个type
global: 上的全局锁对应的这个doc的id
_create:强制必须是创建,如果/filesystem/lock/global这个doc已经存在,那么创建失败,报错

此时另外一个线程尝试加锁

PUT /filesystem/lock/global/_create
{}

加锁报错。。。

当前线程可以执行各种操作

POST /filesystem/file/1/_update
{
  "doc": {
    "name": "README1.txt"
  }
}

然后释放锁

DELETE /fs/lock/global

(2)全局锁的优点和缺点

优点:操作非常简单,非常容易使用,成本低
缺点:对整个index加锁,对index中所有的doc的操作都会被block住,导致整个系统的并发能力很低。

 

6、基于document实现乐观锁

document锁,执行增删改的那些doc锁,其他线程就不能对这些doc执行增删改操作了,但是你只是锁了部分doc,其他线程对其他的doc还是可以上锁和执行增删改操作;

document锁使用脚本加锁

POST /fs/lock/1/_update
{
  "upsert": { "process_id": 123 },
  "script": "if ( ctx._source.process_id != process_id ) { assert false }; ctx.op = 'noop';"
  "params": {
    "process_id": 123
  }
}

/fs/lock,是固定的,就是说fs下的lock type,专门用于进行上锁
/fs/lock/id,比如1,id其实就是你要上锁的那个doc的id,代表了某个doc数据对应的lock(也是一个doc)
_update + upsert:执行upsert操作

params,里面有个process_id,process_id,是你的要执行增删改操作的进程的唯一id,比如说可以在java系统,启动的时候,给你的每个线程都用UUID自动生成一个thread id,你的系统进程启动的时候给整个进程也分配一个UUID。process_id + thread_id就代表了某一个进程下的某个线程的唯一标识。可以自己用UUID生成一个唯一ID;

加锁的过程:

(1)、在scripts/文件夹下创建judge-lock.groovy脚本,内容为

 if ( ctx._source.process_id != process_id ) { assert false }; ctx.op = 'noop';


(2)、es加锁

POST /fs/lock/1/_update
{
  "upsert": { "process_id": 123 },
  "script": {
    "lang": "groovy",
    "file": "judge-lock", 
    "params": {
      "process_id": 123
    }
  }
}

(3)、获取锁的情况

GET /fs/lock/1

{
  "_index": "fs",
  "_type": "lock",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "process_id": 123
  }
}

(4)、另一个线程加锁,加锁失败

POST /fs/lock/1/_update
{
  "upsert": { "process_id": 234 },
  "script": {
    "lang": "groovy",
    "file": "judge-lock", 
    "params": {
      "process_id": 234
    }
  }
}

{
  "error": {
    "root_cause": [
      {
        "type": "remote_transport_exception",
        "reason": "[4onsTYV][127.0.0.1:9300][indices:data/write/update[s]]"
      }
    ],
    "type": "illegal_argument_exception",
    "reason": "failed to execute script",
    "caused_by": {
      "type": "script_exception",
      "reason": "error evaluating judge-lock",
      "caused_by": {
        "type": "power_assertion_error",
        "reason": "assert false\n"
      },
      "script_stack": [],
      "script": "",
      "lang": "groovy"
    }
  },
  "status": 400
}

(5)、相同的线程加锁会成功

POST /fs/lock/1/_update
{
  "upsert": { "process_id": 123 },
  "script": {
    "lang": "groovy",
    "file": "judge-lock", 
    "params": {
      "process_id": 123
    }
  }
}

{
  "_index": "fs",
  "_type": "lock",
  "_id": "1",
  "_version": 1,
  "result": "noop",
  "_shards": {
    "total": 0,
    "successful": 0,
    "failed": 0
  }
}

(6)、获取对应线程对哪些文档加了锁

GET /fs/lock/_search?scroll=1m
{
  "query": {
    "term": {
      "process_id": 123
    }
  }
}

{
  "_scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAACPkFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAj5RY0b25zVFlWWlRqR3ZJajlfc3BXejJ3AAAAAAAAI-YWNG9uc1RZVlpUakd2SWo5X3NwV3oydwAAAAAAACPnFjRvbnNUWVZaVGpHdklqOV9zcFd6MncAAAAAAAAj6BY0b25zVFlWWlRqR3ZJajlfc3BXejJ3",
  "took": 51,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 1,
    "hits": [
      {
        "_index": "fs",
        "_type": "lock",
        "_id": "1",
        "_score": 1,
        "_source": {
          "process_id": 123
        }
      }
    ]
  }
}

(7)、释放锁

PUT /fs/lock/_bulk
{ "delete": { "_id": 1}}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值