大家好,今天给各位分享stats perform的一些知识,其中也会对Elasticsearch性能优化进行解释,文章篇幅可能偏长,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在就马上开始吧!
toad for oracle 的菜单栏怎么设置
菜单说明
新版本 toad软件中,比较有用的菜单
session菜单
Session Information:显示当前session的用户的情况,比如权限,授权等
Database菜单(很多功能都是重复的)
administrator:
审计,参数, redo, tablespace等
monitor:
~ top session finder(监控session),
~ database browser: database的全部内容,包括参数,表空间,数据文件等
~ database monitor:动态监视,包括 logical I/O, session, sga, physical IO, call rates, shared pool, waitevent等等.
~ server statistics:包括, waits, latches, sessions等等
~ session browser:可以用来 kill session,另外还有针对 session的 wait event等.
~ SGA Trace:针对 SGA的使用情况,详细列出了所有的 sql list.
~ SQL Tracker:打开这个,你在toad中的所有操作,都会有对应的SQL语句被输出.
diagnose:
alert log file, db health_check, log switch frequent, tablespace map
optimize:
~ ****yze all objects:类似 SYS.DBMS_STATS.GATHER_TABLE_STATS的作用.
~ Explain plan:执行计划,一般是先选择一个SQL语句,然后点工具栏上的小救护车(其实就是这项),就可以只执行plan.
~ Optimize Current SQL:调优当前SQL,貌似有用,以后实验.
~ Rebuild Multiple Objects:重建对象,比如重建索引,重建表等.
~ Rebulid table:重建表,很详细的设置
File:保存,打开,打印等等;文件比对功能
Edit:一些普通的修改选项,可以选择Editor Options来修改字体等.
Grid:主要是针对查询出来的结果进行另存,过滤等.
SQL Editor:执行编辑sql,另外可以得到查询语句中所使用的列的结构, control+ F9也可达到效果.
Create: create数据库中的对象.
Database: SQL_Modeler自动化生成sql语句(个人感觉没啥用),导入导出一些对象.
Tools: Master Detail Browser主表与外键参考表的情况,可以显示数据(如果数据很多怎么办,所以个人感觉用处小)
SGA Trace/Optimization:某个用户最近issue的sql语句.
? ****yze All Objects:
Rebuild Table:其实就是将整个表删除了重建(数据还保留着)
Rebuild Multiple Objects:(将对象推到了重建)
Multiple Object Privileges:分配和收回权限.
Object Search:能够搜索出某个用户的所有对象,包括列column.
Data Subset Wizard:可以生成脚本一个schema下的对象和数据**到另外一个对象下,我已经测试将scott下的所有对象**到LEON下,很好用的工具.
HTML Schema Doc Generator:生成某个Schema的整体的 html文档.
Tnsnames editor:修改 tnsname并且可以生成另一个文件.
ER Diagram:生成ER图,可以打印,可以生成sql语句
Single Object Comparison:单一对象的比较,其实就是对象的创建的script之间的比较.
Compare data:数据比较,比如两个表的数据比较,个人感觉用处小,直接一个 minus实现了.
Spool SQL:导出 SQL语句.
View:主要是针对当前的schema所有权限等,显示对应视图.
Session info:有用,显示roles, grant情况等.
Reports:可以生成对象的 report,格式很漂亮.
Object Palette:显示当前schema下的对象
Code Snippets:函数
SQL Command Recall:刚才使用过的 sql语句
Oracle Users List:当前数据库中的用户
Options: view的一些选项
DBA:顾名思义,肯定是针对一些数据库管理方面的内容.
Database Monitor:各种数据库监视的内容.
Database Probe:数据库的监测
Health Check:显示当前数据库的运行情况
Top Session Finder:显示当前占用资源的一些session,单击饼状图时,可以显示 session和 session的操作系统的一些信息.
Session Browser: session连接情况
oracle parameters:当前数据库 parameter设置.
NLS parameters: NLS参数设置.
OS Utilities:操作系统监控工具,很好用.
Auditing:针对用户的权限进行审计
Segment Management:各种存储空间的管理
Undo/Redo: redo与 undo的管理
? Data Import/Export:这个工具应该很有用,需要再确认.
Server Statistics: server的运行情况
control files:显示control file中的内容及多路复用情况.
Pinned Code: shared pool中关于锁的控制.
Generate Schema Script:生成某个用户的所有的对象
Compare Schemas:对比两个schema
Compare Database:对比两个数据库
Debug:针对 pl/sql的调试,例如单步跟踪等等.
Team Coding:团队合作时使用,比如某个source只能某个人先修改,然后其他人再修改.
SQL Editor
F2显示全屏还是显示结果内容
使用绑定变量, select* from EMPLOYEE WHERE employee_id=:EMPID
取消正在运行的sql,如果长时间运行的话,在菜单栏绿色运行箭头的右边有个 Cancel按钮.
F8 SQL RECALL功能
EDIT编辑功能, edit tablename F9(运行),这时出现的结果集就可以编辑了.
desc objectname F9(运行)显示这个object的定义等等.
像eclpise的alt+/一样,显示提示信息,这里提示的是列信息, tablename CTRL-T
format code,标准格式化代码,右键->Formating Tools->Formatcode
注释代码,鼠标选中->右键->Comment block(Uncomment block取消)
Data Grid
排序,列位置变更,过滤数据等都可以通过在结果集上右键找到
在结果集中修改数据可以先使用上边的Edit命令.
SQL Opimization
Explain plans
explain plans: show how Oracle executes a statement(在执行SQL以前就可以看到),点“Run Explain Plan for current statement” button在工具栏里.
另外, toad会保存这个 explain plan的结果,以方便跟调整后的sql语句的explain plan的结果进行比对.在 view->explain里进行比较.不过你如果想使用此功能,必须设置 view->options->oracle->general打开 Save previous Explain Plan results
其他设备
其他的,比如 SGA/Trace, session browser等
AutoTrace
AutoTrace:打开 autotrace,在 SQL Editor->右键->AutoTrace,注意: autotrace需要v$session支持,这样你就能打开autotrace,关闭 autotrace的地方也在这里.
SQL Trace
SQL Trace:(TKPROF) SQL Trace比 Auto trace功能更强大,另外结果文件会保存在服务器指定的 USER_DUMP_DESC参数所指定的位置.总之,这个设置起来可能稍微麻烦一点,如果真有需要,再专题看吧.
SGA Trace Optimization
Tools->SGA Trace Optimization
可以通过这个查看已经执行过的sql语句的资源使用情况.如果需要,你可以将某个 sql语句 copy到 SQL Editor进行编辑.
Session browser:你可以看哪些session连接到服务器,也可以 kill session.
Debugger
主要是用来调试 plsql的
perform line-by-line debugging and error trapping
Trace into other PL/SQL objects
Change the values of variables during runtime
Set breakpoints and watches
view the results of a returned REF cursor
设置 debugger, view->options->procedure Editor->Debugging,采用默认的设置就可以了
在 procedure Editor->右键->Debug
当你调试完以后,要将toad上边工具栏上的小昆虫点掉(Toggle compiling with debug)然后再重新编译一遍.
Database Administration
Toad provides a powerful but easy-to-use interface for managing the many ongoing tasks associated with Oracle database administration.你可以用 toad来进行日常的dba管理.
database browser: The database browser reads your TNSNAMEs.ora file,监控数据库的状态.
Managing Sessions
DBA->Top Session Finder,这个是按照使用资源的情况来排序.
Managing Tablespaces
3个主要的windows管理 tablespace
dba->segment management->tablespaces
dba->segment Management->Tablespace Map
SchemaBrowser->Tablespace Tab
Checking Extents
dba->segment management->extents
Using Import/Export Tools
dba->data import/export->export/import utility wizards
dba->data import/export->export/data pump export/import utility wizards
Performing SGA Trace Optimization
Tools->SGA Trace Optimization
User Administration
Schema Browser->Users
Schema Browser->Roles
Schema Browser->Resource Groups& Resource Plans
Schema Browser->Policies& PolicyGroups
Schema Browser->Sys Prives
DBA->Auditing->Audit SQL/SYS Privs
DBA->Auditing->Audit Object
Managing Users
Create User and Alter User:在 create模板里边有, Schema broswer选择到user找到对应user,右键alter就可以修改.而且还可以克隆这个 user.
On the Tablespace tab, Toad has an option to set your selections for the user’s default and temporary tabespaces as defaults for all future Create user sessions in Toad for the current database.
Compare users
在 schema browser中找到一个user右键->“Compare with another user”
Managing Roles
Schema Browser找到 role,然后可以查看细节等.
Managing Resource Groups and Resouorce Plans
Sechema Browser for managing your database’s resource consumer groups nad resource plans.
Managing Policies and Policy Groups
Sechema Browser for managing your database’s policies and policy groups.
Auditing
DBA->Auditing->Audit SQL/SYS Privs
Database->Auditing->Audit Objects
在启动master
以下。
一、master启动流程
1、脚本启动
1.1、启动脚本(start-hbase.sh)阅读:
"$bin"/hbase-daemon.sh--config"${HBASE_CONF_DIR}"$commandToRunmaster
1.2、hbase-daemon.sh:
(start)
check_before_start
hbase_rotate_log$HBASE_LOGOUT
hbase_rotate_log$HBASE_LOGGC
echorunning$command,loggingto$HBASE_LOGOUT
$thiscmd--config"${HBASE_CONF_DIR}"\
foreground_start$command$args${HBASE_LOGOUT}2>&1&
disown-h-r
sleep1;head"${HBASE_LOGOUT}"
;;
(foreground_start)
trapcleanAfterRunSIGHUPSIGINTSIGTERMEXIT
if["$HBASE_NO_REDIRECT_LOG"!=""];then
#NOREDIRECT
echo"`date`Starting$commandon`hostname`"
echo"`ulimit-a`"
#incasetheparentshellgetsthekillmakesuretotrapsignals.
#Onlyonewillgetcalled.Eitherthetraportheflowwillgothrough.
nice-n$HBASE_NICENESS"$HBASE_HOME"/bin/hbase\
--config"${HBASE_CONF_DIR}"\
$command"$@"start&
else
echo"`date`Starting$commandon`hostname`">>${HBASE_LOGLOG}
echo"`ulimit-a`">>"$HBASE_LOGLOG"2>&1
#incasetheparentshellgetsthekillmakesuretotrapsignals.
#Onlyonewillgetcalled.Eitherthetraportheflowwillgothrough.
nice-n$HBASE_NICENESS"$HBASE_HOME"/bin/hbase\
--config"${HBASE_CONF_DIR}"\
$command"$@"start>>${HBASE_LOGOUT}2>&1&
fi
#Addtothecommandlogfilevitalstatsonourenvironment.
hbase_pid=$!
echo$hbase_pid>${HBASE_PID}
wait$hbase_pid
;;
2、master的类实例化:
//1、创建rpc工厂类
rpcControllerFactory=RpcControllerFactory.instantiate(this.conf);
rpcRetryingCallerFactory=RpcRetryingCallerFactory.instantiate(this.conf);
rpcServices=createRpcServices();
this.rpcServices.start(zooKeeper);
//2、登录zk,并设置主监听ZKWatcher
ZKUtil.loginClient(this.conf,HConstants.ZK_CLIENT_KEYTAB_FILE,
HConstants.ZK_CLIENT_KERBEROS_PRINCIPAL,hostName);
zooKeeper=newZKWatcher(conf,getProcessName()+":"+
rpcServices.isa.getPort(),this,canCreateBaseZNode());
//3、启动线程,追踪master的地址,集群状态
masterAddressTracker=newMasterAddressTracker(getZooKeeper(),this);
masterAddressTracker.start();
clusterStatusTracker=newClusterStatusTracker(zooKeeper,this);
clusterStatusTracker.start();
//4、用于记录RegionServer的一些基本的实时信息,包括全局的memstore大小,memstore堆上堆
外开销,还包括replayeditsperregion
regionServerAccounting=newRegionServerAccounting(conf);
//5、是否启动blockCache块缓存和mob缓存,下边是判断条件,核心就是看看有没有表
booleani**asterNotCarryTable=
thisinstanceofHMaster&&!LoadBalancer.isTablesOnMaster(conf);
if(!i**asterNotCarryTable){
blockCache=BlockCacheFactory.createBlockCache(conf);
mobFileCache=newMobFileCache(conf);
}
//6、启动文件系统
initializeFileSystem();
//7、实例化用于收集“Spans”结构的信息的追踪系统
spanReceiverHost=SpanReceiverHost.getInstance(getConfiguration());
3、run方法启动master:
?
最终通过hbase命令启动master,然后通过HMasterCommandLine执行HMaster中的run方法:
publicvoidrun(){
try{
if(!conf.getBoolean("hbase.testing.nocluster",false)){
Threads.setDaemonThreadRunning(newThread(()->{
try{
intinfoPort=putUpJettyServer();
startActiveMasterManager(infoPort);
}catch(Throwablet){
//Makesurewelogtheexception.
Stringerror="FailedtobecomeActiveMaster";
LOG.error(error,t);
//Abortshouldhavebeencalledalready.
if(!isAborted()){
abort(error,t);
}
}
}),getName()+":becomeActiveMaster");
}
//Fallinhereevenifwehavebeenaborted.Needtoruntheshutdownservicesand
//thesuperruncallwilldothisforus.
super.run();
}finally{
if(this.clusterSchemaService!=null){
//Ifonwayout,thenwearenolongeractivemaster.
this.clusterSchemaService.stopAsync();
try{
this.clusterSchemaService.awaitTerminated(
getConfiguration().getInt(HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS,
DEFAULT_HBASE_MASTER_WAIT_ON_SERVICE_IN_SECONDS),TimeUnit.SECONDS);
}catch(TimeoutExceptionte){
LOG.warn("FailedshutdownofclusterSchemaService",te);
}
}
this.activeMaster=false;
}
}
1、注册backupmaster:
1.1startActiveMasterManager方法将会在znode中注册为backupmaster,因为最终不一定能成为active。当activemaster存在并知晓当前的backupmaster以后,就不会分配region给当前服务。
privatevoidstartActiveMasterManager(intinfoPort)throwsKeeperException{
StringbackupZNode=ZNodePaths.joinZNode(
zooKeeper.getZNodePaths().backupMasterAddressesZNode,serverName.toString());
LOG.info("AddingbackupmasterZNode"+backupZNode);
if(!MasterAddressTracker.setMasterAddress(zooKeeper,backupZNode,serverName,infoPort)){
LOG.warn("Failedcreateof"+backupZNode+"by"+serverName);
}
1.2、MasterAddressTracker.setMasterAddress将会调用zkUtil来完成节点和监听者的创建:
publicstaticbooleansetMasterAddress(finalZKWatcherzkw,
finalStringznode,finalServerNamemaster,intinfoPort)
throwsKeeperException{
returnZKUtil.createEphemeralNodeAndWatch(zkw,znode,toByteArray(master,infoPort));
}
2、启动线程,尝试成为activemaster:
blockUntilBecomingActiveMaster会阻塞,通过while不停地抢占znode:
if(activeMasterManager.blockUntilBecomingActiveMaster(timeout,status)){
finishActiveMasterInitialization(status);
}
在检查到可以成为activemaster之前,会删除znode中的backup信息,并修改状态为active:
if(ZKUtil.checkExists(this.watcher,backupZNode)!=-1){
LOG.info("DeletingZNodefor"+backupZNode+"frombackupmasterdirectory");
ZKUtil.deleteNodeFailSilent(this.watcher,backupZNode);
}
开始启动当前节点为activemaster:
privatevoidfinishActiveMasterInitialization(MonitoredTaskstatus)
循环判断当前节点是否还是master
//StarttheZombiemasterdetectoraftersettingmasterasactive,seeHBASE-21535
ThreadzombieDetector=newThread(newInitializationMonitor(this),
"ActiveMasterInitializationMonitor-"+System.currentTimeMillis());
zombieDetector.setDaemon(true);
zombieDetector.start();
master成为activemaster的过程:
//如果系统中有表就初始化MemStoreLAB
if(LoadBalancer.isTablesOnMaster(conf)){
initializeMemStoreChunkCreator();
}
//初始化文件系统
this.fileSystemManager=newMasterFileSystem(conf);
this.walManager=newMasterWalManager(this);
//启动tabledesc缓存
this.tableDescriptors.setCacheOn();
initializeMemStoreChunkCreator初始化主要是MemstoreLab创建对象,封装参数:
配置文件中对应的参数有三个:
hbase.hregion.memstore.mslab.enabled:是否启用memstoreLab
hbase.hregion.memstore.mslab.chunksize:chunk的尺寸
hbase.hregion.memstore.mslab.max.allocation:MSLAB中单次分配内存的最大尺寸,默认256K,超过该尺寸的内存直接在Heap上分配,当key/value大小大于256k,认为不是内存碎片
MemStoreChunkPool&MSLAB可以用来检测内存碎片并优化,从而避免regionserver产生大量的堆碎片-------传送门
MasterFileManager,封装了HMaster对底层文件系统的基本操作接口,交互所需的创建初始化布局,检查文件系统等
MasterWalManager则负责日志的管理,如拆分、查找文件&目录等操作
将clusterId存到zk中:
ZKClusterId.setClusterId(this.zooKeeper,fileSystemManager.getClusterId());
publicstaticvoidsetClusterId(ZKWatcherwatcher,ClusterIdid)
throwsKeeperException{
ZKUtil.createSetData(watcher,watcher.getZNodePaths().clusterIdZNode,id.toByteArray());
}
启动HBCK:
if(this.conf.getBoolean("hbase.write.hbck1.lock.file",true)){
HBaseFsck.checkAndMarkRunningHbck(this.conf,
HBaseFsck.createLockRetryCounterFactory(this.conf).create());
}
创建一个管理work的线程池:
createProcedureExecutor();
创建一个作业管理器,并返回处于RIT状态的region列表
//CreateAssignmentManager
this.assignmentManager=createAssignmentManager(this);
this.assignmentManager.start();
//TODO:TRSPcanperformasthesubprocedureforotherprocedures,soevenifiti**arkedas
//completed,itcouldstillbeintheprocedurelist.Thisisabitstrangebutisanother
//story,needtoverifytheimplementationforProcedureExecutorandProcedureStore.
ListritList=
proc**yType.getOrDefault(TransitRegionStateProcedure.class,Collections.emptyList()).stream()
.filter(p->!p.isFinished()).map(p->(TransitRegionStateProcedure)p)
.collect(Collectors.toList());
this.assignmentManager.setupRIT(ritList);
创建RegionServerTracker,该服务用于通过zk追踪reigonserver的状态。然后通过(initializeZKBasedSystemTrackers)初始化其他基于zk的追踪器。
this.regionServerTracker=newRegionServerTracker(zooKeeper,this,this.serverManager);
this.regionServerTracker.start(
proc**yType.getOrDefault(ServerCrashProcedure.class,Collections.emptyList()).stream()
.map(p->(ServerCrashProcedure)p).map(p->p.getServerName()).collect(Collectors.toSet()),
walManager.getLiveServersFromWALDir(),walManager.getSplittingServersFromWALDir());
//Thi**anagerwillbestartedAFTERhbase:metaisconfirmedonline.
//hbase.mirror.table.state.to.zookeeperissohbase1clientscanconnect.Theyreadtable
//statefromzookeeperwhilehbase2readsitfromhbase:meta.Disableifnohbase1clients.
this.tableStateManager=
this.conf.getBoolean(MirroringTableStateManager.MIRROR_TABLE_STATE_TO_ZK_KEY,true)
?
newMirroringTableStateManager(this):
newTableStateManager(this);
status.setStatus("InitializingZKsystemtrackers");
initializeZKBasedSystemTrackers();
检查meta是否需要初始化,会单独启动一个新线程去执行:
InitMetaProceduretemp=newInitMetaProcedure();
procedureExecutor.submitProcedure(temp);
后边很多不是很重要,就不一一列举了,比如负载均衡器。
Elasticsearch性能优化
注:文本整理自《ELKstack权威指南》
在 CRUD章节,我们已经知道 ES的数据写入是如何操作的了。喜欢自己动手的读者可能已经迫不及待的自己写了程序开始往 ES里写数据做测试。这时候大家会发现:程序的运行速度非常一般,即使 ES服务运行在本机,一秒钟大概也就能写入几百条数据。
这种速度显然不是 ES的极限。事实上,每条数据经过一次完整的 HTTP POST请求和 ES indexing是一种极大的性能浪费,为此,ES设计了批量提交方式。在数据读取方面,叫 mget接口,在数据变更方面,叫 bulk接口。mget一般常用于搜索时 ES节点之间批量获取中间结果集,对于 Elastic Stack用户,更常见到的是 bulk接口。
bulk接口采用一种比较简朴的数据积累格式,示例如下:
格式是,每条 JSON数据的上面,加一行描述性的元 JSON,指明下一行数据的操作类型,归属索引信息等。
采用这种格式,而不是一般的 JSON数组格式,是因为接收到 bulk请求的 ES节点,就可以不需要做完整的 JSON数组解析处理,直接按行处理简短的元 JSON,就可以确定下一行数据 JSON转发给哪个数据节点了。这样,一个固定内存大小的 network buffer空间,就可以反复使用,又节省了大量 JVM的 GC。
事实上,产品级的 logstash、rsyslog、spark都是默认采用 bulk接口进行数据写入的。对于打算自己写程序的读者,建议采用 Perl的 Search::Elasticsearch::Bulk或者 Python的 elasticsearch.helpers.*库。
在配置 bulk数据的时候,一般需要注意的就是请求体大小(bulk size)。
这里有一点细节上的矛盾,我们知道,HTTP请求,是可以通过 HTTP状态码 100 Continue来持续发送数据的。但对于 ES节点接收 HTTP请求体的 Content-Length来说,是按照整个大小来计算的。所以,首先,要确保 bulk数据不要超过 http.max_content_length设置。
那么,是不是尽量让 bulk size接近这个数值呢?当然不是。
依然是请求体的问题,因为请求体需要全部加载到内存,而 JVM Heap一共就那么多(按 31GB算),过大的请求体,会挤占其他线程池的空间,反而导致写入性能的下降。
再考虑网卡流量,磁盘转速的问题,所以一般来说,建议 bulk请求体的大小,在 15MB左右,通过实际测试继续向上探索最合适的设置。
注意:这里说的 15MB是请求体的字节数,而不是程序里里设置的 bulk size。bulk size一般指数据的条目数。不要忘了,bulk请求体中,每条数据还会额外带上一行元 JSON。
以 logstash默认的 bulk_size=> 5000为例,假设单条数据平均大小 200B,一次 bulk请求体的大小就是 1.5MB。那么我们可以尝试 bulk_size=> 50000;而如果单条数据平均大小是 20KB,一次 bulk大小就是 100MB,显然超标了,需要尝试下调至 bulk_size=> 500。
gateway是 ES设计用来长期存储索引数据的接口。一般来说,大家都是用本地磁盘来存储索引数据,即 gateway.type为 local。
数据恢复中,有很多策略调整我们已经在之前分片控制小节讲过。除开分片级别的控制以外,gateway级别也还有一些可优化的地方:
注意:gateway中说的节点,仅包括主节点和数据节点,纯粹的 client节点是不算在内的。如果你有更明确的选择,也可以按需求写:
虽然 ES对 gateway使用 NFS,iscsi等共享存储的方式极力反对,但是对于较大量级的索引的副本数据,ES从 1.5版本开始,还是提供了一种节约成本又不特别影响性能的方式:影子副本(shadow replica)。
首先,需要在集群各节点的 elasticsearch.yml中开启选项:
同时,确保各节点使用相同的路径挂载了共享存储,且目录权限为 Elasticsearch进程用户可读可写。
然后,创建索引:
针对 shadow replicas,ES节点不会做实际的索引操作,而是单纯的每次 flush时,把 segment内容 fsync到共享存储磁盘上。然后 refresh让其他节点能够搜索该 segment内容。
如果你已经决定把数据放到共享存储上了,采用 shadow replicas还是有一些好处的:
但是请注意:主分片节点还是要承担一个副本的写入过程,并不像 Lucene的 FileReplicator那样通过**文件完成,所以达不到完全节省 CPU的效果。
shadow replicas只是一个在某些特定环境下有用的方式。在资源允许的情况下,还是应该使用 local gateway。而另外采用 snapshot接口来完成数据长期备份到 HDFS或其他共享存储的需要。
我们都知道,ES中的 master跟一般 MySQL、Hadoop的 master是不一样的。它即不是写入流量的唯一入口,也不是所有数据的元信息的存放地点。所以,一般来说,ES的 master节点负载很轻,集群性能是可以近似认为随着 data节点的扩展线性提升的。
但是,上面这句话并不是完全正确的。
ES中有一件事情是只有 master节点能管理的,这就是集群状态(cluster state)。
集群状态中包括以下信息:
这些信息在集群的任意节点上都存放着,你也可以通过/_cluster/state接口直接读取到其内容。注意这最后一项信息,之前我们已经讲过 ES怎么通过简单地取余知道一条数据放在哪个分片里,加上现在集群状态里又记载了分片在哪个节点上,那么,整个集群里,任意节点都可以知道一条数据在哪个节点上存储了。所以,数据读写才可以发送给集群里任意节点。
至于修改,则只能由 master节点完成!显然,集群状态里大部分内容是极少变动的,唯独有一样除外——索引的映射。因为 ES的 schema-less特性,我们可以任意写入 JSON数据,所以索引中随时可能增加新的字段。这个时候,负责容纳这条数据的主分片所在的节点,会暂停写入操作,将字段的映射结果传递给 master节点;master节点合并这段修改到集群状态里,发送新版本的集群状态到集群的所有节点上。然后写入操作才会继续。一般来说,这个操作是在一二十毫秒内就可以完成,影响也不大。
但是也有一些情况会是例外。
在较大规模的 Elastic Stack应用场景中,这是比较常见的一个情况。因为 Elastic Stack建议采用日期时间作为索引的划分方式,所以定时(一般是每天),会统一产生一批新的索引。而前面已经讲过,ES的集群状态每次更新都是阻塞式的发布到全部节点上以后,节点才能继续后续处理。
这就意味着,如果在集群负载较高的时候,批量新建新索引,可能会有一个显著的阻塞时间,无法写入任何数据。要等到全部节点同步完成集群状态以后,数据写入才能恢复。
不巧的是,中国使用的是北京时间,UTC+0800。也就是说,默认的 Elastic Stack新建索引时间是在早上 8点。这个时间点一般日志写入量已经上涨到一定水平了(当然,晚上 0点的量其实也不低)。
对此,可以通过定时任务,每天在最低谷的早上三四点,提前通过 POST mapping的方式,创建好之后几天的索引。就可以避免这个问题了。
如果你的日志是比较严重的非结构化数据,这个问题在 2.0版本后会变得更加严重。 Elasticsearch从 2.0版本开始,对 mapping更新做了重构。为了防止字段类型冲突和减少 master定期下发全量 cluster state导致的大流量压力,新的实现和旧实现的区别在:
也就是说,一旦你日志中字段数量较多,在新创建索引的一段时间内,可能长达几十分钟一直被反复锁死!
这是另一种常见的滥用。在使用 Elastic Stack处理访问日志时,为了查询更方便,可能会采用 logstash-filter-kv插件,将访问日志中的每个 URL参数,都切分成单独的字段。比如一个"/index.do?uid=1234567890&action=payload"的 URL会被转换成如下 JSON:
但是,因为集群状态是存在所有节点的内存里的,一旦 URL参数过多,ES节点的内存就被大量用于存储字段映射内容。这是一个极大的浪费。如果碰上 URL参数的键内容本身一直在变动,直接撑爆 ES内存都是有可能的!
以上是真实发生的事件,开发人员莫名的选择将一个 UUID结果作为 key放在 URL参数里。直接导致 ES集群 master节点全部 OOM。
如果你在 ES日志中一直看到有新的 updating mapping [logstash-2015.06.01]字样出现的话,请郑重考虑一下自己是不是用的上如此细分的字段列表吧。
好,三秒钟过去,如果你确定一定以及肯定还要这么做,下面是一个变通的解决办法。
用 nested object来存放 URL参数的方法稍微复杂,但还可以接受。单从 JSON数据层面看,新方式的数据结构如下:
没错,看起来就是一个数组。但是 JSON数组在 ES里是有两种处理方式的。
如果直接写入数组,ES在实际索引过程中,会把所有内容都平铺开,变成 Arrays of Inner Objects。整条数据实际类似这样的结构:
这种方式最大的问题是,当你采用 urlargs.key:"uid" AND urlargs.value:"0987654321"语句意图搜索一个 uid=0987654321的请求时,实际是整个 URL参数中任意一处 value为 0987654321的,都会命中。
要想达到正确搜索的目的,需要在写入数据之前,指定 urlargs字段的映射类型为 nested object。命令如下:
这样,数据实际是类似这样的结构:
当然,nested object节省字段映射的优势对应的是它在使用的复杂。Query和 Aggs都必须使用专门的 nested query和 nested aggs才能正确读取到它。
nested query语法如下:
nested aggs语法如下:
ES内针对不同阶段,设计有不同的缓存。以此提升数据检索时的响应性能。主要包括节点层面的 filter cache和分片层面的 request cache。下面分别讲述。
ES的 query DSL在 2.0版本之前分为 query和 filter两种,很多检索语法,是同时存在 query和 filter里的。比如最常用的 term、prefix、range等。怎么选择是使用 query还是 filter成为很多用户头疼的难题。于是从 2.0版本开始,ES干脆合并了 filter统一归为 query。但是具体的检索语法本身,依然有 query和 filter上下文的区别。ES依靠这个上下文判断,来自动决定是否启用 filter cache。
query跟 filter上下文的区别,简单来说:
所以,选择也就出来了:
不过我们要怎么写,才能让 ES正确判断呢?看下面这个请求:
在这个请求中,
需要注意的是,filter cache是节点层面的缓存设置,每个节点上所有数据在响应请求时,是共用一个缓存空间的。当空间用满,按照 LRU策略淘汰掉最冷的数据。
可以用 indices.cache.filter.size配置来设置这个缓存空间的大小,默认是 JVM堆的 10%,也可以设置一个绝对值。注意这是一个静态值,必须在 elasticsearch.yml中提前配置。
ES还有另一个分片层面的缓存,叫 shard request cache。5.0之前的版本中,request cache的用途并不大,因为 query cache要起作用,还有几个先决条件:
以 Elastic Stack场景来说,Kibana里几乎所有的请求,都是有@timestamp作为过滤条件的,而且大多数是以最近 N小时/分钟这样的选项,也就是说,页面每次刷新,发出的请求 JSON里的时间过滤部分都是在变动的。query cache在处理 Kibana发出的请求时,完全无用。
而 5.0版本的一大特性,叫 instant aggregation。解决了这个先决条件的一大阻碍。
在之前的版本,Elasticsearch接收到请求之后,直接把请求原样转发给各分片,由各分片所在的节点自行完成请求的解析,进行实际的搜索操作。所以缓存的键是原始 JSON串。
而 5.0的重构后,接收到请求的节点先把请求的解析做完,发送到各节点的是统一拆分修改好的请求,这样就不再担心 JSON串多个空格啥的了。
其次,上面说的『拆分修改』是怎么回事呢?
比如,我们在 Kibana里搜索一个最近 7天(@timestamp:["now-7d" TO"now"])的数据,ES就可以根据按天索引的判断,知道从 6天前到昨天这 5个索引是肯定全覆盖的。那么这个横跨 7天的 date range query就变成了 5个 match_all query加 2个短时间的 date_range query。
现在你的仪表盘过 5分钟自动刷新一次,再提交上来一次最近 7天的请求,中间这 5个 match_all就完全一样了,直接从 request cache返回即可,需要重新请求的,只有两头真正在变动的 date_range了。
注1: match_all不用遍历倒排索引,比直接查询@timestamp:*要快很多。
注2:判断覆盖修改为 match_all并不是真的按照索引名称,而是 ES从 2.x开始提供的 field_stats接口可以直接获取到@timestamp在本索引内的 max/min值。当然从概念上如此理解也是可以接受的。
响应结果如下:
和 filter cache一样,request cache的大小也是以节点级别控制的,配置项名为 indices.requests.cache.size,其默认值为 1%。
字段数据(fielddata),在 Lucene中又叫 uninverted index。我们都知道,搜索引擎会使用倒排索引(inverted index)来映射单词到文档的 ID号。而同时,为了提供对文档内容的聚合,Lucene还可以在运行时将每个字段的单词以字典序排成另一个 uninverted index,可以大大加速计算性能。
作为一个加速性能的方式,fielddata当然是被全部加载在内存的时候最为有效。这也是 ES默认的运行设置。但是,内存是有限的,所以 ES同时也需要提供对 fielddata内存的限额方式:
Elasticsearch在 total,fielddata,request三个层面上都设计有 circuit breaker以保护进程不至于发生 OOM事件。在 fielddata层面,其设置为:
但是相比较集群庞大的数据量,内存本身是远远不够的。为了解决这个问题,ES引入了另一个特性,可以对精确索引的字段,指定 fielddata的存储方式。这个配置项叫: doc_values。
所谓 doc_values,其实就是在 ES将数据写入索引的时候,提前生成好 fielddata内容,并记录到磁盘上。因为 fielddata数据是顺序读写的,所以即使在磁盘上,通过文件系统层的缓存,也可以获得相当不错的性能。
注意:因为 doc_values是在数据写入时即生成内容,所以,它只能应用在精准索引的字段上,因为索引进程没法知道后续会有什么分词器生成的结果。
由于在 Elastic Stack场景中, doc_values的使用极其频繁,到 Elasticsearch 5.0以后,这两者的区别被彻底强化成两个不同字段类型: text和 keyword。
等同于过去的:
而
等同于过去的:
也就是说,以后的用户,已经不太需要在意 fielddata的问题了。不过依然有少数情况,你会需要对分词字段做聚合统计的话,你可以在自己接受范围内,开启这个特性:
你可以看到在上面加了一段 fielddata_frequency_filter配置,这个配置是 segment级别的。上面示例的意思是:只有这个 segment里的文档数量超过 500个,而且含有该字段的文档数量占该 segment里的文档数量比例超过 10%时,才加载这个 segment的 fielddata。
下面是一个可能有用的对分词字段做聚合的示例:
这个示例可以对经过了 logstash-filter-punct插件处理的数据,获取每种 punct类型日志的关键词和对应的代表性日志原文。其效果类似 Splunk的事件模式功能:
[图片上传失败...(image-b0b69f-1511752650964)]
如果经过之前章节的一系列优化之后,数据确实超过了集群能承载的能力,除了拆分集群以外,最后就只剩下一个办法了:清除废旧索引。
为了更加方便的做清除数据,合并 segment,备份恢复等管理任务,Elasticsearch在提供相关 API的同时,另外准备了一个命令行工具,叫 curator。curator是 Python程序,可以直接通过 pypi库安装:
注意,是 elasticsearch-curator不是 curator。PyPi原先就有另一个项目叫这个名字
和 Elastic Stack里其他组件一样,curator也是被 Elastic.co收购的原开源社区周边。收编之后同样进行了一次重构,命令行参数从单字母风格改成了长单词风格。新版本的 curator命令可用参数如下:
Options包括:
--host TEXT Elasticsearch host.
--url_prefix TEXT Elasticsearch http url prefix.
--port INTEGER Elasticsearch port.
--use_ssl Connect to Elasticsearch through SSL.
--http_auth TEXT Use Basic Authentication ex: user:pass
--timeout INTEGER Connection timeout in seconds.
--master-only Only operate on elected master node.
--dry-run Do not perform any changes.
--debug Debug mode
--loglevel TEXT Log level
--logfile TEXT log file
--logformat TEXT Log output format [default|logstash].
--version Show the version and exit.
--help Show this message and exit.
Commands包括:
alias Index Aliasing
allocation Index Allocation
bloom Disable bloom filter cache
close Close indices
delete Delete indices or snapshots
open Open indices
optimize Optimize Indices
replicas Replica Count Per-shard
show Show indices or snapshots
snapshot Take snapshots of indices(Backup)
针对具体的 Command,还可以继续使用--help查看该子命令的帮助。比如查看 close子命令的帮助,输入 curator close--help,结果如下:
在使用 1.4.0以上版本的 Elasticsearch前提下,curator曾经主要的一个子命令 bloom已经不再需要使用。所以,目前最常用的三个子命令,分别是 close, delete和 optimize,示例如下:
这一顿任务,结果是:
logstash-mweibo-nginx-yyyy.mm.dd索引保存最近 5天, logstash-mweibo-client-yyyy.mm.dd保存最近 10天, logstash-mweibo-yyyy.mm.dd索引保存最近 30天;且所有七天前的 logstash-*索引都暂时关闭不用;最后对所有非当日日志做 segment合并优化。
profiler是 Elasticsearch 5.0的一个新接口。通过这个功能,可以看到一个搜索聚合请求,是如何拆分成底层的 Lucene请求,并且显示每部分的耗时情况。
启用 profiler的方式很简单,直接在请求里加一行即可:
可以看到其中对 query和 aggs部分的返回是不太一样的。
query部分包括 collectors、rewrite和 query部分。对复杂 query,profiler会拆分 query成多个基础的 TermQuery,然后每个 TermQuery再显示各自的分阶段耗时如下:
我们可以很明显的看到聚合统计在初始化阶段、收集阶段、构建阶段、汇总阶段分别花了多少时间,遍历了多少数据。
注意其中 reduce阶段还没实现完毕,所有都是 0。因为目前 profiler只能在 shard级别上做统计。
collect阶段的耗时,有助于我们调整对应 aggs的 collect_mode参数选择。目前 Elasticsearch支持 breadth_first和 depth_first两种方式。
initialise阶段的耗时,有助于我们调整对应 aggs的 execution_hint参数选择。目前 Elasticsearch支持 map、 global_ordinals_low_cardinality、 global_ordinals和 global_ordinals_hash四种选择。在计算离散度比较大的字段统计值时,适当调整该参数,有益于节省内存和提高计算速度。
对高离散度字段值统计性能很关注的读者,可以关注 https://github.com/elastic/elasticsearch/pull/21626这条记录的进展。
(本文完)
文本整理自《ELKstack权威指南》
好了,文章到此结束,希望可以帮助到大家。
专题推荐: