快速删除大量小文件
由于bash会展开例如 rm aa/* 这样的命令 如果后面的文件太多就会报参数太长, 所以有时候删除大量小文件就不适合用rm了
可以使用find先查找在删除 就不会出现上面那种报错问题, 可是还有一个问题文件太多的话 -exec rm {} ; 完全没有效率, 一两个小时估计也就只能删除几十万的文件
对于需要删除百万为单位的文件数来说 这就等于蜗牛爬
搜索后发现 可以使用另一个find的参数 -delete 网上有人开玩笑说这个比-exec 快1000倍
虽然这个有些夸张了 但是快10-100倍还是有的 删除200W的文件 大概需要40几分钟
网上还有一个方案, (个人觉得不太适合)
建立一个空目录, 利用rsync同步这个空目录到需要删除的目录, 这样就可以删除掉目录下的文件, 感觉这样的操作和直接rm -rf /path 效果差不多
只是后者把目录删了, 前者是否会改变文件存储的inode号待测试.
这里的需求主要是只删除文件夹内部分文件, 而不是清空文件夹/删除文件夹
推荐的命令 find /path/to/delete -type f -name “test-log*” -mtime +10 -delete
先推荐一本书: <<unix痛恨者手册>>
这本书内容不多 几个小时就可以看完, 里面介绍的有些内容现在已经处理过了, 还是可以推荐看下
另外一本 大教堂与小集市 也可以推荐看下
screen&tmux快捷键
screen
Key 快捷键 | Action 动作 | Notes 备注 |
Ctrl+a c | new window 新窗口 | |
Ctrl+a n | next window 下一个窗口 | I bind F12 to this |
Ctrl+a p | previous window 上一个窗口 | I bind F11 to this |
Ctrl+a " | select window from list | I have window list in the status line |
Ctrl+a Ctrl+a | previous window viewed | |
Ctrl+a S | split terminal horizontally into regions | Ctrl+a c to create new window there |
Ctrl+a | | split terminal vertically into regions | Requires screen >= 4.1 |
Ctrl+a :resize | resize region | |
Ctrl+a :fit | fit screen size to new terminal size | Ctrl+a F is the same. Do after resizing xterm |
Ctrl+a :remove | remove region | Ctrl+a X is the same |
Ctrl+a tab | Move to next region | |
Ctrl+a d | detach screen from terminal | Start screen with -r option to reattach |
Ctrl+a A | set window title | |
Ctrl+a x | lock session | Enter user password to unlock |
Ctrl+a [ | enter scrollback/copy mode | Enter to start and end copy region. Ctrl+a ] to leave this mode |
Ctrl+a ] | paste buffer | Supports pasting between windows |
Ctrl+a > | write paste buffer to file | useful for copying between screens |
Ctrl+a < | read paste buffer from file | useful for pasting between screens |
Ctrl+a ? | show key bindings/command names | Note unbound commands only in man page |
Ctrl+a : | goto screen command prompt | up shows last command entered |
tmux
Ctrl+b | 激活控制台;此时以下按键生效 | |
系统操作 | ? | 列出所有快捷键;按q返回 |
d | 脱离当前会话;这样可以暂时返回Shell界面,输入tmux attach能够重新进入之前的会话 | |
D | 选择要脱离的会话;在同时开启了多个会话时使用 | |
Ctrl+z | 挂起当前会话 | |
r | 强制重绘未脱离的会话 | |
s | 选择并切换会话;在同时开启了多个会话时使用 | |
: | 进入命令行模式;此时可以输入支持的命令,例如kill-server可以关闭服务器 | |
[ | 进入复制模式;此时的操作与vi/emacs相同,按q/Esc退出 | |
~ | 列出提示信息缓存;其中包含了之前tmux返回的各种提示信息 | |
窗口操作 | c | 创建新窗口 |
& | 关闭当前窗口 | |
数字键 | 切换至指定窗口 | |
p | 切换至上一窗口 | |
n | 切换至下一窗口 | |
l | 在前后两个窗口间互相切换 | |
w | 通过窗口列表切换窗口 | |
, | 重命名当前窗口;这样便于识别 | |
. | 修改当前窗口编号;相当于窗口重新排序 | |
f | 在所有窗口中查找指定文本 | |
面板操作 | ” | 将当前面板平分为上下两块 |
% | 将当前面板平分为左右两块 | |
x | 关闭当前面板 | |
! | 将当前面板置于新窗口;即新建一个窗口,其中仅包含当前面板 | |
Ctrl+方向键 | 以1个单元格为单位移动边缘以调整当前面板大小 | |
Alt+方向键 | 以5个单元格为单位移动边缘以调整当前面板大小 | |
Space | 在预置的面板布局中循环切换;依次包括even-horizontal、even-vertical、main-horizontal、main-vertical、tiled | |
q | 显示面板编号 | |
o | 在当前窗口中选择下一面板 | |
方向键 | 移动光标以选择面板 | |
{ | 向前置换当前面板 | |
} | 向后置换当前面板 | |
Alt+o | 逆时针旋转当前窗口的面板 | |
Ctrl+o |
顺时针旋转当前窗口的面板 |
通过elasticsearch对日志进行搜索热词统计
通过logstash搜集日志
这里搜集日志可以使用ELK的一个插件filebeat对日志进行处理,并传输到后端的程序
在这里有一个不好的地方, 如果想要直接使用filebeat将日志发送到elasticsearch的话, 它并不能对任何字段进行替换等处理
比较明显的问题就是, 一般我们需要将@timestamp替换成日志里面的时间而不是程序对日志的处理时间, 这一点它无法做到
还有一点, 使用filebeat对多行日志进行处理时似乎会发生日志收集错乱的现象, 这个问题有待测试, 因为filebeat程序是自带处理多行日志的
当然好处也是有点, 可以比较省资源
1 | input { |
上面是一个logstash的配置文件,处理的日志格式大概是这样的
1 | [ERROR 2017-05-04 10:12:24,281 ./connect_info.py:336 @ 8299] - socket send and recieve Error: Traceback (most recent call last): |
这里分为三个段落
input段:
采用文件的形式, path可以采用*来匹配任意字符(匹配单个字符待测试),
add_field 可以增加字段, 可以很好的区分开日志
codec => multiline 采用多行的模式 如果不是以[
开头的将后面的行算作第一行
filter段:
这里采用的是 grok 匹配前面的无规则(非json格式)内容, 其后的json格式内容统一存到 result 字段, 并移除message字段
再通过 if 判断, 提取需要处理的日志 使用 mutate 对日志进行切分, 标准的json格式日志将保存在 field2 字段 之后通过 json 进行格式化该字段
最好将格式化好的字段中的时间 替换默认的 @timestamp 字段
output字段:
elasticsearch 将日志输出到elasticsearch 中
stdout 将日志输出到屏幕终端
通过elasticsearch对日志进行检索
先通过results.req.searchFunc字段过滤出包含 searchmusic的内容, 再判断 results.response.data.total 是否大于 1 排除搜索无结果的内容
最后使用 aggregations 对 results.req.text.keyword 字段结果进行聚合 统计出该字段的每个内容的个数, size控制显示多少个内容
aggregations 上面的size控制不显示其他搜索内容, 只关注aggregations 统计结果
1 | GET /logstash-name-2017.06*/_search |
登陆获取shell时的配置文件加载过程
最近遇到一台ubuntu服务器登陆时默认语言环境变量变成posix问题, 导致中文显示乱码,影响程序的正常运行
1 | # locale |
按照常规的分析,应该是开机没有加载某个配置文件
在ubuntu系统下,语言变量一般是在 /etc/default/locale
中设置的,安装系统时选择的语言最终也会写入到这里
查看这个值发现并没有问题
1 | cat /etc/default/locale |
那么另一个可以设置这个变量的就是 /etc/environment
了
在测试时,系统这个文件上并没有写入 LANG的配置,其实这也是合理的
现在最主要的问题是ssh登陆时没有加载到配置文件 /etc/default/locale
找了一会问题,还没完全解决,这时告诉同事等会再来排查问题。突然同时说问题已经解决了,
遂激动的询问是怎样解决的。
直接在 /etc/profile
里面加上 export LANG=en.US-UTF-8
尴尬了。。。 这么简单的事。。一直想着查找为什么无法加载locale文件,忘记了最简单的解决办法。下次切记~~
登陆流程获取shell,肯定会加载profile文件嘛,还有在用户家目录里面设置环境变量也是可以的
当然,上面虽然可以正常解决问题,可是具体原因并没有对症下药
后来对比了login获取shell时的环境变量 和su时获取的环境变量, 发现这两个都是正常的
查看了ssh的配置文件 /etc/ssh/sshd_config
, 其中有一句
1 | # Allow client to pass locale environment variables |
按照注释看,这应该是加载本地的环境变量, 所以我把当前shell的语言变量设置成其他的,用ssh到其他电脑,果然环境变量会随着本地机器的设定而变化,然后再用login及su登陆系统, 奇怪的是系通正常设定了语言(这才应该是正常的情况)
问题到这,已经确定是ssh登陆时会有问题,而login和su登陆没有问题
简单的联想到是不是PAM模块未加载这个配置文件,一番对比,发现 /etc/pam.d/
下面的 login su sshd 并没有太大的区别 都有一句加载locale变量
1 | # Read environment variables from /etc/environment and |
按理说,这应该是没问题的。。。
往往,答案就隐藏在某个看不见的最安全的也是最容易发现地方,只是可惜被我忽略了
在sshd_config配置文件的最后有一个配置
1 | UsePAM no |
都已经设置成不加载PAM模块了,之前设定的语言变量肯定也不会被加载了嘛, 更改成yes后,登陆正常
再来看下曾今答案离我多近
1 | ClientAliveInterval 3600 |
也就是在设定加载本地语言环境变量的下面。。。。
好了,现在粗略总结下系统登陆时获取shell的加载过程。排除登陆问题可以按照这些流程一一排查可能出错的地方
开机–>init程序–>获取登陆shell–>加载PAM模块–>加载/etc/profile文件–>加载用户家目录配置文件
网络的设置是在init程序的时候由/etc/init.d内文件指定,开机时会按顺序加载,作为开机自启程序
PAM模块 会加载一些环境变量
/etc/profile文件会调用一些其他设置,包括/etc/profile.d/*.conf 文件
用户家目录配置文件可以设定一些自定义变量, 比如可以设置语言为 zh_CN.UTF-8 (系统环境最好保持en_US.UTF-8)
linux共享库加载
参考自: <<程序员的自我修养–链接、装载与库>> 第八章 Linux共享库的组织
以下截取部分内容 (这本书比较好的讲解了从程序的链接,装载,到运行)
共享库的兼容性
linux下的共享库就是普通的ELF共享对象
更改类型 | 兼容性 |
---|---|
往共享库 Hbfoo.so里面添加一个导出符号 foo2 | 兼容 |
删除共享库libfoo.so里面一个原有的导出符号 foo | 不兼容 |
将libfoo.so给一个导出函数加一个参数,比如原来的foo(int a)变成了foo(int a,int b) | 不兼容 |
删除一个导出函数中的一个参数,如原来的foo(int a, int b)变成了 foo(int a) | 不兼容 |
如果一个结构类型被用于一个导出函数或导出全局变量,那么改变结构类型的长度、 内容、成员类型,如libfoo.so有导出函数foo(struct bar b),而bar的结构被改变 | 不兼容 |
修正一个导出函数中的bug, 或者改进某个导出函數的性能,但是不改变导出函数的语义、功能,行为和接口类型 | 兼容 |
修正一个导出函数中的bug, 或者改进某个导出函数的性能, 但是同时改变了导出函数的语义、功能、行为或接口类型 | 不兼容 |
共享库的版本命名
lib 主版本号 次版本号 发布版本号 .so
共享库中的符号版本
GLIBC_2.0 GLIBC_2.6
共享库系统路径
/lib
/usr/lib
/usr/local/lib
共享库查找过程
- 由 LD_LIBRARY_PATH 指定
- 由/etc/ld.so.cache 指定
- 默认共享库 先/usr/lib 然后/lib
环境变量及共享库的创建
LD_LIBRARY_PATH
LD_PRELOAD
LD_DEBUG
ldconfig -n sharede_library_directory
linux安全相关
2017-05-11突然谈到linux安全相关的话题,记录一下
搜了一下,找到一篇介绍apparmor和selinux的文章
http://www.361way.com/apparmor-selinux/3648.html
节选一段
AppArmor 最初由 Immunix 开发,随后由 Novell 维护,它是 SELinux 的替代方法,也使用了 Linux 安全模块(LSM)框架。由于 SELinux 和 AppArmor 使用了同样的框架,所以它们可以互换。AppArmor 的开发初衷是因为人们认为 SELinux 太过复杂,不适合普通用户管理。AppArmor 包含一个完全可配置的 MAC 模型和一个学习模式。SELinux 的一个问题在于,它需要一个支持扩展属性的文件系统;而 AppArmor 对文件系统没有任何要求。您可以在 SUSE、OpenSUSE,以及 Ubuntu 中找到 AppArmor。 同样,在redhat、centos、fedora上,也会找到SElinux 。
及时打补丁也是安全防范的重要步骤,一般涉及内核补丁都需要重启机器生效,从linux kernel 4.4开始引入热加载补丁了
看当时的文章介绍好像想要真正实现也有很多限制。 最近看某群里面讨论,似乎ubuntu 16.04开始可以使用热加载内核补丁,而且挺方便的
todo: 查找下文章
PIC/PIE分析
PaX
Grsecurity’s RBAC or SELinux
另外听说有人完全按照Hardened Gentoo来搭建自己的VPS,也是佩服啊(关键人家还是刚成年)
ELK常用API使用方法
以下ELK系列文章参考自http://www.tianyiqingci.com/
总目录:
Monitor API
ElasticSearch聚合分析API
Elasticsearch信息检索API
ElasticSearch索引管理API
附录
Monitor API
Cluster health
查看集群健康状态接口。
http://localhost:9200/_cluster/health?pretty
1 | { |
Status:集群的健康状态
- Green:健康。所有分片(shards)与所有副本(replicas)都可用
- Yellow:亚健康。表示所有的分片可用(集群可以正常处理任何请求),但是副本不完全可用
- Red:集群不可用。有一或多个分片不可用。
number_of_nodes:总节点数
number_of_data_nodes:总数据节点数
active_primary_shards:分片数
active_shards:分片/副本总数
relocating_shards:正在迁移中的分片数
initializing_shards:正在初始化的分片数
unassigned_shards:未分配到节点上的分片数
number_of_pending_tasks:挂起的任务数
active_shards_percent_as_number:可用分片百分比
cluster stats
查看集群各种统计指标数据的接口。
http://localhost:9200/_cluster/stats?human&pretty
1 | { |
Pending tasks
查看集群挂起的任务的接口。
http://localhost:9200/_cluster/pending_tasks?human&pretty
1 | { |
Nodes stats
查看集群中各节点的统计指标数据的接口。
http://localhost:9200/_nodes/stats?pretty
1 | { |
会将集群下所有node统计指标全列出来
ElasticSearch聚合分析API
ES高级功能API – 聚合(Aggregations),聚合功能为ES注入了统计分析的血统,使用户在面对大数据提取统计指标时变得游刃有余。同样的工作,你在hadoop中可能需要写mapreduce或hive,在mongo中你必须得用大段的mapreduce脚本,而在ES中仅仅调用一个API就能实现了。
关于Aggregations
Aggregations的部分特性类似于SQL语言中的group by,avg,sum等函数。但Aggregations API还提供了更加复杂的统计分析接口。
掌握Aggregations需要理解两个概念:
- 桶(Buckets):符合条件的文档的集合,相当于SQL中的group by。比如,在users表中,按“地区”聚合,一个人将被分到北京桶或上海桶或其他桶里;按“性别”聚合,一个人将被分到男桶或女桶
- 指标(Metrics):基于Buckets的基础上进行统计分析,相当于SQL中的count,avg,sum等。比如,按“地区”聚合,计算每个地区的人数,平均年龄等
对照一条SQL来加深我们的理解:
1 | SELECT COUNT(color) FROM table GROUP BY color |
GROUP BY相当于做分桶的工作,COUNT是统计指标。
下面介绍一些常用的Aggregations API。
Metrics
AVG
求均值。
1 | GET /company/employee/_search |
执行结果
1 | { |
其他的简单统计API,如valuecount, max,min,sum作用与SQL中类似,就不一一解释了。
Cardinality
cardinality的作用是先执行类似SQL中的distinct操作,然后再统计排重后集合长度。得到的结果是一个近似值,因为考虑到在大量分片中排重的性能损耗Cardinality算法并不会load所有的数据。
1 | { |
Stats
返回聚合分析后所有有关stat的指标。具体哪些是stat指标是ES定义的,共有5项。
1 | { |
执行结果
1 | { |
Extended Stats
返回聚合分析后所有指标,比Stats多三个统计结果:平方和、方差、标准差
1 | { |
执行结果
1 | { |
Percentiles
百分位法统计,举例,运维人员记录了每次启动系统所需要的时间,或者,网站记录了每次用户访问的页面加载时间,然后对这些时间数据进行百分位法统计。我们在测试报告中经常会看到类似的统计数据
1 | { |
结果是
1 | { |
加载时间在15ms内的占1%,20ms内的占5%,等等。
我们还可以指定百分位的指标,比如只想统计95%、99%、99.9%的加载时间
1 | { |
Percentile Ranks
Percentile API中,返回结果values中的key是固定的0-100间的值,而Percentile Ranks返回值中的value才是固定的,同样也是0到100。例如,我想知道加载时间是15ms与30ms的数据,在所有记录中处于什么水平,以这种方式反映数据在集合中的排名情况。
1 | { |
执行结果
1 | { |
Bucket
Filter
先过滤后聚合,类似SQL中的where,也有点象group by后加having。比如
1 | { |
只统计红色衣服的均价。
Range
反映数据的分布情况,比如我想知道小于50,50到100,大于100的数据的个数。
1 | { |
执行结果
1 | { |
Missing
我们想找出price字段值为空的文档的个数。
1 | { |
执行结果
1 | { |
Terms
针对某个字段排重后统计个数。
1 | { |
执行结果
1 | { |
Date Range
针对日期型数据做分布统计。
1 | { |
这里的format参数是指定返回值的日期格式。
执行结果
1 | { |
Global Aggregation
指定聚合的作用域与查询的作用域没有关联。因此返回结果中query命中的文档,与聚合的的统计结果是没有关系的。
1 | { |
Histogram
跟range类似,不过Histogram不需要你指定统计区间,只需要提供一个间隔区间的值。好象不太好理解,看个例子就全明白了。
比如,以50元为一个区间,统计每个区间内的价格分布
1 | { |
执行结果
1 | { |
由于最高的价格没超过200元,因此最后的结果自动分为小于50,50到100,100到150,大于150共四个区间的值。
100到150区间的文档数为0个,我们想在返回结果中自动过滤该值,或者过滤偏小的值,可以添加一个参数”min_doc_count”,比如
1 | { |
返回结果会自动将你设定的值以下的统计结果过滤出去。
Date Histogram
使用方法与Histogram类似,只是聚合的间隔区间是针对时间类型的字段。
1 | { |
执行结果
1 | { |
IPv4 range
由于ES是一个企业级的搜索和分析的解决方案,在做大量数据统计分析时比如用户访问行为数据,会采集用户的IP地址,类似这样的数据(还有地理位置数据等),ES也提供了最直接的统计接口。
1 | { |
执行结果
1 | { |
Return only aggregation results
在统计分析时我们有时候并不需要知道命中了哪些文档,只需要将统计的结果返回给我们。因此我们可以在request body中添加配置参数size。
1 | curl -XGET 'http://localhost:9200/twitter/tweet/_search' -d '{ |
聚合缓存
ES中经常使用到的聚合结果集可以被缓存起来,以便更快速的系统响应。这些缓存的结果集和你掠过缓存直接查询的结果是一样的。因为,第一次聚合的条件与结果缓存起来后,ES会判断你后续使用的聚合条件,如果聚合条件不变,并且检索的数据块未增更新,ES会自动返回缓存的结果。
注意聚合结果的缓存只针对size=0的请求(参考3.10章节),还有在聚合请求中使用了动态参数的比如Date Range中的now(参考3.5章节),ES同样不会缓存结果,因为聚合条件是动态的,即使缓存了结果也没用了。
Elasticsearch信息检索API
前言
想要更好的理解ES不应当把它的存储功能与搜索功能割裂开学习,只有正确的索引文档才能使之被正确的检索。
Elasticsearch真正强大之处在于可以从混乱的数据中找出有意义的信息。
ES与其他nosql数据库的差别之一,ES接收非结构化数据但数据本身必须是结构化的JSON文档,不能保存无结构的二进制数据。这是由ES搜索引擎的基因决定的。
ES搜索中你要知道的
倒排索引(Reverted Index)
ES使用倒排索引结构来做快速的全文搜索。倒排索引由在文档中出现的唯一的单词列表,以及对于每个单词在文档中的位置组成。
从网上扒了个很好的例子,假设我们有三句话:
1 | T[0] = "it is what it is" |
常规索引是指通过位置找到相应的单词,比如:T[0]的第一个单词是it,可以记为 (0,0) : “it”,再如 (2,1) : “is”。
倒排索引则是反过来,通过单词获取位置,比如:”it” 这个单词出现的位置有 (0,0) (0,3) (1,2) (2,0),这样可以记为 “it” :{(0,0) (0,3) (1,2) (2,0)}。通过对上述三句话建立倒排索引可以得到:
1 | "a" : {(2,2)} |
通过构建好的倒排索引,使得我们可以很方便的实现对语句的检索,比如: 需要检索包含”what” “is” “it”三个单词的语句,忽略倒排表中的第二位(单词在每句中的位置),可以得到 {0 1}∩{ 0 1 2}∩{0 1 2} = {0 1},因此我们断定T[0]和T[1]满足条件。在检索 “what is it”这个词组的时候还需要考虑到单词的具体位置,因此我们只能够获取到 T[1] 满足条件{(1,0) (1,1) (1,2)}。
上面的分析可以告诉我们,单词或语句的检索在构建好倒排索引之后可以转化成一个集合求解的问题,而不用逐行逐字的扫描,这使得检索效率得到大大地提高,这也就是为什么倒排索引在搜索领域如此重要的原因。
分析与分词(Analyzer)
将一段文本转换为一组唯一的标准化的标记(token),以便创建倒排索引,然后查询倒排索引。
Analyzer的工作分三部分:
- 字符过滤器(Character filters):分词前整理字符串,做一些类似去掉HTML标签. 把&符号转换成单词 And 之类的操作;
- 分词解析器(Tokenizer):分词解析器将会把一个字符串拆分成单独的terms。 一个简单的分词器可能会使用空白和标点符号(punctuation), 来进行分词解析;
- 解析过滤器(Token filters):进一步进行terms 整理。变成小写,删除介词,语义连接词: ‘A, and , the And,添加同义词
例如,
1 | The quick brown fox jumped over <b>the lazy dog</b> |
字符过滤:The quick brown fox jumped over the lazy dog
解析:[the, quick, brown, fox, jump, over, the, lazy, dog]
解析过滤器:[quick, fast, brown, fox, foxes, foxing, foxed, jump, jumps, leap, lazy, dog, dogs]
实践中需要注意的是,查询时只能查找在倒排索引中出现的词,所以确保在文档索引的时候以及字符串查询的时候使用同一个分析器。
相似性算法(Similarity algorithm)
默认情况下,ES查询结果是按相关性倒序返回的。查询结果中有一项_score值,存储一个浮点型的数字,就是相关性评分。
在ES中应用相似性算法较典型的有:
- Fuzzy query(模糊匹配)是看与原始检索term的相似程度,允许查询时匹配拼写错误的单词, 音标表过滤器能在索引时用于发音相似的匹配;
- Terms query(词条匹配)是看找到的terms 百分比值
相似性算法包含:
- Inverse document frequency:在当前index库中出现的越多,相关性越低
- Term frequency/inverse document frequency(TF/IDF):一种用于资讯检索与资讯探勘的常用加权技术。如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
- Term frequency:在一个field中出现越频繁得分越高
- Field-length norm:匹配百分比越高得分越高。如,中华人民共和国 < 人民共和国 < 人民。
一个独立的查询中,可能是一个term proximity(近似度,如“人民”与“人民共和国”) 和 term similarity(相似度,如“人民”与“人们”) 的结合体。
在ES查询中你附加yes/no 从句越多(bool 子句),_score数值越高。
简易检索(Query String)
把参数直接附加在URL后面进行信息的检索。适合用作curl 测试。
1 | # 查指定index,字段user为kimchy的 |
结构化检索(Query DSL)
为方便理解,我会把结构化过滤(Filter DSL)也穿插在这个章节中进行介绍。
简易检索是一种非常有效的命令行查询。但是,如果你想要善用搜索,你必须使用结构化检索查询(request body search)API。
DSL,面向领域专用语言,近似人类语言的查询语言,让不熟悉编程的人也能使用查询功能。使用结构化查询,你需要传递query参数来构成一个查询子句,例如
1 | GET _search { |
我们可以看到,request body是由一条条简单的查询子句合并而成。合并的方式有两种:叶子子句与复合子句。
request body还可以添加查询语句和过滤语句,他们可以放在各自的上下文中。上面的例子是一条带过滤的查询语句,你也可以照着样子写一个带查询的过滤语句。
下面我们再来认识一下其他一些重要的搜索API。
Match All
空查询,即匹配所有的文档
1 | # 匹配全部 |
Term/terms
精确过滤/包含。term只在倒排查询里精确地查找特定短语,而不会匹配短语的其它变形,如Kitty或KITTY。这里的过滤不是指等于。
执行下面的命令
1 | { |
返回文档中有可能会包含
1 | { “user” : [“kitty”, hello”] } |
因为term 过滤器的工作原理是检查倒排索引中所有具有短语的文档。而上面的文档在倒倒索引中与kitty建立了关联,因此它可以作为结果被返回。
1 | GET /school/students/_search |
这里要注意,如果你期望用term精确过滤字符型字段时,需要在mapping时将该字段置为not_analyzed。否则会出现下面这种情况,文档
1 | { “user” : “hello.kitty” } |
在执行
1 | { |
后无法返回结果,原因是在索引过程中分析器自动将hello.kitty分成了hello与kitty两个term,而且中间的分隔符.号被过滤掉了。
Range
范围过滤
1 | {"range" : |
对应逻辑运算符有:
- gte 大于等于
- gt 大于
- lte 小于等于
- lt 小于
注意,range过滤也对字符串有效,但相比数字或日期的效率要慢得多,比如,
1 | {"range" : |
因为ES会在从g到r范围内的每个短语执行term过滤。
Exists
过滤是否包含指定字段的文档
1 | { "exists" : |
以下都是满足条件的:
1 | { “user”: “jane” } |
Prefix and Wildcard
前缀与通配符查询,效果相当于SQL脚本中的like。
1 | { "regexp": |
星号代表多个字符序列,问号代表单个字符。
两个查询器性能都很差。ES中不允许使用?*开始的通配符(如,?itty)。
Regexp
正则查询。跟通配符查询功能类似,但是正则查询支持更灵活定制细化查询,这一点与通配符的泛化是不一样的,而且正则查询天生支持使用强大的正则表达式的来准确匹配一个或几个term。
1 | { "regexp": |
使用尽量精确的正则来匹配terms,避免过重的迭代扫描。
需要注意的是,使用正则查询的字段最好是不分词的,因为分词的字段可能会导致边界问题,从而使查询失败,得不到任何结果,这一点和Wildcard效果是一样的。
Fuzzy
模糊查询。在相似性算法一节中有点到过,如果在输入关键字时你的单词拼写有问题或打了一个同音字,却搜不出结果来,这个搜索引擎的体验会非常差。搜索引擎就应该把可能的结果,通过相关性分数将匹配结果呈现给我们。
1 | { |
其中,fuzziness默认值为auto,没事别改它;prefix_length代表从第N位开始匹配。
IDS
这里的IDs就是文档在倒排索引中的DocIDs,即根据文档ID精确检索。
1 | { "ids" : |
注意使用该查询器时需要指定index与type,除非你的集群里只有一组index与type。
Constant Score
得分过滤,设定相关性分值,使返回的文档相似度能满足用户要求。
1 | { "constant_score" : |
Bool
布尔过滤。由一个或者多个boolean类型的从句嵌套组成复合查询。
该过滤器的子句类型有:
- Must必须匹配;
- Must_not必须不匹配;
- Should至少有一个分句匹配,与OR 相同
Bool条件匹配的越多,该条记录的得分越高。但是, filter的匹配与否,对则结果集相似度分值不产生影响。
1 | # minimum_should_match:1,should条件列表中至少满足一项 |
Dis Max
dis_max(Disjuction Max,Disjuction是”OR”的意思)与bool过滤中的should有点象,就是返回匹配了任何从句的文档,但dis_max有个更高级的功能,就是tie_breaker,打破平局。
1 | { |
如果没有tie_breaker,那么返回两个文档
1 | {“name”:”kitty”, “age”:33} |
且两个文档得分一致,因为name与age只要满足一项就能匹配上,加上tie_breaker后,会将age的匹配度也算上。因此最终的得分,第二个文档会高于第一个。
Indices
同时查询多个index
1 | { |
From/Size
ES中的翻页
1 | { |
Sort
ES中的排序
1 | { |
Source
指定返回字段
1 | # 不返回字段 |
优化你的检索
先记住一个事,尽量多的使用过滤器。
假设你使用term去匹配user字段,过滤器的内部逻辑是:
- term 过滤器在倒排索引中进行匹配
- 为匹配过的文档创建一个由1和0构成的字节集,匹配的文档得到1字节,不匹配为0;
- 将这个字节集放入内存以备后续请求使用,后续请求将跳过步骤1和2。
执行查询时filter会早于query执行,这样内存中的字节集会传给query以跳过已经被排除的文档。过滤语句的作用就是为query缩小匹配范围。
而query语句不仅要查找匹配的文档,还要计算每个文档的相关性,所以一般来讲query语句要比过滤语句更耗时,并且查询结果不能缓存。
ElasticSearch索引管理API
前言
ES的可定制化做得非常丰富灵活,只要能熟悉它强大的API库,它能几乎满足你关于垂直搜索领域的所有特殊的需求。今天这篇文章将介绍ES索引管理与配置的API,内容来源自官网的Indices API(https://www.elastic.co/guide/en/elasticsearch/reference/2.2/indices.html),讲实话ES官网的文档写得不是很友好,给我感觉好象是有多位作者各自编辑且互相间没什么呼应很容易让人看蒙圈。讲方法库的文章又总是很枯燥的,但Indices API在ES众多API库中起核心支配的作用,所以我把它放在API的第一篇,在翻译文档的同时我会将自己的理解尽量用大白话讲出来。
Elasticsearch官方API遵循restful标准,支持接口有get,post,put,delete,head等等。Get是请求指定的页面信息,并返回实体主体;Post是请求服务器接受所指定的文档更新所标识的URI;Put向服务器传送的数据取代指定的文档的内容;Delete是请求服务器删除指定的页面;Head只请求页面的首部。
为了偷懒,文章中会直接复制很多官网的源码。而官方文档中在贴出大块代码时经常会采用简写的样式,会让初学者很难理解,我先举个例子,下面是一个完整的请求:
1 | curl -XGET 'localhost:9200/_count?pretty' -d ' |
简写curl请求:
1 | GET /_count |
Index索引管理
创建Index
使用默认设置创建索引
1 | curl -XPUT 'http://localhost:9200/twitter/' |
默认shards数量为5
默认replicas数量为1
确保索引被创建在适当数量的分片上以及指定若干数量的副本
1 | curl -XPUT 'http://localhost:9200/twitter/' -d ' |
在创建索引后,shards的数量不可修改,replicas的数量可以修改
删除Index
1 | curl -XDELETE 'http://localhost:9200/twitter/' |
删除多个
1 | curl -XDELETE 'http://localhost:9200/index_one,index_two/' |
获取Index信息
根据指定index的名称或别名查询
1 | curl -XGET 'http://localhost:9200/twitter/' |
根据指定index的名称或别名返回指定的数据
1 | curl -XGET 'http://localhost:9200/twitter/_settings,_mappings ,_warmers,_aliase' |
指定返回的数据包括:
_settings当前库的配置信息(分片shards或副本replicas等);_mappings当前库的结构;_warmers用于查询优化的加热器;_aliase别名。
查询Indices是否存在
1 | curl -XHEAD -i 'http://localhost:9200/twitter' |
返回404表示不存在,200表示存在。
开启/关闭Index
1 | curl -XPOST 'localhost:9200/my_index/_close' |
Index被关闭后不能做增删改查操作。
Mapping管理
Mapping主要用于描述ES的库表结构,mapping不仅告诉ES一个type中有哪些field,还描述field是什么类型,它还告诉ES如何索引数据以及数据是否能被搜索到。
Lucene中没有mapping,你可以将mapping理解为一个JSON文档到Lucene扁平化数据的映射器。
很多初学者会忽视mapping的作用,比如同一个index下两个type中都有createdate字段,一个存日期型“20160328”,一个是数值型20160328。在检索“20160328”时两种类型的文档都会被过滤出来,好象没定义mapping也可以啊。这其实是一个陷阱,当数值型的那个createdate值变成时间戳如1459129963726,这时候无论是过滤还是排序都有问题,Lucene是不关心字段是日期型还是数值型,它的排序规则是根据遇到的第一个createdate的类型决定的。
规避陷阱的办法就是真正的理解ES中type的含义,type的确是一张非结构化的数据表,但别把不相关的数据都扔到一个type中。原则上确保index下各个type中,同名的字段用相同的mapping,如果再谨慎一点,字段名称也请定义再明确再详细一些。
创建Mapping
可以通过创建mapping来创建index,或向已有index添加type,或向已有type添加field
创建一个名称为twitter的index,同时创建twitter下名称为tweet的type,以及在这个type下创建一个string类型的field名叫message。在一个请求中同时创建库表
1 | PUT twitter |
再往表tweet下添加一个user_name字段
1 | PUT twitter/_mapping/tweet |
在twitter下创建名为user的type
1 | PUT twitter/_mapping/user |
ES还支持同时对多个index进行mapping操作,指定的index名称用逗号,分隔
获取mapping信息
获取指定index信息,或index下指定type信息
1 | curl -XGET 'http://localhost:9200/_mapping/twitter' |
获取多个index或多个type信息
1 | curl -XGET 'http://localhost:9200/_mapping/twitter,kimchy' |
获取全库信息
1 | curl -XGET 'http://localhost:9200/_all/_mapping' |
获取field字段信息,假设有下面一个库表结构
1 | { |
查username字段信息
1 | curl -XGET 'http://localhost:9200/twitter/_mapping/tweet/field/username' |
查多个字段信息
1 | curl -XGET 'http://localhost:9200/twitter,kimchy/_mapping/field/username,age' |
查询type是否存在
1 | curl -XHEAD -i 'http://localhost:9200/twitter/tweet' |
返回404表示不存在,200表示存在。
Alias别名管理
同mapping一样,新手上路时很容易忽略aliases功能,老司机们对aliases的好是如饮甘饴。
先说一个重要的事,在实际应用中尽量用别名而不是索引原名。养成好习惯现在就把代码中的索引名换掉。下面说一个场景,你会意识到这事很重要:
你们线上最忙碌的一个index,叫它idx_AAA好了,有一天必须要改其中一个字段的mapping,直接改现有的idx_AAA数据?不行。复制AAA到idx_BBB上,然后将代码中配置中所有idx_AAA改为idx_BBB,然后走上线流程,发升级公告,重启搜索服务?小公司的老板可能会忍你,你看马云会不会。马老板拎刀去了,你才开始流泪太晚了。你如果一开始就给idx_AAA建个别名,你的应用上全都用别名,有天要改mapping,可以但别动idx_AAA,复制AAA到idx_BBB上改mapping,改完后,执行脚本:
1 | POST /_aliases |
无缝切换有没有,马总再也不用担心你的mapping了。
创建aliases关联
关联一个别名
1 | curl -XPOST 'http://localhost:9200/_aliases' -d ' |
可以一个index对应多个aliases,或一个aliases对应多个index
1 | curl -XPOST 'http://localhost:9200/_aliases' -d ' |
ES还提供了根据过滤器filter建立alias的功能,下面的例子,即先user=”kimchy”过滤出结果index,然后再为结果index创建aliases关联
1 | curl -XPOST 'http://localhost:9200/_aliases' -d '{ |
ES还可以将视图创建在指定的分片shard中,这样做的目的是为了减少检索时的寻片工作,在执行查询时指定检索的shard路由地址,将避免shard by shard的加载与过滤
1 | # 查与存都在同一个分片 |
删除aliases关联
1 | curl -XPOST 'http://localhost:9200/_aliases' -d ' |
可以在一个请求中同时做创建与删除操作
1 | curl -XPOST 'http://localhost:9200/_aliases' -d ' |
Setting索引设置
我们可以通过多种方式来自定义ES的索引行为,但对于大部分应用来说,只需要搞清楚两个配置就行了number_of_shards与number_of_replicas。ES已经提供了优化好的默认配置,除非你明白这些配置的意义以及为什么这么做,不然就别改这些配置
修改index设置
Index创建后无法修改分片数量,但可以修改副本数量
1 | curl -XPOST 'localhost:9200/myindex/_close' |
需要注意的是,修改设置前应该先关闭index服务,修改完成后再启动
修改index的分词方式
1 | curl -XPOST 'localhost:9200/myindex/_close' |
获取设置信息
1 | curl -XGET 'http://localhost:9200/twitter/_settings' |
同时获取多个
1 | curl -XGET 'http://localhost:9200/twitter,kimchy/_settings' |
指定过滤条件获取
1 | curl -XGET 'http://localhost:9200/2013-*/_settings/name=index.number_*' |
分析器
这是索引设置的第三个重要的配置项,几年前我从开始接触ES后的很长一段时间里认为ES里的Analyze就是分词器,后来才意识到应该是分析器,分析器中包含了分词器,过滤器。ES官方内置了一些分析器,最重要的就是standard。可惜内置的分析器都是针对西方语系定制的。之后我们会讲到中文的Analyze以及如何定制我们需要的Analyze。Analyze的作用就是将你的文本数据转化成倒排索引。
测试分词
不指定index测试分词效果
1 | curl -XGET 'localhost:9200/_analyze' -d ' |
可以同时为多个文本进行分词
1 | curl -XGET 'localhost:9200/_analyze' -d ' |
可以指定一个index下默认的分词器进行测试
1 | curl -XGET 'localhost:9200/twitter/_analyze' -d ' |
可以指定index下指定分词器进行分词
1 | curl -XGET 'localhost:9200/twitter/_analyze' -d ' |
可以基于指定field的分词器进行分词
1 | curl -XGET 'localhost:9200/twitter/_analyze' -d ' |
也可以在一个URL中带上分词参数
1 | curl -XGET 'localhost:9200/_analyze?tokenizer=keyword&amp;amp;amp;filters=lowercase&amp;amp;amp;text=this+is+a+test' |
分词器Explain
与关系型数据库一样,ES也拥有explain功能,在get _analyze请求中将explain参数设置为true,可以输出分词器的详细信息,explain本身并不提供任何调优的建议,但它能提供重要的信息帮助你做出调优决策。2.x后新特性。
1 | curl -XGET 'localhost:9200/twitter/_analyze' -d ' |
Index模板
索引模板用来预先配置索引。通常用在索引日志数据的场景,比如,你将日志数据索引在一个以日期结尾的索引上,以后每天,一个新的配置好的索引会自动创建好。
创建index模板
Index模板的作用就是它的字面意义,定义好了template,创建index时可以应用这些template,或者,创建好模板后应用到指定的index上。Template中可以包含setting,aliases与mapping配置。
下面的例子就是创建一个模板,将这个模板中包含的一系列配置应用到名称是“te”开头的index上。
1 | curl -XPUT localhost:9200/_template/template_1 -d ' |
同时应用多个模板,应用过程中用order指定每个模板的执行顺序,如果后一个模板中包含的配置信息与前一个模板有重复,就将覆盖前一模板重复的配置。
1 | curl -XPUT localhost:9200/_template/template_1 -d ' |
删除index模板
1 | curl -XDELETE localhost:9200/_template/template_1 |
获取template信息
1 | curl -XGET localhost:9200/_template/template_1 |
查询template是否存在
1 | curl -XHEAD -i localhost:9200/_template/template_1 |
Warmers管理
预热器,原理是将数据段提前加载到内存中,以提升检索性能。该功能主要用于聚合与排序操作。
2.x版本中被deprecated了,即将在3.0版本中被移除,不多作介绍了
https://github.com/elastic/elasticsearch/issues/15607
Replica配置
很奇怪官网上关于副本配置的章节只有对shadow replicas(影子副本)的描述,先留白等后面碰到相关的要点再补充到这里。
影子副本
官网原文“This functionality is experimental and may be changed or removed completely in a future release.”- 这是个实验性功能,在更高版本中可能会被修改或者完全删除。
这个章节已经够枯燥了,对于具有不确定性的功能,我就偷个懒不浪费时间了,感兴趣的朋友可以去官网(https://www.elastic.co/guide/en/elasticsearch/reference/2.2/_node_level_settings_related_to_shadow_replicas.html)
题外话,如何甄别与接受开源项目中这种不稳定的功能,应当成为技术人员身上的一种能力,且不说每个人的精力是有限的,高风险的功能对于你生产集群的维护与升级带来的烦恼和损失才是难以估算的。
Index监控
后面两个章节的阅读对象不仅是开发人员,还有运维人员。
Indices状态
获取索引状态
1 | # 查看所有 |
指定返回的状态信息
1 | curl 'localhost:9200/_stats/merge,refresh' |
Indices段(segments)
一种衡量数据文件空间大小的逻辑单位,例如Oracle的最小结构单位是block,然后是extent,再就是segment,然后才是表空间和数据库。ES中最小逻辑单元的就是segment,从底到顶顺序是这样的,segment -> shard -> inverted index -> index。inverted index看着很陌生,但说到中文名就很响亮了 – 倒排索引,在索引设置的章节中也提到过它。
查看段信息
1 | # 查看指定索引的段 |
Indices恢复
获取索引恢复信息
1 | curl -XGET http://localhost:9200/index1,index2/_recovery |
Indices分片存储
提供Index 分片存储信息:分片在哪个节点上, 最近的状况, 是否有异常发生
1 | # 查看指定索引 |
Status管理
清除缓存
1 | # 清除指定索引 |
强制刷新
如果你的集群从采集到检索的过滤设置是非实时的,那么有时候又有实时性的需求,就可以调用强制刷新的接口。
1 | # 刷新指定索引 |
强制合并
为减少段(segment)的数量,ES允许你将多个索引进行强制合并
1 | # 合并指定索引 |
该接口还提供几个参数来定制你的合并需求,如max_num_segments是你想最终合并为几个segments,如果值为1,那么合并后数据全在一个段中;only_expunge_deletes,是否清除过期数据,大部分分布式数据库中,用户提交一个删除数据的请求,用户以为指定的数据已经消失了,事实上数据库是不会立即执行删除操作的,而只是在该条数据上打上了标签而已,再通过其他的策略定期或统一清除。
清空缓存
清空索引的缓冲数据以及事务日志。
1 | # 清空指定索引 |
升级版本
给指定索引升级版本,这里的版本升级是针对Lucene的,有时候你用旧的Lucene版本创建的索引,新版本的Lucene的reader无法解析,所以开发过程中要注意:
统一测试环境与线上的Lucene版本,其实就是ES版本;
开发人员之间的版本统一;
Solr迁移ES前对各种类型的数据有个全面的测试与评估;
慎用本接口
否则,重建索引的代价ESer们应该会懂
1 | curl -XPOST 'http://localhost:9200/twitter/_upgrade' |
写在最后,如果你打算在你的企业中构建一个搜索引擎,有两个要点,一是吃透ES的技术细节,二是弄清楚你的业务需求,然而很多技术人员会经常犯这样的错,过于关注第一点,而忽略第二点。在我看来,第二点来得更为重要。平台与解决方案是达到最终目的手段,在前行的过程中不要忘记为什么而出发。
附录
以下是几个ELK的管理脚本,供参考
https://github.com/imperialwicket/elasticsearch-logstash-index-mgmt
elasticsearch-backup-index.sh
1 | #!/bin/bash |
elasticsearch-close-old-indices.sh
1 | #!/bin/bash |
elasticsearch-remove-expired-indices.sh
1 | #!/usr/bin/env bash |
elasticsearch-remove-old-indices.sh
1 | #!/bin/bash |
elasticsearch-restore-index.sh
1 | #!/bin/bash |
es-backup-index.sh
1 | #!/bin/bash |
linux bash缓存
http://www.xuebuyuan.com/296675.html
在已经运行的系统中更改了某些以前存在于PATH环境变量所指明的目录中的程序的存放目录后可能出现No such file or directory的错误提示。
比如,以前python存放于/usr/bin/下,现在临时将/usr/bin/python删除,在/usr/local/bin下生成python,按照正常逻辑,此时直接执行python的话,会找到/usr/local/bin/python,因为/usr/local/bin也在环境变量PATH中,但却会出现bash: /usr/bin/python: No such file or directory
为何?
具体原因如下:
bash会保存一个从开机运行以来曾执行过得命令,而不是每次都搜索$PATH,如果在上次执行python命令之后更改了python的实际位置,则bash还会去按照原有hash table里面记录的路径去执行,但因为原执行文件已不存,所以会报出No such file or directory的错误提示。
通过执行 hash 命令可以很容易看出:
root@ubuntu:$ hash$
hits command
3 /usr/bin/which
2 /usr/bin/file
4 /usr/bin/sudo
1 /bin/mv
1 /usr/bin/whereis
7 /usr/bin/python
root@ubuntu:
解决方法:
解决方法也很简单,重置bash 的hash table即可,比如执行 hash -d python即可删除hash table中python的记录,如果再次执行python的时候,bash将搜索$PATH得到新的python路径
redis主从架构及redis集群
https://redis.io/topics/cluster-spec
Redis Cluster does not support multiple databases like the stand alone version of Redis. There is just database 0 and the SELECT command is not allowed.
1 | 31367:M 05 May 13:50:37.300 * DB loaded from disk: 576.582 seconds |
http://blog.csdn.net/yuyixinye/article/details/41114119
1 | int main(int argc, char **argv) { |