问题链接转载 Java面试通关要点汇总集【终极版】
select * from news where year(publish_time) < 2017; // 不要在列上使用函数
select * from news where publish_time < '2017-01-01'; // 可以
select * from news where id/100 = 1; // 不要在列上进行计算
select * from news where id = 1*100; // 可以
数据库范式是为解决关系数据库中数据冗余,更新异常,插入异常,删除异常问题而引入的。即数据库范式可以避免数据冗余,减少数据库的空间并减轻维护数据完整性的麻烦
反模式
通过数据库范式设计,将会导致数据库业务涉及的表变多,并有可能多表联查导致性能变差,也不利于分库分表。出于性能考虑,可能在数据库结构中需要使用反模式的设计,即空间换时间,采取数据冗余的方式避免表之间的关联查询。但需要谨慎使用反模式设计数据库。一般尽可能使用范式化的数据库设计。
分表
通过分表,可以减少数据库的单表负担,将压力分散到不同的表上,起到提高查询性能,缩短查询时间的作用,可以很大缓解表锁的问题。分表策略可以归为垂直拆分和水平拆分。
垂直拆分,把表的字段进行拆分,即一张字段比较多的表拆分为多张表。如何设计
- 将不常用的字段单独拆分到另外一张扩展表
- 将大文本的字段单独拆分到另一张扩展表,如BLOB,TEXT,TINYBLOB,MEDIUMBLOB,LONGBLOB,TINYTEXT,MEDIUMTEXT和LONGTEXT字符串类型
- 将不经常修改的字段放在同一张表中,将经常改变的字段放在另一张表中。
- 对于需要经常关联查询的字段,建议放在同一张表中。
水平拆分,把表的行进行拆分,即把数据存放到多张表中。有很多策略,如取模分表,时间维度分表,以及自定义Hash分表。实际情况中,水平拆分往往会和垂直拆分结合使用。常见的水平分表策略可以总结为随机分表和连续分表。
- 连续分表可以快速定位到表进行高效查询。大多数情况下可以有效避免跨表查询。要想扩展,只需要添加额外的分表,不需要迁移旧数据。但连续分表有可能存在数据热点问题,有些表可能会被频繁地查询从而造成较大压力,热数据的表就成为整个库的瓶颈
- 随机分表是遵循规则策略进行写入与读取,而不是真正意义上的随机。采用取模分表或者自定义Hash分表的方式进行水平拆分。随机分表的数据相对比较均匀,不容易出现热点和并发访问瓶颈。但分表扩展需要迁移旧的数据。此外,随机分表比较容易面临跨表查询的复杂问题。
对于日志场景,可以考虑根据时间维度分表。对于海量用户场景,可以考虑取模分表。对于用户场景,可以考虑按用户维度分表
分库可以减轻Mysql服务器的压力。分库策略也可以归纳为垂直拆分和水平拆分。
- 垂直拆分,按照业务和功能划分,把数据分别放在不同的数据库中
- 水平拆分,把一张表的数据划分到不同的数据库,两个数据库的表结构一样。水平分库跟水平分表类似,如取模分表,时间维度分表,以及自定义Hash分表。
四、分库与分表带来的分布式困境与应对之策
连续分表可能存在数据热点问题,个别表被频繁查询从而造成较大压力,个别表又有可能很少被查询到。热数据的表成为整个库的瓶颈。但连续分表不需要迁移旧数据,只需要添加分表就可扩容。但随机分表的数据分布比较均匀,不会出现热点和并发访问的瓶颈,但扩容需要迁移旧数据。
所以针对水平分布的设计至关重要,需要评估中短期内业务的增长速度,对当前的数据量进行容量规划,综合成本因素,推算出大概需要多少分片。对于数据迁移问题,一般是通过程序先读出数据,然后按照指定的分表策略再将数据写入到各个分表中。
随着分库与分表的演变,联合查询就遇到跨库关联和跨表关系问题。在设计之初就应该尽量避免联合查询,可以通过程序中进行拼装,或者通过反范式化设计进行规避。
一般情况下,列表分页时需要按照指定字段进行排序,随着分库与分表的演变,也会遇到跨库排序和跨表排序问题。为了保证最终结果的准确性,需要在不同的分表中将数据将数据排序并返回,并将不同分表返回的结果集进行汇总和再次排序,最后再返回给用户。
如何保证数据的一致性就成为一个必须面对的问题。目前,分布式事务并没有很好的解决方案,难以满足数据强一致性,一般情况下,使存储数据尽可能达到用户一致,保证系统经过一段较短时间的自我恢复和修正,数据最终达到一致。
在分库分表情况下,数据分布到不同的分表上,不能再借助数据库自增长特性。需要使用全局唯一ID,例如UUID,GUID等。
总结:
分库分表主要应对互联网常见的两个场景:海量数据和高并发。但同时也提高了系统的复杂度和维护成本。所以要结合实际需求,不宜过度设计。在项目一开始可以不采用分库与分表设计,而是随着业务的增长,在无法继续优化情况下,再考虑分库与分表提高系统的性能。
改善性能最好的方式,就是通过数据库中合理地使用索引。MySQL索引可以分为单列索引,复合索引,唯一索引,主键索引等。
create index index_name on tbl_name(index_col_name); // 创建一个单列索引
alter table tbl_name add index index_name on (index_col_name); // 也可以通过修改表结构的方式添加索引
create index index_name on tbl_name(index_col_name,...); // 创建一个复合索引
alter table tbl_name add index index_name on (index_col_name,...); // 也可以通过修改表结构的方式添加索引
create unique index index_name on tbl_name(index_col_name); // 创建一个单列索引
alter table tbl_name add unique index index_name on (index_col_name); // 也可以通过修改表结构的方式添加索引
alter table tbl_name add primary key(index_col_name);
select * from tbl_name force index (index_col_name )...
CREATE TABLE IF NOT EXISTS `app_full_text` (
`app_id` bigint(20) NOT NULL,
`app_name_full_text` text NOT NULL,
`introduce_full_text` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
需要对应用创建全文索引,可以这么设计
alter table `app_full_text` add fulltext key `app_name_intro` (`app_name_full_text`);
MySQL默认不支持中文全文搜索。
线上遇到MySQL死锁的相关问题,需要查看MySQL出现的Deadlock日志,可以通过如下命令执行
show engine innodb status;
来查看InnoDB类型数据库的状态,查找latest detected deadlock部分,可以看得最近造成死锁的两条msql
MySQL不同的存储引擎支持不同的锁机制。比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking);BDB存储引擎采用的是页面锁(page-level locking),但也支持表级锁;InnoDB存储引擎既支持行级锁(row-level locking),也支持表级锁,但默认情况下是采用行级锁。
在数据库中有两种基本的锁类型:排他锁(Exclusive Locks,即X锁)和共享锁(Share Locks,即S锁)。当数据对象被加上排他锁时,其他事务不能对它读取和修改。加了共享锁的数据对象可以被其他事务读取,但不能修改。数据库利用这两种基本的锁类型来对数据库的事务进行并发控制。
用户A访问表A(锁住了表A),然后又访问表B;用户B访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,用户B要等用户A释放表A才能继续,这样死锁就产生了。
解决方法:
这种死锁比较常见,是由于程序BUG产生。对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁住两个资源,如操作表A和表B时,总是按先A后B的顺序处理。必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。
用户A查询一条记录,然后修改该记录;这时用户B修改该记录,这时用户A的事务里锁的性质由查询的共享锁企图上升到排他锁,而用户B的排他锁由于A有共享锁存在所以必须等A释放共享锁,而A由于B的排他锁而无法上升排他锁,也就不可能释放共享锁,于是出现了死锁。
解决方法:
如果在事务中执行一条不满足条件的update语句,则执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引键的过少或不合适时,使得经常发生全表扫描,最终应用系统会越来越慢,发生阻塞或死锁。
解决方法:
SQL语句中不要使用太复杂的关联多表查询;使用执行计划对SQL语句进行分析,对于有全表扫描的SQL语句,建立相应的索引进行优化。
在MySQL 5.1及之前的版本中,MyISAM是默认的存储引擎,而在MySQL 5.5以后,默认使用InnoDB存储引擎。
一般情况下,应该优先选择InnoDB存储引擎,并且尽量不要将InnoDB和MyISAM混合使用。
当一个新表被创建之时,系统将在磁盘中分配一段以8K为单位的连续空间,当字段的值从内存写入磁盘时,就在这一既定空间随机保存,当一个8K空间用完的时候,SQL Server指针会自动分配一个8K的空间。这里,每个8K空间被称为一个数据页,又名页面或数据页面,并分配从0-7的页号,每个文件的第0页记录引导信息,叫文件头;每8个数据页(64K)的组合形成扩展区(Extent),称为扩展,全部数据页的组合形成堆(Heap)。
SQL Server规定行不能跨越数据页,所以,每行记录的最大数据量只能为8K。这就是char和varchar这两种字符串类型容量要限制在8K以内的原因。存储超过8K的数据应使用text类型,实际上,text类型的字符值不能直接录入和保存,它只是存储一个指针,指向由若干8K的文本数据页所组成的扩展区,真正的数据正是放在这些数据页中。
数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库表中数据。索引的实现通常使用B树及其变种B+树。为表设置索引要付出代价的:一是增加了数据库的存储空间,二是在插入和修改数据时要花费较多的时间
上图展示了一种可能的索引方式。左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址(注意逻辑上相邻的记录在磁盘上也并不是一定物理相邻的)。为了加快Col2的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针。
创建索引可以大大提高系统的性能
创建索引的不足
由于存储介质的特性,磁盘本身存取就比主存慢很多,再加上机械运动耗费,磁盘的存取速度往往是主存的几百分之一。因此为了提高效率,要尽量减少磁盘I/O。所以磁盘往往不是严格按需读取,而是每次都会预读,即使只需要一个字节,磁盘也会从这个位置开始,顺序向后读取一定长度的数据放入内存。
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址,下图是MyISAM索引的原理图
这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primary key)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondary key)在结构上没有任何区别,只是主索引要求key是唯一,而辅助索引的key可以重复。如果在Col2上建立一个辅助索引,则此索引结构如下图所示
同样也是一棵B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。MyISAM的索引方式也叫做“非聚集”
MyISAM索引文件和数据文件是分离。InnoDB的数据文件本身就是索引文件。表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
上图是InnoDB主索引(同时也是数据文件)的示意图,可以看得叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身是按主键聚集,所以InnoDB要求表必须有主键,如果没有显示指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
InnoDB的辅助索引data域存储相应记录主键的值而不是地址。InnoDB的所有辅助索引都引用主键作为data域。如下图为定义在Col3上的一个辅助索引:
聚集索引使得按主键搜索十分高效,但辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
根据B-Tree的定义,可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。为了达到这个目的,在实际实现B-Tree还需要使用如下技巧:
每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加上计算机存储分配都是按页对齐的,就实现了一个node只需一次I/O。B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)。综上所述,用B-Tree作为索引结构效率是非常高。而红黑树这种结构,h明显要深得多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的I/O渐进复杂度也为O(h),效率明显比B-Tree差很多。B+Tree更适合外存索引,原因和内节点出度d有关。从上面分析可以看出,d越大索引的性能越好,而出度的上限取决于节点内key和data的大小:
dmax = floor(pagesize / (keysize+datasize+pointsize)) (pagesize - dmax >= pointsize)
或
dmax = floor(pagesize / (keysize+datasize+pointsize)) - 1 (pagesize - dmax >= pointsize)
floor表示向下取整。由于B+Tree内节点去掉了data域,因此可以拥有更大的出度,拥有更好的性能。
一、聚集索引
聚集索引中键值的逻辑顺序决定了表中相应行的物理存储位置,因此一个表中只能有一个聚集索引。索引的逻辑顺序与相应行的物理位置一致。
MySQL的InnoDB引擎
InnoDB按照主键进行聚集,当没有设置主键时,InnoDB会以唯一的非空索引来代替,如果既没有主键,也没有唯一的非空索引,InnoDB则会生成一个隐藏的主键然后在上面进行聚集。
SQL SERVER 在建立索引时,可以将索引设置为聚集索引,但是varbinary类型的字段是不可以建立索引的。
二、非聚集索引
limit 分页优化方法
select * from Member where MemberID >= (select MemberID from Member limit 100000,1) limit 100;
类似建立索引,用一张表来维护页数,然后通过高效的连接得到数据,缺点是只适合数据数固定的情况,数据不能删除,维护表困难
当偏移超过一半记录数的时候,先用排序,这样偏移就反转了。缺点是order by优化比较麻烦,要增加索引,索引会影响数据的修改效率,并且要知道总记录数,偏移大于数据的一半
正向查找SQL:
Sql代码
1.SELECT * FROM `abc` WHERE `BatchID` = 123 LIMIT 1199960, 40
SELECT * FROM `abc` WHERE `BatchID` = 123 LIMIT 1199960, 40
时间:2.6493 秒
反向查找sql:
Sql代码
1.SELECT * FROM `abc` WHERE `BatchID` = 123 ORDER BY InputDate DESC LIMIT 428775, 40
SELECT * FROM `abc` WHERE `BatchID` = 123 ORDER BY InputDate DESC LIMIT 428775, 40
时间:1.0035 秒
优点:维护简单,实现也容易
缺点:应用的计算成本较大,且GUID的长度比较长,占用数据库存储空间较大,涉及到应用的开发
优点:全局唯一主键简单,维护相对容易
缺点:实现复杂,需要应用开发,且ID表要频繁查和频繁更新,插入数据时影响性能
优点:没有特别明显的优点
缺点:实现较为复杂,且整体可用性维系在这个中心数据库服务器上,一旦这里crash,所有的集群都无法进行插入操作
优点:实现简单,维护也比较简单,对应用透明
缺点:引用关联操作相对比较复杂,需要两个字段,主键占用空间较大,在使用InnoDB时这点副作用特别明显
优点:实现简单,且比较容易根据ID大小直接判断数据处于哪个集群,对应用透明
缺点:维护相对复杂,需要高度关注各个集群ID增长状况
优点:实现简单,后期维护简单,对应用透明
缺点:第一次设置相对较为复杂
数据存储的选型问题,是选择使用关系型数据库MySQL,还是选择内存数据库Redis,还是选择文档数据库MongoDB,还是选择列族数据库HBase,还是选择全文搜索引擎ElasticSearch
MySQL是一个最流行的关系型数据库,在互联网产品中应用比较广泛。一般情况下,MySQL数据库是选择的第一方案,基本上有80% - 90%的场景都是基于MySQL数据库的。因为,需要关系型数据库进行管理,此外,业务存在许多事务性的操作,需要保证事务的强一致性。同时,可能还存在一些复杂的SQL查询,值得注意的是,前期尽量减少表的联合查询,便于后期数据量增大的情况下,做数据库的分库分表。
随着数据量的增长,MySQL已经满足不了大型互联网应用的需求。因此,Redis基于内存存储数据,可以极大的提高查询性能,对产品在架构上很好的补充。例如,为了提高服务端接口的访问速度,尽可能将读频率高的热点数据存放在Redis中。这个是非常典型的以空间换时间的策略,使用更多的内存换取CPU资源,通过增加系统的内存消耗,来加快程序的运行速度。
在某些场景下,可以充分的利用Redis特性,大大提高效率。这些场景包括缓存,会话缓存,时效性,访问频率,计数器,社交列表,记录用户判定信息,交集,并集和差集,热门列表与排行榜,最新动态等。
使用Redis做缓存时,需要考虑数据不一致与脏读,缓存更新机制,缓存可用性,缓存服务降级,缓存穿越,缓存预热等缓存使用问题。
MongoDB是对传统关系型数据库的补充,它非常适合高伸缩性的场景,它是可扩展性的表结构。基于这点,可以将预期范围内,表结构可能会不同扩展的MySQL表结构,通过MongoDB来存储,这就可以保证表结构的扩展性。
此外,日志系统数据量特别大,如果用MongoDB数据库存储这些数据,利用分片集群支持海量数据,同时使用聚集分析和MapReduce能力,是个很好的选择。
MongoDB还适合存储大尺寸的数据,GridFS存储方案是基于MongoDB的分布式文件存储系统
HBase适合海量数据的存储与高性能实时查询,它是运行于HDFS文件系统之上,并且作为MapReduce分布式处理的目标数据库,以支撑离线分析型应用。在数据仓库,数据集市,商业智能等领域发挥了越来越多的作用,在数以千计的企业中支撑着大量的大数据分析场景的应用
在一般情况下,关系型数据库的模糊查询,都是通过like的方式进行查询,其中like "value%" 可以使用索引,但对于 like "%value%" 执行全表查询。这在数据量小的表,不存在性能问题,但对于海量数据,全表扫描是非常可怕的事情。ElasticSearch作为一个建立在全文搜索引擎Apache Lucene基础上的实时分布式搜索和分析引擎,适用于处理实时搜索应用场景。此外,使用ElasticSearch全文搜索引擎,还可以支持多词条查询,匹配度与权重,自动联想,拼写纠错等高级功能。因此,可以使用ElasticSearch作为关系型数据库全文搜索的功能补充,将要进行全文搜索的数据缓存一份到ElasticSearch上,达到处理复杂的业务与提高查询速度的目的。
ElasticSearch不仅仅适用于搜索场景,还非常适合日志处理与分析的场景。著名的ELK日志处理方案,由ElasticSearch,Logstash和Kibana三个组件组成,包括了日志收集,聚合,多维度查询,可视化显示等
MongoDB采用了一个称之为ObjectId的类型来做主键。ObjectId是一个12字节的BSON类型字符串。按照字节顺序依次代表:
ObjectId获取时间
从ObjectId的构造上来看,内部就嵌入了时间类型。我们肯定可以从中获取时间信息:即插入此文档时的时间。MongoDB对ObjectId对象提供了getTimestamp()方法来获取ObjectId的时间
> a = new ObjectId()
ObjectId("53102b43bf1044ed8b0ba36b")
> a.getTimestamp()
ISODate("2014-02-28T06:22:59Z")
根据时间构造ObjectId
通过字符串来构造
//使用Date的字符串构造方法生成日期,然后使用Date对象的getTime获取豪秒数,再除以1000得到标准时间戳
> a = new Date("2012-12-12 00:00:00").getTime()/1000
1355241600
//获取时间戳的标准十六进制表示
> a = a.toString(16)
50c75880
//在后面填补16个0
> a = a + new Array(17).join("0") 50c7588000000000000000000 // 使用24个字符串构造ObjectId
> b = new ObjectId(a) ObjectId("50c7588000000000000000000") // 获取时间以验证
> b.getTimestamp() ISODate("2012-12-12T16:00:00Z")
上述过程中 new Array(17).join("0") 目的是生成16个0拼接的字符串。
根据ObjectId按照插入时间排序
MongoDB默认在ObjectId建立索引,是按照插入时间排序的。我们可以使用此索引进行查询和排序
// 按序插入三个文档
> db.col.insert({"num":1})
> db.col.insert({"num":2})
> db.col.insert({"num":3})
>db.col.find().pretty()
{"_id":ObjectId("53102fb4bf1044ed8b0ba36c"),"num":1}
{"_id":ObjectId("53102fb4bf1044ed8b0ba36c"),"num":2}
{"_id":ObjectId("53102fb4bf1044ed8b0ba36c"),"num":3}
MongoDB是对传统关系型数据库的补充,但是MongoDB不支持事务,因此对事务性有要求的程序不建议使用MongoDB。此外,MongoDB也不支持表联合查询。
MongoDB非常适合高伸缩性的场景,它是可扩展性的表结构。基于这点,可以将预期范围内,表结构可能会不断扩展的MySQL表结构,通过MongoDB来存储,这就可以保证表结构的扩展性
日志系统数据量特别大,如果用MongoDB数据库存储这些数据,利用分片集群支持海量数据,同时使用聚集分析和MapReduce的能力
MongoDB还适合存储大尺寸的数据,如GridFS存储方案,就是基于MongoDB的分布式文件存储系统
倒排索引源于实际应用中需要根据属性的值来查找记录,Lucene是基于倒排索引实现的。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件被称为倒排索引文件,简称倒排文件(inverted file)。倒排索引一般表示为一个关键词,然后是它的频度,位置。
倒排索引由两个部分组成:单词词典和倒排文件
假设我们有两个文档,每个文档的content域包含如下内容:
为了创建倒排索引,我们首先将每个文档的content域拆分成单独的词,创建一个包含所有不重复词条的排序列表,然后列出每个词条出现在哪个文档,如下
Term | Doc_1 | Doc_2 |
Quick | X |
|
The | X | |
brown | X | X |
dog | X | |
dogs | X | |
fox | X | |
foxes | X | |
in | X | |
jumped | X | |
lazy | X | X |
leap | X | |
over | X | X |
quick | X | |
summer | X | |
the | X | X |
如果要搜索quick brown,两个文档都匹配,但第一个文档Doc_1比第二个文档Doc_2匹配度更高。
如果我们将词条规范为标准模式,便可找到与用户搜索的词条不完全一致,但具有足够相关性的文档,如
Term | Doc_1 | Doc_2 |
brown | X | X |
dog | X | X |
fox | X | X |
in | X | |
jump | X | X |
lazy | X | X |
over | X | X |
quick | X | X |
summer | X | |
the | X | X |
如果对搜索的字符串使用与content域相同的标准化规则,会变成查询quick+fox,这样两个文件都匹配。
传统项目中,搜索引擎是部署在成熟的数据存储的顶部,以提供快速且相关的搜索能力。这是因为早期的搜索引擎不能提供耐用的存储或其他经常需要的功能,如统计。
ElasticSearch是提供持久存储,统计等多项功能的现代搜索引擎。如果开发一个新项目,可以考虑使用ElasticSearch作为唯一的数据存储,以帮助保持设计尽可能简单。此场景不支持包含频繁更新,事务操作。
如新建一个博客系统使用es作为存储:
ES作为存储的优势:如果一台服务器出现故障时可以通过复制数据到不同的服务器以达到容错的目的。
由于ES不能提供存储的所有功能,一些场景下需要在现有系统数据存储的基础上新增ES支持。
在一些使用情况下,您不必写一行代码就能通过ElasticSearch完成一项工作。很多工具都可以与ElasticSearch一起工作,不必从头编写。如部署一个大规模的日志框架存储,搜索,并分析了大量的事件。
如下图,处理日志和输出到ElasticSearch,可以使用日志记录工具,如rsyslog(www.rsyslog.com),Logstash(www.elastic.co/products/logstash),或apache Flume(flume.apache.org)。搜索和可视化界面分析这些日志,可以使用Kibana
为什么那么多工具适配ElasticSearch?原因如下