ES 全文搜索与数据分析引擎

本文介绍了Elasticsearch作为全文搜索引擎的工作原理,与关系型数据库的对比,以及基本操作如连接、插入、查询等。还讨论了如何通过连接池管理、硬件配置、数据冷热分离和查询优化实现高性能检索。最后,提出了分布式查询中深分页性能问题及找出系统热数据的策略。
摘要由CSDN通过智能技术生成

ES

它是一个 分布式的 全文搜索与数据分析引擎。

与关系型数据库对比:
一个ES集群可以包含多个索引(数据库),每个索引又包含了很多类型(表),类型中包含了很多文档(行),每个文档使用 JSON 格式存储数据,包含了很多字段(列)

基本操作:

连接:

安装

pip install elasticsearch

连接


from elasticsearch import Elasticsearch

# 实例化
es = Elasticsearch([{"host": "ip", "port": 9200}])

插入:

创建数据库

es.indices.create(index="index_name", ignore=400)

创建数据库,并忽略400错误(数据库重复时,会返回400)

插入数据

body = {
	"name": "李李小明",
	"age": 7,
	"sex": "male"
}

es.index(index="es_test", doc_type='_doc', body=body)

插入多条

doc = [
	{"index": {'_index': "es_test", "_type": '_doc', '_id': 1}},
	{"name": "王王大志", "age": 8, "sex": "male"},
	{"index": {'_index': "es_test", "_type": '_doc', '_id': 2}},
	{"name": "刘刘大彪", "age": 8, "sex": "male"},
	{"index": {'_index': "es_test", "_type": '_doc', '_id': 3}},
	{"name": "赵赵灵儿", "age": 7, "sex": "male"},
]

es.bulk(index='es_test', doc_type='_doc', body=doc)

查询:

基本命令


# 返回字段
filter_path = [
	'hits.hits._source.ziduan1'
]

# 查询内容 过滤条件	
body = {
	"from"0,
	"size": 10
}

		
es.search(index='es_python', filter_path=filter_path, body=body)

from, size 类似于 limit 0, 10

各种查询方式:
body 用法

模糊查询:match

body = {
	"query": {
		"match": {
			"name": "王"
		}
	},
	"size": 10
}

模糊匹配 字段 name 中有王的 文档。

精确查询: term

body = {
	"query": {
		"term": {
			"name.keyword": "刘刘大彪"
		}
	},
	"size": 10
}

精确多值查询:terms

body = {
	"query": {
		"terms": {
			"name.keyword": ["刘刘大彪", "赵赵灵儿" ]
		}
	},
	"size": 10
}

注意是或的关系

模糊多字段查询:multi_match

body = {
	"query": {
		"multi_match": {
			"query": "大"
			"fields":["name", "info"] # 指定字段
		}
	},
	"size": 10
}

前缀查询:prefix

body = {
	"query": {
		"prefix": {
			"name.keyword": "王"
		}
	},
	size: 20
}

其他类似
通配符查询:wildcard
正则查询:regexp

布尔关系: bool

  • must 相当于and
  • should 相当于 or
  • *_not 取反,
  • 支持嵌套
body = {
	"query": {
		"bool": {
			"must": [
			
				{"term": xxxxxxxx},
				{"prefix": xxxx},
				
				{
					"bool": {
						"should": [
							{"term": xxxxxx},
							{"term": xxxxxx}
						]
					}
				}
			
			]
		}
	}
}

连接池

import os
import json
from datetime import datetime
from elasticsearch import Elasticsearch, RequestsHttpConnection
from elasticsearch import Transport
from elasticsearch.exceptions import NotFoundError

pool = Transport(
				 hosts=hosts, 
				 connection_class=RequestsHttpConnection
		).connection_pool

conn = pool.get_connection()

一个工具类: 写的不错,直接就复制过来了

小建议: 对于查询直接写成对应函数,并提供连接池管理。

import os
import json
from datetime import datetime
from elasticsearch import Elasticsearch, RequestsHttpConnection
from elasticsearch import Transport
from elasticsearch.exceptions import NotFoundError
 
 
class ES(object):
 
    _index = ""
    _type = ""
    
    def __init__(self, hosts):
        # 基于requests实例化es连接池
        self.conn_pool = Transport(hosts=hosts, connection_class=RequestsHttpConnection).connection_pool
 
    def get_conn(self):
        """
        从连接池获取一个连接
        """
        conn = self.conn_pool.get_connection()
        return conn
 
    def request(self, method, url, headers=None, params=None, body=None):
        """
        想es服务器发送一个求情
        @method     请求方式
        @url        请求的绝对url  不包括域名
        @headers    请求头信息
        @params     请求的参数:dict
        @body       请求体:json对象(headers默认Content-Type为application/json)
        # return    返回体:python内置数据结构
        """
        conn = self.get_conn()
        try:
            status, headers, body = conn.perform_request(method, url, headers=headers, params=params, body=body)
        except NotFoundError as e:
            return None
        if method == "HEAD":
            return status
        return json.loads(body)
 
    def search(self, query=None, method="GET"):
        url = "/%s/%s/_search" % (self._index, self._type)
        if method == "GET":
            data = self.get(url, params=query)
        elif method == "POST":
            data = self.post(url, body=query)
        else:
            return None
        return data
 
    def get(self, url, params=None, method="GET"):
        """
        使用get请求访问es服务器
        """
        data = self.request(method, url, params=params)
        return data
 
    def put(self, url, body=None, method="PUT"):
        """
        使用put请求访问es
        """
        data = self.request(method, url, body=body)
        return data
 
    def post(self, url, body=None, method="POST"):
        """使用post请求访问服务器"""
        data = self.request(method, url, body=body)
        return data
 
    def head(self, url, *args, **kwargs):
        status = self.request("HEAD", url, *args, **kwargs)
        return status
 
    def delete(self, url, *args, **kwargs):
        ret = self.request("DELETE", url, *args, **kwargs)
        return ret

进阶

es 做到数十亿数据查询毫秒级响应

往es中写文件 最终是保存在硬盘上, 查询的时候 是从硬盘上读数据。然而:

操作系统会把从硬盘上读出的数据放到filesystem cache 中, 下次读取的时候直接从缓存中取数据, 从内存中取数据明显要快于从硬盘读取,性能会得到极大的提升。

所以在设计(调优)时:

首先:硬件方面
一般至少确保60% - 80% 的数据能够缓存到 系统缓存中, 这样整个系统性能才能有保证。 并且尽量保证运行es 的机器上 不运行其他程序,避免cpu 与内存的争用。

其次: 设计方面
冷热分离,在某些情况下, 数据确实很多,不能将大部分数据放大系统缓存。根据业务场景,一般热数据占比是比较少的, 也就是大量数据中,经常访问的数据量不多,但这批数据访问频次很高。所以只要保证将这批热数据放到系统缓存中,就能保证系统中大部分用户的使用体验。
因此,可以将es中冷热数据分离,给他们分配单独机器,并适当调大内存,保证热数据访问效率。 对于剩下的大批冷数据可以 放到另外机器中,虽然查询会多花费一些时间,但这些是一小批用户。 对于系统整体来说,在设备条件有限的情况下 能最大程度使用硬件。

再者:编码方面
文档设计方面,在机器性能有限的情况下, 应避免存放与查询无关的内容,尽可能减小单个文档大小,增大缓存文档数量。
例如: 商品搜索中,只存放 id title keyword, 每次查询,根据条件查询出商品id ,然后根据id 从其他数据库中查询出详细信息。

另外为提升用户体验,可以进行数据预热,始终保证大部分热数据存在于系统缓存中。

分页性能优化,使用scroll , 一页一页 返回, 并对分页深度进行限制。

另外,集群条件下,提高系统查询效率,可以多添加一些副本,分担系统压力。


问题: 分布式查询数据,较深的分页 为什么性能很差

问题: 怎么找出系统热数据


参考文章: xx 官方文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值