<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" > <channel> <title>Big Data | Mobabel</title> <atom:link href="http://www.mobabel.net/category/big-data/feed/?lang=zh_cnpage-2page-7page-3page-10" rel="self" type="application/rss+xml" /> <link>http://www.mobabel.net</link> <description>Just One Pure ITer</description> <lastBuildDate>Thu, 15 Dec 2022 16:03:25 +0000</lastBuildDate> <language>en-US</language> <sy:updatePeriod> hourly </sy:updatePeriod> <sy:updateFrequency> 1 </sy:updateFrequency> <generator>https://wordpress.org/?v=6.6.2</generator> <item> <title>[汇总]数据分析项目</title> <link>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e9%a1%b9%e7%9b%ae/</link> <comments>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e9%a1%b9%e7%9b%ae/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Tue, 04 Jan 2022 00:15:03 +0000</pubDate> <category><![CDATA[Big Data Practice]]></category> <category><![CDATA[Big Data]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=7308</guid> <description><![CDATA[<p>Edouard Thomas 开发的一个类似的 开源 AI 博彩机器人项目(https://github.com/edouardthom/ATPBetting)   名为 freqtrade (https://github.com/freqtrade/freqtrade)的开源交易机器人。它使用 Python 构建,并实现了多种机器学习算法   人脸识别库(https://pypi.org/project/face-recognition/),并将其与摄像头的输出连接起来。 如何启动股票量化系统QTYX-Python环境安装Anaconda+Pycharm及TaLib        </p> The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e9%a1%b9%e7%9b%ae/">[汇总]数据分析项目</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p>Edouard Thomas 开发的一个类似的 开源 AI 博彩机器人项目(https://github.com/edouardthom/ATPBetting)</p> <p> </p> <p>名为 freqtrade (https://github.com/freqtrade/freqtrade)的开源交易机器人。它使用 Python 构建,并实现了多种机器学习算法</p> <p> </p> <p>人脸识别库(https://pypi.org/project/face-recognition/),并将其与摄像头的输出连接起来。</p> <p><span id="more-7308"></span></p> <p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/VueiIXrv9-lwHKcTp9crWg">如何启动股票量化系统QTYX-Python环境安装Anaconda+Pycharm及TaLib</a></p> <p> </p> <p> </p> <p> </p> <p> </p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e9%a1%b9%e7%9b%ae/">[汇总]数据分析项目</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e9%a1%b9%e7%9b%ae/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[汇总]数据分析经验</title> <link>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e7%bb%8f%e9%aa%8c/</link> <comments>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e7%bb%8f%e9%aa%8c/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Mon, 03 Jan 2022 22:51:54 +0000</pubDate> <category><![CDATA[Big Data Practice]]></category> <category><![CDATA[Pandas]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=7283</guid> <description><![CDATA[<p>推荐2个十分好用的pandas数据探索分析神器! PandasGUI 在Jupyter当中使用的小插件名叫ipympl,能够使得matplotlib绘制出来的图表也能够具备交互性的特征, 我用Java几分钟处理完30亿个数据        </p> The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e7%bb%8f%e9%aa%8c/">[汇总]数据分析经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/oW3IbXgUyxTEhtycqhOOUw">推荐2个十分好用的pandas数据探索分析神器!</a></p> <ul> <li data-tool="mdnice编辑器">PandasGUI</li> <li>在Jupyter当中使用的小插件名叫ipympl,能够使得matplotlib绘制出来的图表也能够具备交互性的特征,</li> </ul> <p><span id="more-7283"></span></p> <p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/k8uIu1naCTs8ps-hJVZlWA">我用Java几分钟处理完30亿个数据</a></p> <p> </p> <p> </p> <p> </p> <p> </p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e7%bb%8f%e9%aa%8c/">[汇总]数据分析经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e7%bb%8f%e9%aa%8c/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[汇总]日志系统</title> <link>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%97%a5%e5%bf%97%e7%b3%bb%e7%bb%9f/</link> <comments>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%97%a5%e5%bf%97%e7%b3%bb%e7%bb%9f/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Mon, 03 May 2021 23:35:57 +0000</pubDate> <category><![CDATA[Theory & Solution]]></category> <category><![CDATA[Logs]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=6728</guid> <description><![CDATA[<p>用了日志系统新贵Loki,ELK突然不香了! ELK不香了!我用Graylog NewRelic 替代ELK:ClickHouse+Kafka+FlieBeat</p> The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%97%a5%e5%bf%97%e7%b3%bb%e7%bb%9f/">[汇总]日志系统</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/xeke9b8pLwbcmVg5U7wdDw">用了日志系统新贵Loki,ELK突然不香了!</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/7ANaRewi839K0xnVgEV5VA">ELK不香了!我用Graylog</a></p> <p>NewRelic</p> <p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/YeyWAreoK8NjRyfYXaDBHQ">替代ELK:ClickHouse+Kafka+FlieBeat</a></p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%97%a5%e5%bf%97%e7%b3%bb%e7%bb%9f/">[汇总]日志系统</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bb%e6%97%a5%e5%bf%97%e7%b3%bb%e7%bb%9f/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>Set statement timeout for query execution</title> <link>http://www.mobabel.net/set-statement-timeout-for-query-execution/</link> <comments>http://www.mobabel.net/set-statement-timeout-for-query-execution/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Wed, 11 Nov 2020 13:59:54 +0000</pubDate> <category><![CDATA[Big Data Practice]]></category> <category><![CDATA[Python]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=6676</guid> <description><![CDATA[<p>How to set statement timeout for query execution? Prerequisites: sqlalchemy 1.3.19 MySQL 5.7.13 You can set the connection timeout easily like this: [crayon-674f7bff9116d897906022/] But sometimes when you execute a long time processing SQL, and...</p> The post <a href="http://www.mobabel.net/set-statement-timeout-for-query-execution/">Set statement timeout for query execution</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p>How to set statement timeout for query execution?</p> <p>Prerequisites:</p> <p>sqlalchemy 1.3.19</p> <p>MySQL 5.7.13</p> <p>You can set the connection timeout easily like this:</p><pre class="crayon-plain-tag">sqlalchemy.create_engine("mysql+pymysql://{}:{}@{}:{}/{}?charset={}", connect_args={'connect_timeout': 10}) //10 seconds</pre><p><span id="more-6676"></span></p> <p>But sometimes when you execute a long time processing SQL, and you need to set a timeout for the query. Otherwise, the script will keep working for hours and gives no feedback.</p> <p>For MySQL it self, you can use timeouts to prevent long-running queries from taking down your MySQL.</p> <p>With MySQL 5.7 you can now use a new optimizer query hint to <a href="https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time">configure the max execution time</a> of <code>SELECT</code> queries in Milliseconds.</p><pre class="crayon-plain-tag">SELECT /*+ MAX_EXECUTION_TIME(1000) */ status, count(*) FROM articles GROUP BY status ORDER BY status;</pre><p>Or you can set a <a href="https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_max_execution_time">session-wide or global timeout</a>:</p><pre class="crayon-plain-tag">SET SESSION MAX_EXECUTION_TIME=2000; SET GLOBAL MAX_EXECUTION_TIME=2000;</pre><p>The timeouts only apply to read-only SELECT queries.</p> <p>If your query takes to long, it fails with the following error:</p><pre class="crayon-plain-tag">ERROR 3024 (HY000): Query execution was interrupted, maximum statement execution time exceeded</pre><p>In Python, we can do this:</p><pre class="crayon-plain-tag">class TimeOutProxy(ConnectionProxy): def cursor_execute(self, execute, cursor, statement, parameters, context, executemany): timeout = context.execution_options.get('timeout', None) if timeout: c = cursor.connection.cursor() c.execute('SET SESSION MAX_EXECUTION_TIME=%d;' % int(timeout * 1000)) c.close() return execute(cursor, statement, parameters, context)</pre><p></p><pre class="crayon-plain-tag">engine_ppc = sqlalchemy.create_engine( "mysql+pymysql://{}:{}@{}:{}/{}?charset={}", connect_args={'connect_timeout': 10}, proxy=TimeOutProxy())</pre><p>Then in the place where you want to execute the query:</p><pre class="crayon-plain-tag">try: engine_ppc.execute( sa_text('''Select from table where id>0 ''').execution_options(autocommit=True, timeout=10)) except sqlalchemy.exc.OperationalError as e: print("Timeout when querying", flush=True)</pre><p>the timeout was set to 10 seconds.</p> <p> </p> <p> </p> <p>Referfence:</p> <p>https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_interactive_timeout</p> <p>https://stackoverflow.com/questions/35640726/how-to-set-connection-timeout-in-sqlalchemy/35640876</p> <blockquote class="wp-embedded-content" data-secret="xXepuTGlgr"><p><a href="https://tideways.com/profiler/blog/use-timeouts-to-prevent-long-running-select-queries-from-taking-down-your-mysql">Use timeouts to prevent long-running SELECT queries from taking down your MySQL</a></p></blockquote> <p><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted" title="“Use timeouts to prevent long-running SELECT queries from taking down your MySQL” — Tideways" src="https://tideways.com/profiler/blog/use-timeouts-to-prevent-long-running-select-queries-from-taking-down-your-mysql/embed#?secret=xXepuTGlgr" data-secret="xXepuTGlgr" width="500" height="282" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe></p> <p>https://stackoverflow.com/questions/6492366/how-to-set-statement-timeout-for-query-execution</p> <p> </p> <p> </p> <p> </p>The post <a href="http://www.mobabel.net/set-statement-timeout-for-query-execution/">Set statement timeout for query execution</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/set-statement-timeout-for-query-execution/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[汇总] ElasticSearch经验</title> <link>http://www.mobabel.net/%e6%b1%87%e6%80%bb-elasticsearch%e7%bb%8f%e9%aa%8c/</link> <comments>http://www.mobabel.net/%e6%b1%87%e6%80%bb-elasticsearch%e7%bb%8f%e9%aa%8c/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Sat, 07 Mar 2020 23:30:56 +0000</pubDate> <category><![CDATA[Elasticsearch]]></category> <category><![CDATA[ElasticSearch]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=6222</guid> <description><![CDATA[<p>图解ElasticSearch原理,你可收好了! Elasticsearch 技术分析(九):全文搜索引擎Elasticsearch,这篇文章给讲透了! Elasticsearch 如何做到快速检索 – 倒排索引的秘密 为什么 ElasticSearch 比 MySQL 更适合复杂条件搜索 一次惊心动魄的ElasticSearch集群灾难恢复记! SpringBoot 操作 ElasticSearch 详解(万字长文) 如何设计百万级商品数据实时同步的秒级搜索系统? 2 个 Canal 组件,第一个 Canal 实现数据 ETL,把商家、商品数据库的某些表及字段,抽取到搜索服务数据库。 再利用第二个 Canal,读取搜索服务 MySQL 数据库的 Binlog,实时传输到 Kafka 消息队列,再由 canal adapter...</p> The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb-elasticsearch%e7%bb%8f%e9%aa%8c/">[汇总] ElasticSearch经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/gZ7LqwHz1Vk4brKLiO-vrA">图解ElasticSearch原理,你可收好了!</a></p> <p><a id="cb_post_title_url" class="postTitle2 post-del-title" href="https://www.cnblogs.com/jajian/p/11223992.html">Elasticsearch 技术分析(九):全文搜索引擎Elasticsearch,这篇文章给讲透了!</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/hGTzbxZZA9Wis-OBWdYjUg">Elasticsearch 如何做到快速检索 – 倒排索引的秘密</a></p> <p><a href="https://mp.weixin.qq.com/s/1C8K1_lEN6U6hqOKEIyHLg">为什么 ElasticSearch 比 MySQL 更适合复杂条件搜索</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/HRvCIYkg4JSOESmOHRaIbw">一次惊心动魄的ElasticSearch集群灾难恢复记!</a><span id="more-6222"></span></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/Lz5Gq1Lwkn8GgMUozqUVjQ">SpringBoot 操作 ElasticSearch 详解(万字长文)</a></p> <p class="rich_media_title"><a href="https://mp.weixin.qq.com/s/ioCQ0_-VTY9ix7bPw7AYwQ">如何设计百万级商品数据实时同步的秒级搜索系统?</a></p> <blockquote><p>2 个 Canal 组件,第一个 Canal 实现数据 ETL,把商家、商品数据库的某些表及字段,抽取到搜索服务数据库。</p> <p>再利用第二个 Canal,读取搜索服务 MySQL 数据库的 Binlog,实时传输到 Kafka 消息队列,再由 canal adapter 对数据进行关联、父子文档映射等,将处理好的数据存储到 ElasticSearch 中。</p></blockquote> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/6QNevywkvmQq_pekRzRrjA">日均5亿查询量的京东订单中心,为什么舍MySQL用ES?</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/POJ43Pa2ZTHwhqDTYmHQ2w">Elasticsearch 实例管理在京东的使用场景及演进之路</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/Ix2Ax6cxXxLnPq4uBkbhng">解决 Elastic Search 的深分页问题</a></p> <p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/J3k9ybtWRQJX_lkU1HEvmg">Elasticsearch 实现分页的 3 种方式,还有谁不会??</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/NMcrzNq73rZoFQ4RmSTTjA">如何用ELK搭建TB级的日志监控系统?</a></p> <p id="activity-name" class="rich_media_title"><a href="https://mp.weixin.qq.com/s/MwGY6hA-uVxHGviejnOFow">别说,Cerebro还真好用!老板再也不用担心ES集群了</a></p> <p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/ZFQsPlQ89fRsjFBOR0qMDw">老板让我牵头搞ELK,我该如何确定ES的集群规模?</a></p> <p id="activity-name" class="rich_media_title "><a href="https://mp.weixin.qq.com/s/6KwAFjrxJQArcvP0UQSbbQ">Elasticsearch现在每家公司都在用,可是用的时候这些坑得避开!</a></p>The post <a href="http://www.mobabel.net/%e6%b1%87%e6%80%bb-elasticsearch%e7%bb%8f%e9%aa%8c/">[汇总] ElasticSearch经验</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e6%b1%87%e6%80%bb-elasticsearch%e7%bb%8f%e9%aa%8c/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[转]数据可视化图表选型</title> <link>http://www.mobabel.net/%e8%bd%ac%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e5%9b%be%e8%a1%a8%e9%80%89%e5%9e%8b/</link> <comments>http://www.mobabel.net/%e8%bd%ac%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e5%9b%be%e8%a1%a8%e9%80%89%e5%9e%8b/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Sun, 30 Jun 2019 09:57:07 +0000</pubDate> <category><![CDATA[Big Data Practice]]></category> <category><![CDATA[Big Data]]></category> <category><![CDATA[Charts]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=5844</guid> <description><![CDATA[<p>常听到一句话,“能用图描述的就不用表,能用表就不用文字”。这句话也直接的表明了:在认知上,大家对于图形的敏感度远比文字高。 但同时我们也面临着这样一些问题: 写 PPT、做 demo 时,心中有万千想法和海量数据想要去展现,但总是最后还是以文字和枯燥的图表堆叠呈现了出来,苦于怎么把这些数据展现的直观、性感、一看就懂。这时候,在心里怎么想和手上怎么画之间,差了一座“理解图表内涵”的桥梁了。 常见的图表选择原则文章有很多,这里只做简单总结,重点是从逆向来看常见场景选用以及按照数据关系选用,最后是使用中会遇到的一些常见问题。本文将分为以下两部分: 两个优秀的数据可视化案例 基础图形及特点 如何选用图标 常见的问题 作为视觉动物的我们,不妨先来看看惊艳全球的一些数据可视化的例子(原文链接)。 两个优秀的数据可视化案例 A. 按年龄段分布的美国人口百分比: 这个 GIF 动画,显示了随着时间推移的人口统计数量的变化。这是如何以令人信服的方式呈现一种单一的数据的好榜样。   B. 最有价值的运动队 这是通过叠加数据来讲述深层故事的一个例子。 这个交互由 ColumnFive 设计,受福布斯“2014年最具价值的运动队50强”名单得到的启发。但是它不仅将列表可视化,用户还可以通过它看到每支队伍参赛的时间以及夺得总冠军的数量。这为各队的历史和成功提供了更全面的看法。 1. 基础图形及特点 接下来给大家介绍下数据可视化图表的基本类型和选用原则,选用正确的数据可视化的图表。 ① 柱形图(Bar Chart) 优势:柱形图利用柱子的高度,能够比较清晰的反映数据的差异,一般情况下用来反映分类项目之间的比较,也可以用来反映时间趋势。 注意:柱形图的局限在于它仅适用于中小规模的数据集,当数据较多时就不易分辨。一般而言,不要超过10个。 通常来说,柱形图的横轴是时间维度,用户习惯性认为存在时间趋势。如果遇到横轴不是时间维度的情况,建议用颜色区分每根柱子。 衍生...</p> The post <a href="http://www.mobabel.net/%e8%bd%ac%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e5%9b%be%e8%a1%a8%e9%80%89%e5%9e%8b/">[转]数据可视化图表选型</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p>常听到一句话,“能用图描述的就不用表,能用表就不用文字”。这句话也直接的表明了:在认知上,大家对于图形的敏感度远比文字高。</p> <p>但同时我们也面临着这样一些问题:</p> <section class="" data-style-type="6" data-tools="新媒体排版" data-id="9166"> <section class="" data-source="bj.96weixin.com"> <section> <section> <section class="" data-css="background-color: rgb(0, 128, 0);box-sizing: border-box;height: 4px;width: 50px"></section> </section> <section data-css="background-color: rgb(239, 239, 239);box-sizing: border-box;margin-top: -4px;padding: 10px"> <section> <section>写 PPT、做 demo 时,心中有万千想法和海量数据想要去展现,但总是最后还是以文字和枯燥的图表堆叠呈现了出来,苦于怎么把这些数据展现的直观、性感、一看就懂。这时候,在心里怎么想和手上怎么画之间,差了一座“理解图表内涵”的桥梁了。</p> </section> </section> </section> </section> </section> </section> <p><span id="more-5844"></span></p> <p>常见的图表选择原则文章有很多,这里只做简单总结,重点是从逆向来看常见场景选用以及按照数据关系选用,最后是使用中会遇到的一些常见问题。本文将分为以下两部分:</p> <ul class="list-paddingleft-2"> <li>两个优秀的数据可视化案例</li> <li>基础图形及特点</li> <li>如何选用图标</li> <li>常见的问题</li> </ul> <p>作为视觉动物的我们,不妨先来看看惊艳全球的一些数据可视化的例子(原文链接)。</p> <p dir="ltr"><strong>两个优秀的数据可视化案例</strong></p> <hr /> <p dir="ltr"><strong>A. </strong>按年龄段分布的美国人口百分比:</p> <p><img fetchpriority="high" decoding="async" class="alignnone size-full wp-image-5846" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095711.gif" width="525" height="500" /></p> <p>这个 GIF 动画,显示了随着时间推移的人口统计数量的变化。这是如何以令人信服的方式呈现一种单一的数据的好榜样。</p> <p> </p> <p><strong>B. </strong>最有价值的运动队</p> <p><img decoding="async" class="alignnone size-full wp-image-5847" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095719.jpg" width="640" height="357" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095719.jpg 640w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095719-300x167.jpg 300w" sizes="(max-width: 640px) 100vw, 640px" /></p> <p>这是通过叠加数据来讲述深层故事的一个例子。</p> <p>这个交互由 ColumnFive 设计,受福布斯“2014年最具价值的运动队50强”名单得到的启发。但是它不仅将列表可视化,用户还可以通过它看到每支队伍参赛的时间以及夺得总冠军的数量。这为各队的历史和成功提供了更全面的看法。</p> <h1 id='1-基础图形及特点' id="boomdevs_1" dir="ltr" >1. <strong>基础图形及特点</strong></h1> <hr /> <p dir="ltr">接下来给大家介绍下数据可视化图表的基本类型和选用原则,选用正确的数据可视化的图表。</p> <p><strong>① 柱形图(Bar Chart)</strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5848" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095725.jpg" width="1080" height="490" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095725.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095725-300x136.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095725-768x348.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095725-1024x465.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p><strong>优势:</strong>柱形图利用柱子的高度,能够比较清晰的反映数据的差异,一般情况下用来反映分类项目之间的比较,也可以用来反映时间趋势。</p> <p><strong>注意:</strong>柱形图的局限在于它仅适用于中小规模的数据集,当数据较多时就不易分辨。一般而言,不要超过10个。</p> <p>通常来说,柱形图的横轴是时间维度,用户习惯性认为存在时间趋势。如果遇到横轴不是时间维度的情况,建议用颜色区分每根柱子。</p> <p><strong>衍生 – 堆叠柱状图:</strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5849" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095732.png" width="695" height="416" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095732.png 695w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095732-300x180.png 300w" sizes="(max-width: 695px) 100vw, 695px" /></p> <p>不仅可以直观的看出每个系列的值,还能够反映出系列的总和,尤其是当需要看某一单位的综合以及各系列值的比重时,比如:1-8月伦敦和柏林房产交易笔数(万)。</p> <p> </p> <p><strong>② 条形图(Bar Chart)</strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5850" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095739.png" width="704" height="432" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095739.png 704w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095739-300x184.png 300w" sizes="(max-width: 704px) 100vw, 704px" /></p> <p><strong>优势:</strong>条形图用来反映分类项目之间的比较,适合应用于跨类别比较数据。在我们需要比较项类的大小、高低时适合使用条形图。</p> <p><strong>③ 折线图(Line Chart)</strong></p> <p><img decoding="async" class="rich_pages img_loading" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" crossorigin="anonymous" data-ratio="0.6085714285714285" data-s="300,640" data-src="https://mmbiz.qpic.cn/mmbiz_png/qdzZBE73hWv3P1NgUjEtnhvPVuMCjibe81j1LT1fbCmZcFESEUXj5RicL9ClkyozFicDYszWuZRr2bFJOXq0gGPog/640?wx_fmt=png" data-type="png" data-w="700" /><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5851" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095746.png" width="700" height="426" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095746.png 700w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095746-300x183.png 300w" sizes="(max-width: 700px) 100vw, 700px" /></p> <p><strong>优势:</strong>折线图用来反映随时间变化的趋势。当我们需要描述事物随时间维度的变化时常常需要使用该图形。</p> <p><strong>衍生 – 光滑折线图(Smooth line chart)</strong>:</p> <p>假如关注的是数据反映的整体趋势,光滑折线图最适合,尤其是当数据波动较大时,采用折线图会显得很乱。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5852" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095753.png" width="705" height="408" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095753.png 705w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095753-300x174.png 300w" sizes="(max-width: 705px) 100vw, 705px" /></p> <p><strong>衍生 – 面积图(Area chart):</strong></p> <p>折线图下方填充阴影,构成面积图,如果有两个或以上折线图,在各自折线的下方填充不同颜色的阴影,构成堆积面积图,便于了解折线的相对占比</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5853" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095800.png" width="720" height="387" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095800.png 720w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095800-300x161.png 300w" sizes="(max-width: 720px) 100vw, 720px" /></p> <p> </p> <p><strong>④ 饼图(Pie Chart)</strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5854" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095807.png" width="499" height="398" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095807.png 499w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095807-300x239.png 300w" sizes="(max-width: 499px) 100vw, 499px" /></p> <p><strong>注意:</strong>饼图是一种应该避免使用的图表,因为肉眼对面积大小不敏感。是最容易被误用的。但在具体反映某个比重的时候,配上具体数值,会有较好的效果。</p> <p>在需要描述某一部分占总体的百分比时,适合使用饼图。例如:占据公司全部资金一半的两个渠道;某公司员工的男女比例等。</p> <p>而需要比较数据时,尤其是比较两个以上整体的成分时,请务必使用条形图或柱形图,切勿要求看图人将扇形转换成数据在饼图间相互比较,因为人的肉眼对面积大小不敏感,会导致对数据的误读。</p> <p>另外,为了使饼图发挥最大作用,在使用中一般不宜超过6个部分,如需要表达6个以上的部分,也请使用条形图,扇形边个数过多,会导致饼图分块的意义解释过于困难。不要出现下面<img src="https://s.w.org/images/core/emoji/15.0.3/72x72/1f447.png" alt="👇" class="wp-smiley" style="height: 1em; max-height: 1em;" />这种饼图。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5855" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095814.jpg" width="874" height="731" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095814.jpg 874w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095814-300x251.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095814-768x642.jpg 768w" sizes="(max-width: 874px) 100vw, 874px" /></p> <p> </p> <p><strong>⑤ 散点图(Scatter Chart)</strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5856" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095822.jpg" width="831" height="397" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095822.jpg 831w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095822-300x143.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095822-768x367.jpg 768w" sizes="(max-width: 831px) 100vw, 831px" /></p> <p>散点图的数据为三维数据,使用两组数据构成多个坐标点,分析坐标点的分布情况,判断两个变量之间的关联或分布趋势。</p> <p>可以用颜色区分系列,也可以用散点大小定第三维度,这就衍生图出了气泡图。</p> <p> </p> <p><strong>⑥ 气泡图(Bubble chart)</strong></p> <p><img decoding="async" class="rich_pages img_loading" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" crossorigin="anonymous" data-ratio="0.7037037037037037" data-s="300,640" data-src="https://mmbiz.qpic.cn/mmbiz_png/qdzZBE73hWv3P1NgUjEtnhvPVuMCjibe8ljsH8ycE8LTSasffpmyBC072nSm6zLhZUYaBMpdUUjPAldl1MC5uZQ/640?wx_fmt=png" data-type="png" data-w="864" /><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5857" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095827.jpg" width="864" height="608" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095827.jpg 864w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095827-300x211.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095827-768x540.jpg 768w" sizes="(max-width: 864px) 100vw, 864px" /></p> <p>气泡图是散点图的一种衍生,通过每个点的面积大小来衡量第三维度,适合三维数据的对比,且需要强调第三维,超过三维就搞不定。</p> <p><strong>衍生 – 力学气泡图(Mechanical bubble chart) </strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5858" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095836.jpg" width="822" height="485" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095836.jpg 822w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095836-300x177.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095836-768x453.jpg 768w" sizes="(max-width: 822px) 100vw, 822px" /></p> <p><strong>⑦ 雷达图(radar chart)</strong></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5859" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095842.jpg" width="534" height="402" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095842.jpg 534w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095842-300x226.jpg 300w" sizes="(max-width: 534px) 100vw, 534px" /></p> <p>雷达图适用于多维数据(四维以上),且每个维度必须可以排序。数据点一般6个左右,太多的话辨别起来有困难。</p> <p> </p> <h1 id='2-如何选用图标' id="boomdevs_2" dir="ltr" >2. <strong>如何选用图标</strong></h1> <hr /> <p dir="ltr">在讲完了每种图表的优势和缺点,使用禁忌外,接下来就要为大家介绍:</p> <p> </p> <p><strong>① 按应用场景选择</strong></p> <p>虽然图表种类繁多,但是基于使用场景大致可以分为以下几种情况。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5860" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095847.jpg" width="1080" height="591" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095847.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095847-300x164.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095847-768x420.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095847-1024x560.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>基于这个大致的应用场景,可以初步选出可用的可视化图,但对于细微的选择差异,在下面会进一步的阐述;对于单一的可视化图无法满足需要时,就需要考虑组合展示,这里暂且不过多谈。</p> <p><strong>② 按数据关系选择</strong></p> <p>根据可视化专家 Andrew Abela 对该数据关系分类方式的提炼,他提出将图表展示的数据关系分为四类:比较、分布、构成和联系。下面对这四种关系以及应用举例和对应的可视化解决方案做了简要的分析。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5861" src="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095857.jpg" width="707" height="526" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095857.jpg 707w, http://www.mobabel.net/wp-content/uploads/2019/06/30/4ffce04d92a4d6cb21c1494cdfcd6dc1_095857-300x223.jpg 300w" sizes="(max-width: 707px) 100vw, 707px" /></p> <p>大部分情况下,我们根据这份选择指南按图索骥就可以找到,方便又轻松,在实际应用中,也存在需要展示多种数据关系的情况,那么对应的图表类型也是每种关系对应的基本图形的综合运用。例如多个时间点上构成的比较等。</p> <p> </p> <p dir="ltr"><strong>常见问题</strong></p> <hr /> <p dir="ltr">最后整理了一些常见问题,供大家在实际操作中使用。</p> <p> </p> <p><strong>Q: </strong>柱形图和条形图都可以表示分类比较,那两者在使用上有何差异呢?</p> <p><em><strong>A</strong></em><strong>: </strong>当所比较项目的标签文本比较长时,柱形图的横轴下的标签会出现重叠或者倾斜,且占用空间大,影响阅读者的目光移动。所以在表示分类时,如项目数量较少,使用柱形图或条形图均可,如项目_数量较多_,则建议使用_条形图_。</p> <p> </p> <p><strong>Q: </strong>柱形图和折线图都可以表示时间序列的趋势,如何选择?</p> <p><em><strong>A</strong></em><strong>: </strong>一般来说,建议使用折线图反映趋势变化。柱形图强调各数据点值之间的差异,折线图则强调起伏变化的趋势;柱形图更适于表现离散型的时间序列,_折线图适合表现连续型的时间序列_。所以当时间序列的_数据点较少时,可以使用柱形图_,而当数据点较多时,则建议使用折线图。</p> <p> </p> <p><strong>Q: </strong>面积图和折线图都可以表示时间序列的趋势,两者之间如何选择?</p> <p><em><strong>A</strong></em><strong>: </strong>当只展示一个度量数据的趋势时,两者完全等价,都可以使用,通常使用折线图更多。</p> <p>但是,当在大型会议室展示数据时,即读图人离图表可能较远的情况,使用面积图能让后排的人看的更清楚。当比较多个度量数据的趋势时,建议使用折线图。如使用面积图,则存在数据序列之间相互遮挡的情况,除了靠近横轴的那个数据序列外,很难观察出其他数据序列的变化趋势。</p> <p><strong>Q: </strong>是不是应该避免使用饼图,能不用就不用?</p> <p><em><strong>A</strong></em><strong>: </strong>从精确比较数据的角度来说,条形图的确更易于比较数据点之间的差异,但每种图表都有它的长处和适用场景,饼图能给我们一种整体和构成的印象,适用于表达“占比”——看到饼图就让想起100%,这个特点是条形图所没有的。</p> <p>但是要尽量避免并列使用两个及以上的饼图,虽然这种用法很常见。例如如下图,分布展示两个国家在不同年份家庭花销占比,由于读图人很难通过饼图直接、准确的看出各个分类的变化趋势和幅度,需要反复在4个饼图之间比较和判断,如果变化幅度不大,很容易造成误读。所以同样是想表示占比,这种情况使用饼图就不是很有效的图表形式。</p> <p>充分了解每种图表类型的特征,针对于实际的使用场景,判定其数据关系,结合场景判定图和图表类型选择指南,从而有效的传递数据信息,让你的数据更加直白,make sense!</p> <p> </p> <p>[source]<a href="https://mp.weixin.qq.com/s/odgBGYkeLRT09XO9Sz_5cQ">数据可视化图表,你选对了吗?</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e5%9b%be%e8%a1%a8%e9%80%89%e5%9e%8b/">[转]数据可视化图表选型</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e6%95%b0%e6%8d%ae%e5%8f%af%e8%a7%86%e5%8c%96%e5%9b%be%e8%a1%a8%e9%80%89%e5%9e%8b/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[转]全文搜索引擎选 ElasticSearch 还是 Solr?</title> <link>http://www.mobabel.net/%e8%bd%ac%e5%85%a8%e6%96%87%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e9%80%89-elasticsearch-%e8%bf%98%e6%98%af-solr%ef%bc%9f/</link> <comments>http://www.mobabel.net/%e8%bd%ac%e5%85%a8%e6%96%87%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e9%80%89-elasticsearch-%e8%bf%98%e6%98%af-solr%ef%bc%9f/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Fri, 21 Jun 2019 15:09:46 +0000</pubDate> <category><![CDATA[Elasticsearch]]></category> <category><![CDATA[ElasticSearch]]></category> <category><![CDATA[Solr]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=5746</guid> <description><![CDATA[<p>最近项目组安排了一个任务,项目中用到了全文搜索,基于全文搜索 Solr,但是该 Solr 搜索云项目不稳定,经常查询不出来数据,需要手动全量同步,而且是其他团队在维护,依赖性太强,导致 Solr 服务一出问题,我们的项目也基本瘫痪,因为所有的依赖查询都无结果数据了。所以考虑开发一个适配层,如果 Solr 搜索出问题,自动切换到新的搜索–ES。 其实可以通过 Solr 集群或者服务容错等设计来解决该问题。但是先不考虑本身设计的合理性,领导需要开发,所以我开始踏上了搭建 ES 服务的道路,从零开始,因为之前完全没接触过 ES,所以通过本系列来记录下自己的开发过程。 1. 1|0什么是全文搜索   什么是全文搜索引擎? 百度百科中的定义: 全文搜索引擎是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。 从定义中我们已经可以大致了解全文检索的思路了,为了更详细的说明,我们先从生活中的数据说起。 我们生活中的数据总体分为两种:结构化数据 和 非结构化数据。 结构化数据: 指具有固定格式或有限长度的数据,如数据库,元数据等。 非结构化数据: 非结构化数据又可称为全文数据,指不定长或无固定格式的数据,如邮件,word文档等。 当然有的地方还会有第三种:半结构化数据,如XML,HTML等,当根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。 根据两种数据分类,搜索也相应的分为两种:结构化数据搜索和非结构化数据搜索。 对于结构化数据,我们一般都是可以通过关系型数据库(mysql,oracle等)的 table 的方式存储和搜索,也可以建立索引。 对于非结构化数据,也即对全文数据的搜索主要有两种方法:顺序扫描法,全文检索。 顺序扫描:通过文字名称也可了解到它的大概搜索方式,即按照顺序扫描的方式查询特定的关键字。 例如给你一张报纸,让你找到该报纸中“RNG”的文字在哪些地方出现过。你肯定需要从头到尾把报纸阅读扫描一遍然后标记出关键字在哪些版块出现过以及它的出现位置。...</p> The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%85%a8%e6%96%87%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e9%80%89-elasticsearch-%e8%bf%98%e6%98%af-solr%ef%bc%9f/">[转]全文搜索引擎选 ElasticSearch 还是 Solr?</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<p>最近项目组安排了一个任务,项目中用到了全文搜索,基于全文搜索 Solr,但是该 Solr 搜索云项目不稳定,经常查询不出来数据,需要手动全量同步,而且是其他团队在维护,依赖性太强,导致 Solr 服务一出问题,我们的项目也基本瘫痪,因为所有的依赖查询都无结果数据了。所以考虑开发一个适配层,如果 Solr 搜索出问题,自动切换到新的搜索–ES。</p> <p>其实可以通过 Solr 集群或者服务容错等设计来解决该问题。但是先不考虑本身设计的合理性,领导需要开发,所以我开始踏上了搭建 ES 服务的道路,从零开始,因为之前完全没接触过 ES,所以通过本系列来记录下自己的开发过程。<span id="more-5746"></span></p> <h1 id='1-1|0什么是全文搜索' id="boomdevs_1" id="autoid-0-0-0" class="header__dev" >1. <b class="dev__fe"><i>1</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>0</i></b><b class="dev__developer"><span class="dev__title">什么是全文搜索</span></b></h1> <p> </p> <p>什么是全文搜索引擎?</p> <blockquote><p><strong><a href="https://baike.baidu.com/item/%E5%85%A8%E6%96%87%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E">百度百科中的定义</a></strong>:<br /> 全文搜索引擎是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。</p></blockquote> <p>从定义中我们已经可以大致了解全文检索的思路了,为了更详细的说明,我们先从生活中的数据说起。</p> <p>我们生活中的数据总体分为两种:<strong>结构化数据</strong> 和 <strong>非结构化数据</strong>。</p> <ul> <li><strong>结构化数据</strong>: 指具有固定格式或有限长度的数据,如数据库,元数据等。</li> <li><strong>非结构化数据</strong>: 非结构化数据又可称为全文数据,指不定长或无固定格式的数据,如邮件,word文档等。</li> </ul> <p>当然有的地方还会有第三种:<strong>半结构化数据</strong>,如XML,HTML等,当根据需要可按结构化数据来处理,也可抽取出纯文本按非结构化数据来处理。</p> <p>根据两种数据分类,搜索也相应的分为两种:结构化数据搜索和非结构化数据搜索。</p> <p>对于结构化数据,我们一般都是可以通过关系型数据库(mysql,oracle等)的 table 的方式存储和搜索,也可以建立索引。<br /> 对于非结构化数据,也即对全文数据的搜索主要有两种方法:<strong>顺序扫描法</strong>,<strong>全文检索</strong>。</p> <p><strong>顺序扫描</strong>:通过文字名称也可了解到它的大概搜索方式,即按照顺序扫描的方式查询特定的关键字。<br /> 例如给你一张报纸,让你找到该报纸中“RNG”的文字在哪些地方出现过。你肯定需要从头到尾把报纸阅读扫描一遍然后标记出关键字在哪些版块出现过以及它的出现位置。</p> <p>这种方式无疑是最耗时的最低效的,如果报纸排版字体小,而且版块较多甚至有多份报纸,等你扫描完你的眼睛也差不多了。</p> <p><strong>全文搜索</strong>:对非结构化数据顺序扫描很慢,我们是否可以进行优化?把我们的非结构化数据想办法弄得有一定结构不就行了吗?将非结构化数据中的一部分信息提取出来,重新组织,使其变得有一定结构,然后对此有一定结构的数据进行搜索,从而达到搜索相对较快的目的。这种方式就构成了全文检索的基本思路。这部分从非结构化数据中提取出的然后重新组织的信息,我们称之<strong>索引</strong>。</p> <p>还以读报纸为例,我们想关注最近英雄联盟S8全球总决赛的新闻,假如都是 RNG 的粉丝,如何快速找到 RNG 新闻的报纸和版块呢?全文搜索的方式就是,将所有报纸中所有版块中关键字进行提取,如”EDG”,”RNG”,”FW”,”战队”,”英雄联盟”等。然后对这些关键字建立索引,通过索引我们就可以对应到该关键词出现的报纸和版块。注意区别<a href="https://baike.baidu.com/item/%E7%9B%AE%E5%BD%95%E6%90%9C%E7%B4%A2%E5%BC%95%E6%93%8E/1190176?fr=aladdin">目录搜索引擎</a>。</p> <h1 id='2-2|0为什么要用全文搜索搜索引擎' id="boomdevs_2" id="autoid-1-0-0" class="header__dev" >2. <b class="dev__fe"><i>2</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>0</i></b><b class="dev__developer"><span class="dev__title">为什么要用全文搜索搜索引擎</span></b></h1> <p> </p> <p>之前,有同事问我,为什么要用搜索引擎?我们的所有数据在数据库里面都有,而且 Oracle、SQL Server 等数据库里也能提供查询检索或者聚类分析功能,直接通过数据库查询不就可以了吗?确实,我们大部分的查询功能都可以通过数据库查询获得,如果查询效率低下,还可以通过建数据库索引,优化SQL等方式进行提升效率,甚至通过引入缓存来加快数据的返回速度。如果数据量更大,就可以分库分表来分担查询压力。</p> <p>那为什么还要全文搜索引擎呢?我们主要从以下几个原因分析:</p> <ul> <li><strong>数据类型</strong><br /> 全文索引搜索支持非结构化数据的搜索,可以更好地快速搜索大量存在的任何单词或单词组的非结构化文本。<br /> 例如 Google,百度类的网站搜索,它们都是根据网页中的关键字生成索引,我们在搜索的时候输入关键字,它们会将该关键字即索引匹配到的所有网页返回;还有常见的项目中应用日志的搜索等等。对于这些非结构化的数据文本,关系型数据库搜索不是能很好的支持。</li> <li><strong>索引的维护</strong><br /> 一般传统数据库,全文检索都实现的很鸡肋,因为一般也没人用数据库存文本字段。进行全文检索需要扫描整个表,如果数据量大的话即使对SQL的语法优化,也收效甚微。建立了索引,但是维护起来也很麻烦,对于 insert 和 update 操作都会重新构建索引。</li> </ul> <p>什么时候使用全文搜索引擎:</p> <ol> <li>搜索的数据对象是大量的非结构化的文本数据。</li> <li>文件记录量达到数十万或数百万个甚至更多。</li> <li>支持大量基于交互式文本的查询。</li> <li>需求非常灵活的全文搜索查询。</li> <li>对高度相关的搜索结果的有特殊需求,但是没有可用的关系数据库可以满足。</li> <li>对不同记录类型、非文本数据操作或安全事务处理的需求相对较少的情况。</li> </ol> <h1 id='3-3|0lucene-solr-elasticsearch' id="boomdevs_3" id="autoid-2-0-0" class="header__dev" >3. <b class="dev__fe"><i>3</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>0</i></b><b class="dev__developer"><span class="dev__title">Lucene,Solr, ElasticSearch ?</span></b></h1> <p> </p> <p>现在主流的搜索引擎大概就是:Lucene,Solr,ElasticSearch。<br /> <img loading="lazy" decoding="async" class="alignnone size-full wp-image-5747" src="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011729083-1478361739_150953.png" width="926" height="474" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011729083-1478361739_150953.png 926w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011729083-1478361739_150953-300x154.png 300w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011729083-1478361739_150953-768x393.png 768w" sizes="(max-width: 926px) 100vw, 926px" /></p> <p>它们的索引建立都是根据<strong>倒排索引</strong>的方式生成索引,何谓倒排索引?</p> <blockquote><p>维基百科<br /> 倒排索引(英语:Inverted index),也常被称为反向索引、置入档案或反向档案,是一种索引方法,被用来存储在全文搜索下某个单词在一个文档或者一组文档中的存储位置的映射。它是文档检索系统中最常用的数据结构。</p></blockquote> <h2 id='3-1-3|1lucene' id="boomdevs_4" id="autoid-3-0-0" class="header__dev" >3-1. <b class="dev__fe"><i>3</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>1</i></b><b class="dev__developer"><span class="dev__title">Lucene</span></b></h2> <p> </p> <p>Lucene是一个Java全文搜索引擎,完全用Java编写。Lucene不是一个完整的应用程序,而是一个代码库和API,可以很容易地用于向应用程序添加搜索功能。</p> <p>Lucene通过简单的API提供强大的功能:</p> <p><strong>可扩展的高性能索引</strong></p> <ul> <li>在现代硬件上超过150GB /小时</li> <li>小RAM要求 – 只有1MB堆</li> <li>增量索引与批量索引一样快</li> <li>索引大小约为索引文本大小的20-30%</li> </ul> <p><strong>强大,准确,高效的搜索算法</strong></p> <ul> <li>排名搜索 – 首先返回最佳结果</li> <li>许多强大的查询类型:短语查询,通配符查询,邻近查询,范围查询等</li> <li>现场搜索(例如标题,作者,内容)</li> <li>按任何字段排序</li> <li>使用合并结果进行多索引搜索</li> <li>允许同时更新和搜索</li> <li>灵活的分面,突出显示,连接和结果分组</li> <li>快速,内存效率和错误容忍的建议</li> <li>可插拔排名模型,包括矢量空间模型和Okapi BM25</li> <li>可配置存储引擎(编解码器)</li> </ul> <p><strong>跨平台解决方案</strong></p> <ul> <li>作为Apache许可下的开源软件提供 ,允许您在商业和开源程序中使用Lucene</li> <li>100%-pure Java</li> <li>可用的其他编程语言中的实现是索引兼容的</li> </ul> <p><strong>Apache软件基金会</strong><br /> 在Apache软件基金会提供的开源软件项目的Apache社区的支持。</p> <p>但是Lucene只是一个框架,要充分利用它的功能,需要使用JAVA,并且在程序中集成Lucene。需要很多的学习了解,才能明白它是如何运行的,熟练运用Lucene确实非常复杂。</p> <h2 id='3-2-3|2solr' id="boomdevs_5" id="autoid-3-1-0" class="header__dev" >3-2. <b class="dev__fe"><i>3</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>2</i></b><b class="dev__developer"><span class="dev__title">Solr</span></b></h2> <p> </p> <p>Apache Solr是一个基于名为Lucene的Java库构建的开源搜索平台。它以用户友好的方式提供Apache Lucene的搜索功能。作为一个行业参与者近十年,它是一个成熟的产品,拥有强大而广泛的用户社区。它提供分布式索引,复制,负载平衡查询以及自动故障转移和恢复。如果它被正确部署然后管理得好,它就能够成为一个高度可靠,可扩展且容错的搜索引擎。很多互联网巨头,如Netflix,eBay,Instagram和亚马逊(CloudSearch)都使用Solr,因为它能够索引和搜索多个站点。</p> <p>主要功能列表包括:</p> <ul> <li>全文搜索</li> <li>突出</li> <li>分面搜索</li> <li>实时索引</li> <li>动态群集</li> <li>数据库集成</li> <li>NoSQL功能和丰富的文档处理(例如Word和PDF文件)</li> </ul> <h2 id='3-3-3|3elasticsearch' id="boomdevs_6" id="autoid-3-2-0" class="header__dev" >3-3. <b class="dev__fe"><i>3</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>3</i></b><b class="dev__developer"><span class="dev__title">ElasticSearch</span></b></h2> <p> </p> <p>Elasticsearch是一个开源(Apache 2许可证),是一个基于Apache Lucene库构建的RESTful搜索引擎。</p> <p>Elasticsearch是在Solr之后几年推出的。它提供了一个分布式,多租户能力的全文搜索引擎,具有HTTP Web界面(REST)和无架构JSON文档。Elasticsearch的官方客户端库提供Java,Groovy,PHP,Ruby,Perl,Python,.NET和Javascript。</p> <p>分布式搜索引擎包括可以划分为分片的索引,并且每个分片可以具有多个副本。每个Elasticsearch节点都可以有一个或多个分片,其引擎也可以充当协调器,将操作委派给正确的分片。</p> <p>Elasticsearch可通过近实时搜索进行扩展。其主要功能之一是多租户。</p> <p>主要功能列表包括:</p> <ul> <li>分布式搜索</li> <li>多租户</li> <li>分析搜索</li> <li>分组和聚合</li> </ul> <h1 id='4-4|0elasticsearch-vs-solr的选择' id="boomdevs_7" id="autoid-3-3-0" class="header__dev" >4. <b class="dev__fe"><i>4</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>0</i></b><b class="dev__developer"><span class="dev__title">Elasticsearch vs. Solr的选择</span></b></h1> <p> </p> <p>由于Lucene的复杂性,一般很少会考虑它作为搜索的第一选择,排除一些公司需要自研搜索框架,底层需要依赖Lucene。所以这里我们重点分析 Elasticsearch 和 Solr。</p> <p>Elasticsearch vs. Solr。哪一个更好?他们有什么不同?你应该使用哪一个?<br /> <img loading="lazy" decoding="async" class="alignnone size-full wp-image-5748" src="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011755612-1426970369_151014.png" width="1024" height="372" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011755612-1426970369_151014.png 1024w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011755612-1426970369_151014-300x109.png 300w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020011755612-1426970369_151014-768x279.png 768w" sizes="(max-width: 1024px) 100vw, 1024px" /></p> <h2 id='4-1-4|1历史比较' id="boomdevs_8" id="autoid-4-0-0" class="header__dev" >4-1. <b class="dev__fe"><i>4</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>1</i></b><b class="dev__developer"><span class="dev__title">历史比较</span></b></h2> <p> </p> <p>Apache Solr是一个成熟的项目,拥有庞大而活跃的开发和用户社区,以及Apache品牌。Solr于2006年首次发布到开源,长期以来一直占据着搜索引擎领域,并且是任何需要搜索功能的人的首选引擎。它的成熟转化为丰富的功能,而不仅仅是简单的文本索引和搜索; 如分面,分组,强大的过滤,可插入的文档处理,可插入的搜索链组件,语言检测等。</p> <p>Solr 在搜索领域占据了多年的主导地位。然后,在2010年左右,Elasticsearch成为市场上的另一种选择。那时候,它远没有Solr那么稳定,没有Solr的功能深度,没有思想分享,品牌等等。</p> <p>Elasticsearch虽然很年轻,但它也自己的一些优势,Elasticsearch 建立在更现代的原则上,针对更现代的用例,并且是为了更容易处理大型索引和高查询率而构建的。此外,由于它太年轻,没有社区可以合作,它可以自由地向前推进,而不需要与其他人(用户或开发人员)达成任何共识或合作,向后兼容,或任何其他更成熟的软件通常必须处理。</p> <p>因此,它在Solr之前就公开了一些非常受欢迎的功能(例如,接近实时搜索,英文:Near Real-Time Search)。从技术上讲,NRT搜索的能力确实来自Lucene,它是 Solr 和 Elasticsearch 使用的基础搜索库。具有讽刺意味的是,因为 Elasticsearch 首先公开了NRT搜索,所以人们将NRT搜索与Elasticsearch 联系在一起,尽管 Solr 和 Lucene 都是同一个 Apache 项目的一部分,因此,人们会首先期望 Solr 具有如此高要求的功能。</p> <h2 id='4-2-4|2特征差异比较' id="boomdevs_9" id="autoid-4-1-0" class="header__dev" >4-2. <b class="dev__fe"><i>4</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>2</i></b><b class="dev__developer"><span class="dev__title">特征差异比较</span></b></h2> <p> </p> <p>这两个搜索引擎都是流行的,先进的的开源搜索引擎。它们都是围绕核心底层搜索库 – Lucene构建的 – 但它们又是不同的。像所有东西一样,每个都有其优点和缺点,根据您的需求和期望,每个都可能更好或更差。Solr和Elasticsearch都在快速发展,所以,话不多说,先来看下它们的差异清单:</p> <table> <thead> <tr class="header"> <th>特征</th> <th>Solr/SolrCloud</th> <th>Elasticsearch</th> </tr> </thead> <tbody> <tr class="odd"> <td>社区和开发者</td> <td>Apache 软件基金和社区支持</td> <td>单一商业实体及其员工</td> </tr> <tr class="even"> <td>节点发现</td> <td>Apache Zookeeper,在大量项目中成熟且经过实战测试</td> <td>Zen内置于Elasticsearch本身,需要专用的主节点才能进行分裂脑保护</td> </tr> <tr class="odd"> <td>碎片放置</td> <td>本质上是静态,需要手动工作来迁移分片,从Solr 7开始 – Autoscaling API允许一些动态操作</td> <td>动态,可以根据群集状态按需移动分片</td> </tr> <tr class="even"> <td>高速缓存</td> <td>全局,每个段更改无效</td> <td>每段,更适合动态更改数据</td> </tr> <tr class="odd"> <td>分析引擎性能</td> <td>非常适合精确计算的静态数据</td> <td>结果的准确性取决于数据放置</td> </tr> <tr class="even"> <td>全文搜索功能</td> <td>基于Lucene的语言分析,多建议,拼写检查,丰富的高亮显示支持</td> <td>基于Lucene的语言分析,单一建议API实现,高亮显示重新计算</td> </tr> <tr class="odd"> <td>DevOps支持</td> <td>尚未完全,但即将到来</td> <td>非常好的API</td> </tr> <tr class="even"> <td>非平面数据处理</td> <td>嵌套文档和父-子支持</td> <td>嵌套和对象类型的自然支持允许几乎无限的嵌套和父-子支持</td> </tr> <tr class="odd"> <td>查询DSL</td> <td>JSON(有限),XML(有限)或URL参数</td> <td>JSON</td> </tr> <tr class="even"> <td>索引/收集领导控制</td> <td>领导者安置控制和领导者重新平衡甚至可以节点上的负载</td> <td>不可能</td> </tr> <tr class="odd"> <td>机器学习</td> <td>内置 – 在流聚合之上,专注于逻辑回归和学习排名贡献模块</td> <td>商业功能,专注于异常和异常值以及时间序列数据</td> </tr> </tbody> </table> <p><a href="http://solr-vs-elasticsearch.com/">这里了解更多</a>。</p> <h2 id='4-3-4|3综合比较' id="boomdevs_10" id="autoid-4-2-0" class="header__dev" >4-3. <b class="dev__fe"><i>4</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>3</i></b><b class="dev__developer"><span class="dev__title">综合比较</span></b></h2> <p> </p> <p>另外,我们在从以下几个方面来分析下:</p> <ul> <li>近几年的流行趋势<br /> 我们查看一下这两种产品的Google搜索趋势。谷歌趋势表明,与 Solr 相比,Elasticsearch具有很大的吸引力,但这并不意味着Apache Solr已经死亡。虽然有些人可能不这么认为,但Solr仍然是最受欢迎的搜索引擎之一,拥有强大的社区和开源支持。</li> </ul> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5749" src="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020012622260-1901329745_151033.png" width="817" height="435" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020012622260-1901329745_151033.png 817w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020012622260-1901329745_151033-300x160.png 300w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181020012622260-1901329745_151033-768x409.png 768w" sizes="(max-width: 817px) 100vw, 817px" /></p> <ul> <li>安装和配置<br /> 与Solr相比,Elasticsearch易于安装且非常轻巧。此外,您可以在几分钟内安装并运行Elasticsearch。<br /> 但是,如果Elasticsearch管理不当,这种易于部署和使用可能会成为一个问题。基于JSON的配置很简单,但如果要为文件中的每个配置指定注释,那么它不适合您。<br /> 总的来说,如果您的应用使用的是JSON,那么Elasticsearch是一个更好的选择。否则,请使用Solr,因为它的schema.xml和solrconfig.xml都有很好的文档记录。</li> <li>社区<br /> Solr拥有更大,更成熟的用户,开发者和贡献者社区。ES虽拥有的规模较小但活跃的用户社区以及不断增长的贡献者社区。<br /> Solr是真正的开源社区代码。任何人都可以为Solr做出贡献,并且根据优点选出新的Solr开发人员(也称为提交者)。Elasticsearch在技术上是开源的,但在精神上却不那么重要。任何人都可以看到来源,任何人都可以更改它并提供贡献,但只有Elasticsearch的员工才能真正对Elasticsearch进行更改。<br /> Solr贡献者和提交者来自许多不同的组织,而Elasticsearch提交者来自单个公司。</li> <li>成熟度<br /> Solr更成熟,但ES增长迅速,我认为它稳定。</li> <li>文档<br /> Solr在这里得分很高。它是一个非常有据可查的产品,具有清晰的示例和API用例场景。 Elasticsearch的文档组织良好,但它缺乏好的示例和清晰的配置说明。</li> </ul> <h1 id='5-5|0总结' id="boomdevs_11" id="autoid-4-3-0" class="header__dev" >5. <b class="dev__fe"><i>5</i></b><span class="dev__slash">|</span><b class="dev__ux"><i>0</i></b><b class="dev__developer"><span class="dev__title">总结</span></b></h1> <p> </p> <p>那么,到底是Solr还是Elasticsearch?<br /> 有时很难找到明确的答案。无论您选择Solr还是Elasticsearch,首先需要了解正确的用例和未来需求。总结他们的每个属性。</p> <p>记住:</p> <ul> <li>由于易于使用,Elasticsearch在新开发者中更受欢迎。但是,如果您已经习惯了与Solr合作,请继续使用它,因为迁移到Elasticsearch没有特定的优势。</li> <li>如果除了搜索文本之外还需要它来处理分析查询,Elasticsearch是更好的选择。</li> <li>如果需要分布式索引,则需要选择Elasticsearch。对于需要良好可伸缩性和性能的云和分布式环境,Elasticsearch是更好的选择。</li> <li>两者都有良好的商业支持(咨询,生产支持,整合等)</li> <li>两者都有很好的操作工具,尽管Elasticsearch因其易于使用的API而更多地吸引了DevOps人群,因此可以围绕它创建一个更加生动的工具生态系统。</li> <li>Elasticsearch在开源日志管理用例中占据主导地位,许多组织在Elasticsearch中索引它们的日志以使其可搜索。虽然Solr现在也可以用于此目的,但它只是错过了这一想法。</li> <li>Solr仍然更加面向文本搜索。另一方面,Elasticsearch 通常用于过滤和分组 – 分析查询工作负载 – 而不一定是文本搜索。Elasticsearch 开发人员在 Lucene 和 Elasticsearch 级别上投入了大量精力使此类查询更高效(降低内存占用和CPU使用)。因此,对于不仅需要进行文本搜索,而且需要复杂的搜索时间聚合的应用程序,Elasticsearch是一个更好的选择。</li> <li>Elasticsearch更容易上手,一个下载和一个命令就可以启动一切。Solr传统上需要更多的工作和知识,但Solr最近在消除这一点上取得了巨大的进步,现在只需努力改变它的声誉。</li> <li>在性能方面,它们大致相同。我说“大致”,因为没有人做过全面和无偏见的基准测试。对于95%的用例,任何一种选择在性能方面都会很好,剩下的5%需要用它们的特定数据和特定的访问模式来测试这两种解决方案。</li> <li>从操作上讲,Elasticsearch使用起来比较简单 – 它只有一个进程。Solr在其类似Elasticsearch的完全分布式部署模式SolrCloud中依赖于Apache ZooKeeper。ZooKeeper是超级成熟,超级广泛使用等等,但它仍然是另一个活跃的部分。也就是说,如果您使用的是Hadoop,HBase,Spark,Kafka或其他一些较新的分布式软件,您可能已经在组织的某个地方运行ZooKeeper。</li> <li>虽然Elasticsearch内置了类似ZooKeeper的组件Xen,但ZooKeeper可以更好地防止有时在Elasticsearch集群中出现的可怕的裂脑问题。公平地说,Elasticsearch开发人员已经意识到这个问题,并致力于改进Elasticsearch的这个方面。</li> <li>如果您喜欢监控和指标,那么使用Elasticsearch,您将会进入天堂。这个东西比新年前夜在时代广场可以挤压的人有更多的指标!Solr暴露了关键指标,但远不及Elasticsearch那么多。</li> </ul> <p>总之,两者都是功能丰富的搜索引擎,只要设计和实现得当,它们或多或少都能提供相同的性能。本篇文章的总体内容大致如下图,该图由园友<a href="https://www.cnblogs.com/reycg-blog/p/10048815.html#4129414">ReyCG</a>精心绘制并提供。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5750" src="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181202204701087-1491673938_151047.png" width="1266" height="629" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181202204701087-1491673938_151047.png 1266w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181202204701087-1491673938_151047-300x149.png 300w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181202204701087-1491673938_151047-768x382.png 768w, http://www.mobabel.net/wp-content/uploads/2019/06/21/1162587-20181202204701087-1491673938_151047-1024x509.png 1024w" sizes="(max-width: 1266px) 100vw, 1266px" /></p> <p>参考:</p> <ol> <li><a class="uri" href="https://www.datanami.com/2015/01/22/solr-elasticsearch-question/">https://www.datanami.com/2015/01/22/solr-elasticsearch-question/</a></li> <li><a class="uri" href="https://blog.csdn.net/hhx0626/article/details/78095593/">https://blog.csdn.net/hhx0626/article/details/78095593/</a></li> <li><a class="uri" href="https://www.elastic.co/cn/">https://www.elastic.co/cn/</a></li> <li><a class="uri" href="https://logz.io/blog/solr-vs-elasticsearch/">https://logz.io/blog/solr-vs-elasticsearch/</a></li> <li><a class="uri" href="https://sematext.com/blog/solr-vs-elasticsearch-differences/">https://sematext.com/blog/solr-vs-elasticsearch-differences/</a></li> </ol> <p> </p> <p> </p> <p>[source]<a href="https://www.cnblogs.com/jajian/p/9801154.html">全文搜索引擎选 ElasticSearch 还是 Solr?</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%85%a8%e6%96%87%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e9%80%89-elasticsearch-%e8%bf%98%e6%98%af-solr%ef%bc%9f/">[转]全文搜索引擎选 ElasticSearch 还是 Solr?</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e5%85%a8%e6%96%87%e6%90%9c%e7%b4%a2%e5%bc%95%e6%93%8e%e9%80%89-elasticsearch-%e8%bf%98%e6%98%af-solr%ef%bc%9f/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[转]超详细的Elasticsearch高性能优化实践</title> <link>http://www.mobabel.net/%e8%bd%ac%e8%b6%85%e8%af%a6%e7%bb%86%e7%9a%84elasticsearch%e9%ab%98%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96%e5%ae%9e%e8%b7%b5/</link> <comments>http://www.mobabel.net/%e8%bd%ac%e8%b6%85%e8%af%a6%e7%bb%86%e7%9a%84elasticsearch%e9%ab%98%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96%e5%ae%9e%e8%b7%b5/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Tue, 18 Jun 2019 21:25:40 +0000</pubDate> <category><![CDATA[Elasticsearch]]></category> <category><![CDATA[ElasticSearch]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=5562</guid> <description><![CDATA[<p>1. ES 性能调优 ES 的默认配置,是综合了数据可靠性、写入速度、搜索实时性等因素。实际使用时,我们需要根据公司要求,进行偏向性的优化。 1-1. 写优化 假设我们的应用场景要求是,每秒 300 万的写入速度,每条 500 字节左右。 针对这种对于搜索性能要求不高,但是对写入要求较高的场景,我们需要尽可能的选择恰当写优化策略。 综合来说,可以考虑以下几个方面来提升写索引的性能: 加大 Translog Flush ,目的是降低 Iops、Writeblock。 增加 Index Refresh 间隔,目的是减少 Segment Merge 的次数。 调整 Bulk 线程池和队列。 优化节点间的任务分布。 优化 Lucene 层的索引建立,目的是降低 CPU...</p> The post <a href="http://www.mobabel.net/%e8%bd%ac%e8%b6%85%e8%af%a6%e7%bb%86%e7%9a%84elasticsearch%e9%ab%98%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96%e5%ae%9e%e8%b7%b5/">[转]超详细的Elasticsearch高性能优化实践</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<h1 id='1-es-性能调优' id="boomdevs_1" >1. ES 性能调优</h1> <p>ES 的默认配置,是综合了数据可靠性、写入速度、搜索实时性等因素。实际使用时,我们需要根据公司要求,进行偏向性的优化。</p> <section> <section> <section> <section></section> <h2 id='1-1-写优化' id="boomdevs_2" >1-1. <strong>写优化</strong></h2> </section> </section> </section> <p>假设我们的应用场景要求是,每秒 300 万的写入速度,每条 500 字节左右。</p> <p>针对这种对于搜索性能要求不高,但是对写入要求较高的场景,我们需要尽可能的选择恰当写优化策略。</p> <p>综合来说,可以考虑以下几个方面来提升写索引的性能:</p> <ul class="list-paddingleft-2"> <li>加大 Translog Flush ,目的是降低 Iops、Writeblock。</li> <li>增加 Index Refresh 间隔,目的是减少 Segment Merge 的次数。</li> <li>调整 Bulk 线程池和队列。</li> <li>优化节点间的任务分布。</li> <li>优化 Lucene 层的索引建立,目的是降低 CPU 及 IO。</li> </ul> <h3 id='1-1-1-①批量提交' id="boomdevs_3" data-anchor-id="43ez" >1-1-1. <strong>①批量提交</strong></h3> <p>ES 提供了 Bulk API 支持批量操作,当我们有大量的写任务时,可以使用 Bulk 来进行批量写入。</p> <p>每次提交的数据量为多少时,能达到最优的性能,主要受到文件大小、网络情况、数据类型、集群状态等因素影响。</p> <p><strong>通用的策略如下:</strong>Bulk 默认设置批量提交的数据量不能超过 100M。数据条数一般是根据文档的大小和服务器性能而定的,但是单次批处理的数据大小应从 5MB~15MB 逐渐增加,当性能没有提升时,把这个数据量作为最大值。</p> <p>我们可以跟着,感受一下 Bulk 接口,如下所示:</p><pre class="crayon-plain-tag">$ vi request $ cat request { "index" : { "_index" : "chandler","_type": "test", "_id" : "1" } } { "name" : "钱丁君","age": "18" } $ curl -s -H "Content-Type: application/json" -XPOST localhost:9200/_bulk --data-binary @request; echo {"took":214,"errors":false,"items":[{"index":{"_index":"chandler","_type":"test","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1,"status":201}}]} $ curl -XGET localhost:9200/chandler/test/1?pretty { "_index" : "chandler", "_type" : "test", "_id" : "1", "_version" : 1, "found" : true, "_source" : { "name" : "钱丁君", "age" : "18" } }</pre><p>Bulk 不支持 Gget 操作,因为没什么用处。</p> <h3 id='1-1-2-②优化存储设备' id="boomdevs_4" data-anchor-id="wqea" >1-1-2. <strong>②优化存储设备</strong></h3> <p>ES 是一种密集使用磁盘的应用,在段合并的时候会频繁操作磁盘,所以对磁盘要求较高,当磁盘速度提升之后,集群的整体性能会大幅度提高。</p> <p>磁盘的选择,提供以下几点建议:</p> <ul class="list-paddingleft-2"> <li><strong>使用固态硬盘(Solid State Disk)替代机械硬盘。</strong>SSD 与机械磁盘相比,具有高效的读写速度和稳定性。</li> <li><strong>使用 RAID 0。</strong>RAID 0 条带化存储,可以提升磁盘读写效率。</li> <li>在 ES 的服务器上挂载多块硬盘。使用多块硬盘同时进行读写操作提升效率,在配置文件 ES 中设置多个存储路径,如下所示:</li> </ul> <p></p><pre class="crayon-plain-tag">path.data:/path/to/data1,/path/to/data2</pre><p>避免使用 NFS(Network File System)等远程存储设备,网络的延迟对性能的影响是很大的。</p> <h3 id='1-1-3-③合理使用合并' id="boomdevs_5" data-anchor-id="si45" >1-1-3. <strong>③合理使用合并</strong></h3> <p>Lucene 以段的形式存储数据。当有新的数据写入索引时,Lucene 就会自动创建一个新的段。</p> <p>随着数据量的变化,段的数量会越来越多,消耗的多文件句柄数及 CPU 就越多,查询效率就会下降。</p> <p>由于 Lucene 段合并的计算量庞大,会消耗大量的 I/O,所以 ES 默认采用较保守的策略,让后台定期进行段合并,如下所述:</p> <ul class="list-paddingleft-2"> <li><strong>索引写入效率下降:</strong>当段合并的速度落后于索引写入的速度时,ES 会把索引的线程数量减少到 1。这样可以避免出现堆积的段数量爆发,同时在日志中打印出“now throttling indexing”INFO 级别的“警告”信息。</li> <li><strong>提升段合并速度:</strong>ES 默认对段合并的速度是 20m/s,如果使用了 SSD,我们可以通过以下的命令将这个合并的速度增加到 100m/s。</li> </ul> <p></p><pre class="crayon-plain-tag">PUT /_cluster/settings { "persistent" : { "indices.store.throttle.max_bytes_per_sec" : "100mb" } }</pre><p></p> <h3 id='1-1-4-④减少-refresh-的次数' id="boomdevs_6" data-anchor-id="7pds" >1-1-4. <strong>④减少 Refresh 的次数</strong></h3> <p>Lucene 在新增数据时,采用了延迟写入的策略,默认情况下索引的 refresh_interval 为 1 秒。</p> <p>Lucene 将待写入的数据先写到内存中,超过 1 秒(默认)时就会触发一次 Refresh,然后 Refresh 会把内存中的的数据刷新到操作系统的文件缓存系统中。</p> <p>如果我们对搜索的实效性要求不高,可以将 Refresh 周期延长,例如 30 秒。</p> <p>这样还可以有效地减少段刷新次数,但这同时意味着需要消耗更多的Heap内存。</p> <p>如下所示:</p><pre class="crayon-plain-tag">index.refresh_interval:30s</pre><p></p> <h3 id='1-1-5-⑤加大-flush-设置' id="boomdevs_7" data-anchor-id="yoz8" >1-1-5. <strong>⑤加大 Flush 设置</strong></h3> <p>Flush 的主要目的是把文件缓存系统中的段持久化到硬盘,当 Translog 的数据量达到 512MB 或者 30 分钟时,会触发一次 Flush。</p> <p>index.translog.flush_threshold_size 参数的默认值是 512MB,我们进行修改。</p> <p>增加参数值意味着文件缓存系统中可能需要存储更多的数据,所以我们需要为操作系统的文件缓存系统留下足够的空间。</p> <h3 id='1-1-6-⑥减少副本的数量' id="boomdevs_8" data-anchor-id="4dts" >1-1-6. <strong>⑥减少副本的数量</strong></h3> <p>ES 为了保证集群的可用性,提供了 Replicas(副本)支持,然而每个副本也会执行分析、索引及可能的合并过程,所以 Replicas 的数量会严重影响写索引的效率。</p> <p>当写索引时,需要把写入的数据都同步到副本节点,副本节点越多,写索引的效率就越慢。</p> <p>如果我们需要大批量进行写入操作,可以先禁止 Replica 复制,设置 index.number_of_replicas: 0 关闭副本。在写入完成后,Replica 修改回正常的状态。</p> <h2 id='1-2-读优化' id="boomdevs_9" >1-2. <strong>读优化</strong></h2> <h3 id='1-2-1-①避免大结果集和深翻' id="boomdevs_10" data-anchor-id="rwss" >1-2-1. <strong>①避免大结果集和深翻</strong></h3> <p>在上一篇讲到了集群中的查询流程,例如,要查询从 from 开始的 size 条数据,则需要在每个分片中查询打分排名在前面的 from+size 条数据。</p> <p>协同节点将收集到的n×(from+size)条数据聚合,再进行一次排序,然后从 from+size 开始返回 size 条数据。</p> <p>当 from、size 或者 n 中有一个值很大的时候,需要参加排序的数量也会增长,这样的查询会消耗很多 CPU 资源,从而导致效率的降低。</p> <p>为了提升查询效率,ES 提供了 Scroll 和 Scroll-Scan 这两种查询模式。</p> <p data-anchor-id="zk3l"><strong>Scroll:是为检索大量的结果而设计的。例如,我们需要查询 1~100 页的数据,每页 100 条数据。</strong></p> <p>如果使用 Search 查询:每次都需要在每个分片上查询得分最高的 from+100 条数据,然后协同节点把收集到的 n×(from+100)条数据聚合起来再进行一次排序。</p> <p>每次返回 from+1 开始的 100 条数据,并且要重复执行 100 次。</p> <p>如果使用 Scroll 查询:在各个分片上查询 10000 条数据,协同节点聚合 n×10000 条数据进行合并、排序,并将排名前 10000 的结果快照起来。这样做的好处是减少了查询和排序的次数。</p> <p>Scroll 初始查询的命令是:</p><pre class="crayon-plain-tag">$ vim scroll $ cat scroll { "query": { "match": { "name": "钱丁君" } }, "size":20 } $ curl -s -H "Content-Type: application/json; charset=UTF-8" -XGET localhost:9200/chandler/test/_search?scroll=2m --data-binary @scroll; echo {"_scroll_id":"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAAGFlB6Y3QtNk9oUmdpc09Tb21rX2NXQXcAAAAAAAAABxZQemN0LTZPaFJnaXNPU29ta19jV0F3AAAAAAAAAAgWUHpjdC02T2hSZ2lzT1NvbWtfY1dBdwAAAAAAAAAJFlB6Y3QtNk9oUmdpc09Tb21rX2NXQXcAAAAAAAAAChZQemN0LTZPaFJnaXNPU29ta19jV0F3","took":14,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":1,"max_score":0.8630463,"hits":[{"_index":"chandler","_type":"test","_id":"1","_score":0.8630463,"_source":{ "name" : "钱丁君","age": "18" }}]}}</pre><p>以上查询语句的含义是,在 chandler 索引的 test type 里查询字段 name 包含“钱丁君”的数据。</p> <p>scroll=2m 表示下次请求的时间不能超过 2 分钟,size 表示这次和后续的每次请求一次返回的数据条数。</p> <p>在这次查询的结果中除了返回了查询到的结果,还返回了一个 scroll_id,可以把它作为下次请求的参数。</p> <p>再次请求的命令,如下所示:</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5626" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212543.jpg" width="1080" height="558" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212543.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212543-300x155.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212543-768x397.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212543-1024x529.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>因为这次并没有到分片里查询数据,而是直接在生成的快照里面以游标的形式获取数据。</p> <p>所以这次查询并没有包含 index 和 type,也没有查询条件:</p> <ul class="list-paddingleft-2"> <li><strong>“scroll”: “2m”:</strong>指本次请求的时间不能超过 2 分钟。</li> <li><strong>scroll_id:</strong>是上次查询时返回的 scroll_id。</li> </ul> <p data-anchor-id="h9a6"><strong>Scroll-Scan:Scroll 是先做一次初始化搜索把所有符合搜索条件的结果缓存起来生成一个快照,然后持续地、批量地从快照里拉取数据直到没有数据剩下。</strong></p> <p data-anchor-id="h9a6">而这时对索引数据的插入、删除、更新都不会影响遍历结果,因此 Scroll 并不适合用来做实时搜索。</p> <p data-anchor-id="h9a6">其思路和使用方式与 Scroll 非常相似,但是 Scroll-Scan 关闭了 Scroll 中最耗时的文本相似度计算和排序,使得性能更加高效。</p> <p>为了使用 Scroll-Scan,需要执行一个初始化搜索请求,将 search_type 设置成 Scan,告诉 ES 集群不需要文本相似计算和排序,只是按照数据在索引中顺序返回结果集:</p><pre class="crayon-plain-tag">$ vi scroll $ cat scroll { "query": { "match": { "name": "钱丁君" } }, "size":20, "sort": [ "_doc" ] } $ curl -H "Content-Type: application/json; charset=UTF-8" -XGET 'localhost:9200/chandler/test/_search?scroll=2m&pretty=true' --data-binary @scroll { "_scroll_id" : "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAABWFlB6Y3QtNk9oUmdpc09Tb21rX2NXQXcAAAAAAAAAVxZQemN0LTZPaFJnaXNPU29ta19jV0F3AAAAAAAAAFgWUHpjdC02T2hSZ2lzT1NvbWtfY1dBdwAAAAAAAABZFlB6Y3QtNk9oUmdpc09Tb21rX2NXQXcAAAAAAAAAWhZQemN0LTZPaFJnaXNPU29ta19jV0F3", "took" : 3, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1, "max_score" : null, "hits" : [ { "_index" : "chandler", "_type" : "test", "_id" : "1", "_score" : null, "_source" : { "name" : "钱丁君", "age" : "18" }, "sort" : [ 0 ] } ] } }</pre><p>注意:Elasticsearch 2.1.0 版本之后移除了 search_type=scan,使用 “sort”: [ “_doc”] 进行代替。</p> <p>Scroll 和 Scroll-Scan 有一些差别,如下所示:</p> <ul class="list-paddingleft-2"> <li>Scroll-Scan不进行文本相似度计算,不排序,按照索引中的数据顺序返回。</li> <li>Scroll-Scan 不支持聚合操作。</li> <li>Scroll-Scan 的参数 Size 代表着每个分片上的请求的结果数量,每次返回 n×size 条数据。而 Scroll 每次返回 size 条数据。</li> </ul> <h3 id='1-2-2-②选择合适的路由' id="boomdevs_11" data-anchor-id="j2ja" >1-2-2. <strong>②选择合适的路由</strong></h3> <p>ES 中所谓的路由和 IP 网络不同,是一个类似于 Tag 的东西。在创建文档的时候,可以通过字段为文档增加一个路由属性的 Tag。在多分片的 ES 集群中,对搜索的查询大致分为如下两种。</p> <p>ES 内在机制决定了拥有相同路由属性的文档,一定会被分配到同一个分片上,无论是主分片还是副本。</p> <p>查询时可以根据 Routing 信息,直接定位到目标分片,避免查询所有的分片,再经过协调节点二次排序。</p> <p>如图 1 所示:</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5627" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212553.png" width="750" height="271" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212553.png 750w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212553-300x108.png 300w" sizes="(max-width: 750px) 100vw, 750px" /></p> <p><em>图 1</em></p> <p>如果在查询条件中不包含 Routing,在查询时就遍历所有分片,整个查询主要分为 Scatter、Gather 两个过程:</p> <ul class="list-paddingleft-2"> <li><strong>Scatter(分发):</strong>请求到达协调节点之后,协调节点将查询请求分发给每个分片。</li> <li><strong>Gather(聚合):</strong>协调点在每个分片上完成搜索,再将搜索到的结果集进行排序,将结果数据返回给用户。</li> </ul> <p>如图 2 所示:</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5628" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212600.png" width="750" height="337" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212600.png 750w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212600-300x135.png 300w" sizes="(max-width: 750px) 100vw, 750px" /><img decoding="async" class="rich_pages img_loading" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" crossorigin="anonymous" data-copyright="0" data-s="300,640" data-src="https://mmbiz.qpic.cn/mmbiz_png/MOwlO0INfQpNJe7PBoToRWtNjwytxeibg6p1NM2VcA70bb3I23t4icZNG5k3E1lzSLweXTBics3mFX5Mz0z8QuWww/640?wx_fmt=png" data-type="png" data-ratio="0.4493333333333333" data-w="750" /></p> <p><em>图 2</em></p> <p>通过对比上述两种查询流程,我们不难发现,使用 Routing 信息查找的效率很高,避免了多余的查询。</p> <p>所以我们在设计 Elasticsearch Mapping 时要合理地利用 Routing 信息,来提升查询的效率。</p> <p>例如,在大型的本地分类网站中,可以将城市 ID 作为 Routing 的条件,让同一个城市的数据落在相同的分片中。</p> <p>默认的公式如下:</p><pre class="crayon-plain-tag">shard = hash(routing)%number_of_primary_shards</pre><p>不过需要注意的是,根据城市 ID 进行分片时,也会容易出现分片不均匀的情况。</p> <p>例如,大型城市的数据过多,而小城市的数据太少,导致分片之间的数据量差异很大。</p> <p>这时就可以进行必要的调整,比如把多个小城市的数据合并到一个分片上,把大城市的数据按区域进行拆分到不同分配。</p> <h3 id='1-2-3-③searchtype' id="boomdevs_12" data-anchor-id="q1t7" >1-2-3. <strong>③SearchType</strong></h3> <p>在 Scatter、Gather 的过程中,节点间的数据传输和打分(SearchType),可以根据不同的场景选择。</p> <p>如下所示:</p> <ul class="list-paddingleft-2"> <li><strong>QUERY_THEN_FETCH:</strong>ES 默认的搜索方式。第一步,先向所有的分片发请求,各分片只返回文档的相似度得分和文档的 ID,然后协调节点按照各分片返回的分数进行重新排序和排名,再取出需要返回给客户端的 Size 个文档 ID。第 2 步,在相关的分片中取出文档的详细信息并返回给用户。</li> <li><strong>QUERY_AND_FETCH:</strong>协调节点向所有分片发送查询请求,各分片将文档的相似度得分和文档的详细信息一起返回。然后,协调节点进行重新排序,再取出需要返回给客户端的数据,将其返回给客户端。由于只需要在分片中查询一次,所以性能是最好的。</li> <li><strong>DFS_QUERY_THEN_FETCH:</strong>与 QUERY_THEN_FETCH 类似,但它包含一个额外的阶段:在初始查询中执行全局的词频计算,以使得更精确地打分,从而让查询结果更相关。QUERY_THEN_FETCH 使用的是分片内部的词频信息,而 DFS_QUERY_THEN_FETCH 使用访问公共的词频信息,所以相比 QUERY_THEN_FETCH 性能更低。</li> <li><strong>DFS_QUERY_AND_FETCH:</strong>与 QUERY_AND_FETCH 类似,不过使用的是全局的词频。</li> </ul> <h3 id='1-2-4-④定期删除' id="boomdevs_13" data-anchor-id="ebh8" >1-2-4. <strong>④定期删除</strong></h3> <p>由于在 Lucene 中段具有不变性,每次进行删除操作后不会立即从硬盘中进行实际的删除,而是产生一个 .del 文件记录删除动作。</p> <p>随着删除操作的增长,.del 文件会越来也多。当我们进行查询操作的时候,被删除的数据还会参与检索中,然后根据 .del 文件进行过滤。.del 文件越多,查询过滤过程越长,进而影响查询的效率。</p> <p>当机器空闲时,我们可以通过如下命令删除文件,来提升查询的效率:</p><pre class="crayon-plain-tag">$ curl -XPOST localhost:9200/chandler/_forcemerge?only_expunge_deletes=true {"_shards":{"total":10,"successful":5,"failed":0}}</pre><p>定期对不再更新的索引做 optimize (ES 2.0 以后更改为 Force Merge API)。</p> <p>这 Optimze 的实质是对 Segment File 强制做合并,可以节省大量的 Segment Memory。</p> <section> <section> <section> <section></section> <h2 id='1-3-堆大小的设置' id="boomdevs_14" >1-3. <strong>堆大小的设置</strong></h2> </section> </section> </section> <p>ES 默认安装后设置的内存是 1GB,对于任何一个现实业务来说,这个设置都太小了。</p> <p>如果是通过解压安装的 ES,则在 ES 安装文件中包含一个 jvm.option 文件,添加如下命令来设置 ES 的堆大小:</p><pre class="crayon-plain-tag">-Xms10g -Xmx10g</pre><p>Xms 表示堆的初始大小,Xmx 表示可分配的最大内存,都是 10GB。</p> <p>确保 Xmx 和 Xms 的大小是相同的,其目的是为了能够在 Java 垃圾回收机制清理完堆区后不需要重新分隔计算堆区的大小而浪费资源,可以减轻伸缩堆大小带来的压力。</p> <p>也可以通过设置环境变量的方式设置堆的大小。服务进程在启动时候会读取这个变量,并相应的设置堆的大小。比如:</p><pre class="crayon-plain-tag">export ES_HEAP_SIEZE=10g</pre><p>也可以通过命令行参数的形式,在程序启动的时候把内存大小传递给 ES,如下所示:</p><pre class="crayon-plain-tag">./bin/elasticsearch -Xmx10g -Xms10g</pre><p>这种设置方式是一次性的,在每次启动 ES 时都需要添加。</p> <p>假设你有一个 64G 内存的机器,按照正常思维思考,你可能会认为把 64G 内存都给 ES 比较好,但现实是这样吗, 越大越好?虽然内存对 ES 来说是非常重要的,但是答案是否定的!</p> <p>因为 ES 堆内存的分配需要满足以下两个原则:</p> <ul class="list-paddingleft-2"> <li><strong>不要超过物理内存的 50%:</strong>Lucene 的设计目的是把底层 OS 里的数据缓存到内存中。Lucene 的段是分别存储到单个文件中的,这些文件都是不会变化的,所以很利于缓存,同时操作系统也会把这些段文件缓存起来,以便更快的访问。如果我们设置的堆内存过大,Lucene 可用的内存将会减少,就会严重影响降低 Lucene 的全文本查询性能。</li> <li><strong>堆内存的大小最好不要超过 32GB:</strong>在 Java 中,所有对象都分配在堆上,然后有一个 Klass Pointer 指针指向它的类元数据。这个指针在 64 位的操作系统上为 64 位,64 位的操作系统可以使用更多的内存(2^64)。在 32 位的系统上为 32 位,32 位的操作系统的最大寻址空间为 4GB(2^32)。但是 64 位的指针意味着更大的浪费,因为你的指针本身大了。浪费内存不算,更糟糕的是,更大的指针在主内存和缓存器(例如 LLC, L1等)之间移动数据的时候,会占用更多的带宽。</li> </ul> <p>Java 使用内存指针压缩(Compressed Oops)技术来解决这个问题。它的指针不再表示对象在内存中的精确位置,而是表示偏移量。</p> <p>这意味着 32 位的指针可以引用 4GB 个 Byte,而不是 4GB 个 bit。也就是说,当堆内存为 32GB 的物理内存时,也可以用 32 位的指针表示。</p> <p>不过,在越过那个神奇的边界 32GB 时,指针就会变为普通对象的指针,每个对象的指针都变长了,就会浪费更多的内存,降低了 CPU 的性能,还要让 GC 应对更大的内存。</p> <p>事实上,当内存到达 40~40GB 时,有效的内存才相当于内存对象指针压缩技术时的 32GB 内存。</p> <p>所以即便你有足够的内存,也尽量不要超过 32G,比如我们可以设置为 31GB:</p><pre class="crayon-plain-tag">-Xms31g -Xmx31g</pre><p>32GB 是 ES 一个内存设置限制,那如果你的机器有很大的内存怎么办呢?现在的机器内存普遍增长,甚至可以看到有 300-500GB 内存的机器。</p> <p>这时我们需要根据业务场景,进行恰当内存的分配:</p> <ul class="list-paddingleft-2"> <li><strong>业务场景是以全文检索为主:</strong>依然可以给 ES 分配小于 32GB 的堆内存,剩下的交给 Lucene 用作操作系统的文件系统缓存,所有的 Segment 都缓存起来,会加快全文检索。</li> <li><strong>业务场景中有很多的排序和聚合:</strong>我们可以考虑一台机器上创建两个或者更多 ES 节点,而不要部署一个使用 32+GB 内存的节点。仍然要坚持 50% 原则,假设你有个机器有 128G 内存,你可以创建两个 Node,使用 32G 内存。也就是说 64G 内存给 ES 的堆内存,剩下的 64G 给 Lucene。</li> </ul> <section> <section> <section> <section></section> <h2 id='1-4-服务器配置的选择' id="boomdevs_15" >1-4. <strong>服务器配置的选择</strong></h2> </section> </section> </section> <p><strong>Swapping 是性能的坟墓:</strong>在选择 ES 服务器时,要尽可能地选择与当前应用场景相匹配的服务器。</p> <p>如果服务器配置很低,则意味着需要更多的节点,节点数量的增加会导致集群管理的成本大幅度提高。</p> <p>如果服务器配置很高,而在单机上运行多个节点时,也会增加逻辑的复杂度。</p> <p>在计算机中运行的程序均需在内存执行,若内存消耗殆尽将导致程序无法进行。为了解决这个问题,操作系统使用一种叫作虚拟内存的技术。</p> <p>当内存耗尽时,操作系统就会自动把内存中暂时不使用的数据交换到硬盘中,需要使用的时候再从硬盘交换到内存。</p> <p>如果内存交换到磁盘上需要 10 毫秒,从磁盘交换到内存需要 20 毫秒,那么多的操作时延累加起来,将导致几何级增长。</p> <p>不难看出 Swapping 对于性能是多么可怕。所以为了使 ES 有更好等性能,强烈建议关闭 Swap。</p> <p>关闭 Swap 的方式如下:</p> <p><strong>①暂时禁用。</strong>如果我们想要在 Linux 服务器上暂时关闭,可以执行如下命令,但在服务器重启后失效:</p><pre class="crayon-plain-tag">sudo swapoff -a</pre><p><strong>②永久性关闭。</strong>我们可以修改 /etc/sysctl.conf(不同的操作系统路径有可能不同),增加如下参数:</p><pre class="crayon-plain-tag">vm.swappiness = 1 //0-100,则表示越倾向于使用虚拟内存。</pre><p>注意:Swappiness 设置为 1 比设置为 0 要好,因为在一些内核版本,Swappness=0 会引发 OOM(内存溢出)。</p> <p>Swappiness 默认值为 60,当设置为 0 时,在某些操作系统中有可能会触发系统级的 OOM-killer,例如在 Linux 内核的内存不足时,为了防止系统的崩溃,会自动强制 Kill 一个“bad”进程。</p> <p><strong>③在 ES 中设置。</strong>如果上面的方法都不能做到,你需要打开配置文件中的 mlockall 开关,它的作用就是运行 JVM 锁住内存,禁止 OS 交换出去。</p> <p>在 elasticsearch.yml 配置如下:</p><pre class="crayon-plain-tag">bootstrap.mlockall: true</pre><p></p> <section> <section> <section> <section></section> <h2 id='1-5-硬盘的选择和设置' id="boomdevs_16" >1-5. <strong>硬盘的选择和设置</strong></h2> </section> </section> </section> <p>所以,如果条件允许,则请尽可能地使用 SSD,它的读写性能将远远超出任何旋转介质的硬盘(如机械硬盘、磁带等)。基于 SSD 的 ES 集群节点对于查询和索引性能都有提升。</p> <p>另外无论是使用固态硬盘还是使用机械硬盘,我们都建议将磁盘的阵列模式设置为 RAID 0,以此来提升磁盘的写性能。</p> <section> <section> <section> <section></section> <h2 id='1-6-接入方式' id="boomdevs_17" >1-6. <strong>接入方式</strong></h2> </section> </section> </section> <p>ES 提供了 Transport Client(传输客户端)和 Node Client(节点客户端)的接入方式,这两种方式各有利弊,分别对应不同的应用场景。</p> <p data-anchor-id="8qnu"><strong>①Transport Client:</strong>作为一个集群和应用程序之间的通信层,和集群是安全解耦的。</p> <p data-anchor-id="8qnu">由于与集群解耦,所以在连接集群和销毁连接时更加高效,适合大量的客户端连接。</p> <p data-anchor-id="q6qq"><strong>②Node Client:</strong>把应用程序当作一个集群中的 Client 节点(非 Data 和 Master 节点)。</p> <p data-anchor-id="q6qq">由于它是集群的一个内部节点,意味着它可以感知整个集群的状态、所有节点的分布情况、分片的分布状况等。</p> <p>由于 Node Client 是集群的一部分,所以在接入和退出集群时进行比较复杂操作,并且还会影响整个集群的状态,所以 Node Client 更适合少量客户端,能够提供更好的执行效率。</p> <section> <section> <section> <section></section> <h2 id='1-7-角色隔离和脑裂' id="boomdevs_18" >1-7. <strong>角色隔离和脑裂</strong></h2> </section> </section> </section> <h3 id='1-7-1-①角色隔离' id="boomdevs_19" data-anchor-id="2yxq" >1-7-1. <strong>①角色隔离</strong></h3> <p>ES 集群中的数据节点负责对数据进行增、删、改、查和聚合等操作,所以对 CPU、内存和 I/O 的消耗很大。</p> <p>在搭建 ES 集群时,我们应该对 ES 集群中的节点进行角色划分和隔离。</p> <p>候选主节点:</p><pre class="crayon-plain-tag">node.master=true node.data=false</pre><p>数据节点:</p><pre class="crayon-plain-tag">node.master=false node.data=true</pre><p>最后形成如图 3 所示的逻辑划分:</p> <p><img decoding="async" class="rich_pages img_loading" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" crossorigin="anonymous" data-copyright="0" data-s="300,640" data-src="https://mmbiz.qpic.cn/mmbiz_png/MOwlO0INfQpNJe7PBoToRWtNjwytxeibgibELFlc81ibnD3cRDrvS3SqkKFDZTt4GwLdK5yYib8rYcgHzv9nM6icYHA/640?wx_fmt=png" data-type="png" data-ratio="0.5236486486486487" data-w="888" /><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5629" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212607.png" width="888" height="465" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212607.png 888w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212607-300x157.png 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212607-768x402.png 768w" sizes="(max-width: 888px) 100vw, 888px" /></p> <p data-anchor-id="wf4u">图 3</p> <h3 id='1-7-2-②避免脑裂' id="boomdevs_20" data-anchor-id="cm9f" >1-7-2. <strong>②避免脑裂</strong></h3> <p>网络异常可能会导致集群中节点划分出多个区域,区域发现没有 Master 节点的时候,会选举出了自己区域内 Maste 节点 r,导致一个集群被分裂为多个集群,使集群之间的数据无法同步,我们称这种现象为脑裂。</p> <p>为了防止脑裂,我们需要在 Master 节点的配置文件中添加如下参数:</p><pre class="crayon-plain-tag">discovery.zen.minimum_master_nodes=(master_eligible_nodes/2)+1 //默认值为1</pre><p>其中 master_eligible_nodes 为 Master 集群中的节点数。这样做可以避免脑裂的现象都出现,最大限度地提升集群的高可用性。</p> <p>只要不少于 discovery.zen.minimum_master_nodes 个候选节点存活,选举工作就可以顺利进行。</p> <h1 id='2-es-实战' id="boomdevs_21" >2. ES 实战</h1> <h2 id='2-1-es-配置说明' id="boomdevs_22" >2-1. <strong>ES</strong> 配置说明</h2> <p>在 ES 安装目录下的 Conf 文件夹中包含了一个重要的配置文件:elasticsearch.yaml。</p> <p>ES 的配置信息有很多种,大部分配置都可以通过 elasticsearch.yaml 和接口的方式进行。</p> <p>下面我们列出一些比较重要的配置信息:</p> <ul class="list-paddingleft-2"> <li><strong>cluster.name:elasticsearch:</strong>配置 ES 的集群名称,默认值是 ES,建议改成与所存数据相关的名称,ES 会自动发现在同一网段下的集群名称相同的节点。</li> <li><strong>node.nam: “node1″:</strong>集群中的节点名,在同一个集群中不能重复。节点的名称一旦设置,就不能再改变了。当然,也可以设置成服务器的主机名称,例如 node.name:${HOSTNAME}。</li> <li><strong>noed.master:true:</strong>指定该节点是否有资格被选举成为 Master 节点,默认是 True,如果被设置为 True,则只是有资格成为 Master 节点,具体能否成为 Master 节点,需要通过选举产生。</li> <li><strong>node.data:true:</strong>指定该节点是否存储索引数据,默认为 True。数据的增、删、改、查都是在 Data 节点完成的。</li> <li><strong>index.number_of_shards:5:</strong>设置都索引分片个数,默认是 5 片。也可以在创建索引时设置该值,具体设置为多大都值要根据数据量的大小来定。如果数据量不大,则设置成 1 时效率最高。</li> <li><strong>index.number_of_replicas:1:</strong>设置默认的索引副本个数,默认为 1 个。副本数越多,集群的可用性越好,但是写索引时需要同步的数据越多。</li> <li><strong>path.conf:/path/to/conf:</strong>设置配置文件的存储路径,默认是 ES 目录下的 Conf 文件夹。建议使用默认值。</li> <li><strong>path.data:/path/to/data1,/path/to/data2:</strong>设置索引数据多存储路径,默认是 ES 根目录下的 Data 文件夹。切记不要使用默认值,因为若 ES 进行了升级,则有可能数据全部丢失。可以用半角逗号隔开设置的多个存储路径,在多硬盘的服务器上设置多个存储路径是很有必要的。</li> <li><strong>path.logs:/path/to/logs:</strong>设置日志文件的存储路径,默认是 ES 根目录下的 Logs,建议修改到其他地方。</li> <li><strong>path.plugins:/path/to/plugins:</strong>设置第三方插件的存放路径,默认是 ES 根目录下的 Plugins 文件夹。</li> <li><strong>bootstrap.mlockall:true:</strong>设置为 True 时可锁住内存。因为当 JVM 开始 Swap 时,ES 的效率会降低,所以要保证它不 Swap。</li> <li><strong>network.bind_host:192.168.0.1:</strong>设置本节点绑定的 IP 地址,IP 地址类型是 IPv4 或 IPv6,默认为 0.0.0.0。</li> <li><strong>network.publish_host:192.168.0.1:</strong>设置其他节点和该节点交互的 IP 地址,如果不设置,则会进行自我判断。</li> <li><strong>network.host:192.168.0.1:</strong>用于同时设置 bind_host 和 publish_host 这两个参数。</li> <li><strong>http.port:9200:</strong>设置对外服务的 HTTP 端口,默认为 9200。ES 的节点需要配置两个端口号,一个对外提供服务的端口号,一个是集群内部使用的端口号。http.port 设置的是对外提供服务的端口号。注意,如果在一个服务器上配置多个节点,则切记对端口号进行区分。</li> <li><strong>transport.tcp.port:9300:</strong>设置集群内部的节点间交互的 TCP 端口,默认是 9300。注意,如果在一个服务器配置多个节点,则切记对端口号进行区分。</li> <li><strong>transport.tcp.compress:true:</strong>设置在节点间传输数据时是否压缩,默认为 False,不压缩。</li> <li><strong>discovery.zen.minimum_master_nodes:1:</strong>设置在选举 Master 节点时需要参与的最少的候选主节点数,默认为 1。如果使用默认值,则当网络不稳定时有可能会出现脑裂。合理的数值为(master_eligible_nodes/2)+1,其中 master_eligible_nodes 表示集群中的候选主节点数。</li> <li><strong>discovery.zen.ping.timeout:3s:</strong>设置在集群中自动发现其他节点时 Ping 连接的超时时间,默认为 3 秒。在较差的网络环境下需要设置得大一点,防止因误判该节点的存活状态而导致分片的转移。</li> </ul> <section> <section> <section> <section></section> <h2 id='2-2-常用接口' id="boomdevs_23" >2-2. <strong>常用接口</strong></h2> </section> </section> </section> <p>虽然现在有很多开源软件对 ES 的接口进行了封装,使我们可以很方便、直观地监控集群的状况,但是在 ES 5 以后,很多软件开始收费。</p> <p>了解常用的接口有助于我们在程序或者脚本中查看我们的集群情况,以下接口适用于 ES 6.5.2 版本。</p> <h3 id='2-2-1-①索引类接口' id="boomdevs_24" data-anchor-id="mhvr" >2-2-1. <strong>①索引类接口</strong></h3> <p>通过下面的接口创建一个索引名称为 indexname 且包含 3 个分片、1 个副本的索引:</p><pre class="crayon-plain-tag">PUT http://localhost:9200/indexname?pretty content-type →application/json; charset=UTF-8 { "settings":{ "number_of_shards" : 3, "number_of_replicas" : 1 } }</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5630" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212618.jpg" width="1080" height="722" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212618.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212618-300x201.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212618-768x513.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212618-1024x685.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>通过下面都接口删除索引:</p><pre class="crayon-plain-tag">DELETE http://localhost:9200/indexname</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5631" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212627.jpg" width="1080" height="476" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212627.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212627-300x132.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212627-768x338.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212627-1024x451.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>通过该接口就可以删除索引名称为 indexname 的索引,通过下面的接口可以删除多个索引:</p><pre class="crayon-plain-tag">DELETE http://localhost:9200/indexname1,indexname2 DELETE http://localhost:9200/indexname*</pre><p>通过下面的接口可以删除集群下的全部索引:</p><pre class="crayon-plain-tag">DELETE http://localhost:9200/_all DELETE http://localhost:9200/*</pre><p>进行全部索引删除是很危险的,我们可以通过在配置文件中添加下面的配置信息,来关闭使用 _all 和使用通配符删除索引的接口,使用删除索引职能通过索引的全称进行。</p><pre class="crayon-plain-tag">action.destructive_requires_name: true</pre><p>通过下面的接口获取索引的信息,其中,Pretty 参数用语格式化输出结构,以便更容易阅读:</p><pre class="crayon-plain-tag">GET http://localhost:9200/indexname?pretty</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5632" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212638.jpg" width="1080" height="700" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212638.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212638-300x194.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212638-768x498.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212638-1024x664.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>通过下面的接口关闭、打开索引:</p><pre class="crayon-plain-tag">POST http://localhost:9200/indexname/_close POST http://localhost:9200/indexname/_open</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5633" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212647.jpg" width="1080" height="442" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212647.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212647-300x123.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212647-768x314.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212647-1024x419.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>通过下面的接口获取一个索引中具体 Type 的 Mapping 映射:</p><pre class="crayon-plain-tag">GET http://localhost:9200/indexname/typename/_mapping?pretty</pre><p>当一个索引中有多个 Type 时,获得 Mapping 时要加上 Typename。</p> <h3 id='2-2-2-②document-操作' id="boomdevs_25" data-anchor-id="2k4p" >2-2-2. <strong>②Document 操作</strong></h3> <p>安装 ES 和 Kibana 之后,进入 Kibana 操作页面,然后进去的 DevTools 执行下面操作:</p><pre class="crayon-plain-tag">#添加一条document PUT /test_index/test_type/1 { "test_content":"test test" } #查询 GET /test_index/test_type/1 #返回 { "_index" : "test_index", "_type" : "test_type", "_id" : "1", "_version" : 2, "found" : true, "_source" : { "test_content" : "test test" } }</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5634" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212655.jpg" width="1080" height="317" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212655.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212655-300x88.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212655-768x225.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212655-1024x301.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5635" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212703.jpg" width="1080" height="312" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212703.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212703-300x87.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212703-768x222.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212703-1024x296.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>put/index/type/id 说明如下:</p> <ul class="list-paddingleft-2"> <li><strong>_index 元数据:</strong>代表这个 Document 存放在哪个 Idnex 中,类似的数据放在一个索引,非类似的数据放不同索引,Index 中包含了很多类似的 Document。</li> <li><strong>_type 元数据:</strong>代表 Document 属于 Index 中的哪个类别(type),一个索引通常会划分多个 Type,逻辑上对 Index 中有些许不同的几类数据进行分类。</li> <li><strong>_id 元数据:</strong>代表 Document 的唯一标识,id 与 Index 和 Type 一起,可以唯一标识和定位一个 Document,可以理解为数据库中主键。我们可以指定 Document 的 id,也可以不指定,由ES自动为我们创建一个 id。</li> </ul> <section> <section> <section> <section></section> <h2 id='2-3-接口应用' id="boomdevs_26" >2-3. <strong>接口应用</strong></h2> </section> </section> </section> <h3 id='2-3-1-①search-接口' id="boomdevs_27" data-anchor-id="ifyf" >2-3-1. <strong>①Search 接口</strong></h3> <p>Search 是我们最常用的 API,ES 给我提供了丰富的查询条件,比如模糊匹配 Match,字段判空 Exists,精准匹配 Term 和 Terms,范围匹配 Range:</p><pre class="crayon-plain-tag">GET /_search { "query": { "bool": { "must": [ //must_not { "match": { "title": "Search" }}, { "match": { "content": "Elasticsearch" }}, {"exists":{"field":"字段名"}} //判断字段是否为空 ], "filter": [ { "term": { "status": "published" }}, { "terms": { "status": [0,1,2,3] }},//范围 { "range": { "publish_date": { "gte": "2015-01-01" }}} //范围gte:大于等于;gt:大于;lte:小于等于;lt:小于 ] } } }</pre><p>查询索引为 test_index,doc 类型为 test_type 的数据:</p><pre class="crayon-plain-tag">GET /test_index/test_type/_search</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5636" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212710.jpg" width="1080" height="549" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212710.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212710-300x153.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212710-768x390.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212710-1024x521.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>查询索引为 test_index,doc 类型为 test_type,docment 字段 num10 为 4 的数据:</p><pre class="crayon-plain-tag">GET /test_index/test_type/_search?pretty=true { "query": { "bool": { "filter": [ { "term": { "num10": 4 }} ] } } }</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5637" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212718.jpg" width="1080" height="583" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212718.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212718-300x162.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212718-768x415.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212718-1024x553.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>更多查询条件的组合,大家可以自行测试。</p> <h3 id='2-3-2-②修改-mapping' id="boomdevs_28" data-anchor-id="z0h9" >2-3-2. <strong>②修改 Mapping</strong></h3> <p></p><pre class="crayon-plain-tag">PUT /my_index/_mapping/my_type { "properties": { "new_field_name": { "type": "string" //字段类型,string、long、boolean、ip } } }</pre><p>如上是修改 Mapping 结构,然后利用脚本 Script 给字段赋值:</p><pre class="crayon-plain-tag">POST my_index/_update_by_query { "script": { "lang": "painless", "inline": "ctx._source.new_field_name= '02'" } }</pre><p></p> <h3 id='2-3-3-③修改别名' id="boomdevs_29" data-anchor-id="kp5s" >2-3-3. <strong>③修改别名</strong></h3> <p>如下给 Index 为 test_index 的数据绑定 Alias 为 test_alias:</p><pre class="crayon-plain-tag">POST /_aliases { "actions": [ { "add": { //add,remove "index": "test_index", "alias": "test_alias" } } ] }</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5638" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212727.jpg" width="1080" height="253" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212727.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212727-300x70.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212727-768x180.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212727-1024x240.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p>验证别名关联,根据别名来进行数据查询,如下:</p><pre class="crayon-plain-tag">GET /test_alias/test_type/3</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5639" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212734.jpg" width="1080" height="347" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212734.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212734-300x96.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212734-768x247.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212734-1024x329.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <h3 id='2-3-4-④定制返回内容' id="boomdevs_30" data-anchor-id="qyvs" >2-3-4. <strong>④定制返回内容</strong></h3> <p><strong>_source 元数据:</strong>就是说,我们在创建一个 Document 的时候,使用的那个放在 Request Body 中的 Json 串(所有的 Field),默认情况下,在 Get 的时候,会原封不动的给我们返回回来。</p> <p>定制返回的结果,指定 _source 中,返回哪些 Field:</p><pre class="crayon-plain-tag">#语法: GET /test_index/test_type/1?_source=test_field2 #返回 { "_index" : "test_index", "_type" : "test_type", "_id" : "1", "_version" : 3, "found" : true, "_source" : { "test_field2" : "test field2" } } #也可返回多个field使用都好分割 GET /test_index/test_type/1?_source=test_field2,test_field1</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5640" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212741.jpg" width="1080" height="312" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212741.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212741-300x87.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212741-768x222.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212741-1024x296.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5641" src="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212749.jpg" width="1080" height="278" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212749.jpg 1080w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212749-300x77.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212749-768x198.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/18/4ffce04d92a4d6cb21c1494cdfcd6dc1_212749-1024x264.jpg 1024w" sizes="(max-width: 1080px) 100vw, 1080px" /></p> <section> <section> <section> <section></section> <h2 id='2-4-java-封装' id="boomdevs_31" >2-4. <strong>Java 封装</strong></h2> </section> </section> </section> <p>组件 elasticsearch.jar 提供了丰富 API,不过不利于我们理解和学习,现在我们自己来进行封装。</p> <p>组件 API 使用 RestClient 封装 Document 查询接口:</p><pre class="crayon-plain-tag">/** * @param index * @param type * @param id * @param fields * 查询返回字段,可空 * @return * @throws Exception * @Description: * @create date 2019年4月3日下午3:12:40 */ public String document(String index, String type, String id, List<String> fields) throws Exception { Map<String, String> paramsMap = new HashMap<>(); paramsMap.put("pretty", "true"); if (null != fields && fields.size() != 0) { String fieldValue = ""; for (String field : fields) { fieldValue += field + ","; } if (!"".equals(fieldValue)) { paramsMap.put("_source", fieldValue); } } return CommonUtils.toString(es.getRestClient() .performRequest("GET", "/" + index + "/" + type + "/" + id, paramsMap).getEntity().getContent()); }</pre><p>工程使用,封装:</p><pre class="crayon-plain-tag">public String searchDocument(String index, String type, String id, List<String> fields) { try { return doc.document(index, type, id, fields); } catch (Exception e) { log.error(e.getMessage()); ExceptionLogger.log(e); throw new RuntimeException("ES查询失败"); } }</pre><p>测试用例,代码如下:</p><pre class="crayon-plain-tag">/** * ES交互验证-查询、更新等等操作 * * @version * @author 钱丁君-chandler 2019年4月3日上午10:27:28 * @since 1.8 */ @RunWith(SpringRunner.class) @SpringBootTest(classes = Bootstrap.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class ESManagerTest { @Autowired private ESBasicManager esBasicManager; @Test public void query() { String result = esBasicManager.searchDocument(ESTagMetadata.INDEX_ALIAS, ESTagMetadata.DOC_TYPE, "188787665220752824", ImmutableList.of("signup_time", "tag_days_no_visit_after_1_order")); System.out.println("----------->" + result); } }</pre><p>控台输出:</p><pre class="crayon-plain-tag">----------->{ "_index" : "crm_tag_idx_20181218_708672", "_type" : "crm_tag_type", "_id" : "188787665220752824", "_version" : 1, "found" : true, "_source" : { "signup_time" : "2017-12-24", "tag_days_no_visit_after_1_order" : "339" } }</pre><p>我只是抛砖引玉,大家可以自行进行各种操作的封装,不管对于理解 ES 的使用,还是对代码质量提升都有很多帮助。</p> <p><em>作者:钱丁君</em></p> <p> </p> <p>[source]<a href="https://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=2655824323&idx=1&sn=2cd6852e7baae4b00c68d2678b5e23cc&chksm=bd74e4148a036d02831449be2301f2c83faabdd03760ba970a63a1d5c2c19c603b4108ebcc77&mpshare=1&scene=24&srcid=#rd">超详细的Elasticsearch高性能优化实践</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e8%b6%85%e8%af%a6%e7%bb%86%e7%9a%84elasticsearch%e9%ab%98%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96%e5%ae%9e%e8%b7%b5/">[转]超详细的Elasticsearch高性能优化实践</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e8%b6%85%e8%af%a6%e7%bb%86%e7%9a%84elasticsearch%e9%ab%98%e6%80%a7%e8%83%bd%e4%bc%98%e5%8c%96%e5%ae%9e%e8%b7%b5/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[转]基于大数据的情绪分析(二)</title> <link>http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%ba%8c%ef%bc%89/</link> <comments>http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%ba%8c%ef%bc%89/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Mon, 17 Jun 2019 21:15:32 +0000</pubDate> <category><![CDATA[Theory & Solution]]></category> <category><![CDATA[Big Data]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=5523</guid> <description><![CDATA[<p>1. 导言 情绪分析使用机器学习算法来确定正面或负面文本内容的方式。情绪分析的示例包括: 快速了解客户评论的基调: 了解客户喜欢或不喜欢的产品或服务。 了解可能影响新客户购买决策的因素。 为企业提供市场意识。 尽早解决问题 了解股市情绪,以获得对金融信号预测的见解 社交媒体监控 品牌/产品/公司人气/声誉/感知监控 不满意的客户检测监控和警报 营销活动监控/分析 客户服务意见监测/分析 品牌情绪态度分析 客户反馈分析 竞争情绪分析 品牌影响者监控 手动分析客户或潜在客户产生的大量文本是非常耗时的,机器学习使这项工作变得更加有效,并且通过流分析,可以实时提供见解。 本文将讨论将流数据与机器学习和快速存储相结合的数据管道的体系结构。在上一篇文章中,我们使用Spark Machine学习数据管道探索了情绪分析,并保存了情绪分析机器学习模型。在本文着重讨论使用保存的情绪分析模型和流数据来对产品情绪进行实时分析,将结果存储在MapR数据库中,并使它们可以快速用于Spark和Drill SQL。 在本文中,我们将讨论以下内容: 流媒体概念概述 使用Spark结构化流媒体获取Kafka事件 使用机器学习模型丰富事件 在MapR数据库中存储事件 使用Apache Spark SQL和Apache Drill查询MapR数据库中快速可用的丰富事件 2. 流媒体概念 使用适用于Apache Kafka的MapR事件存储发布-订阅事件流 Apache Kafka的MapR事件存储是一个分布式发布-订阅事件流系统,它使生产者和消费者能够通过Apache Kafka API以并行和容错的方式实时交换事件。 流表示从生产者到消费者的连续事件序列,其中事件被定义为键值对。...</p> The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%ba%8c%ef%bc%89/">[转]基于大数据的情绪分析(二)</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<h1 id='1-导言' id="boomdevs_1" >1. 导言</h1> <p>情绪分析使用机器学习算法来确定正面或负面文本内容的方式。情绪分析的示例包括:</p> <ul class="list-paddingleft-2"> <li>快速了解客户评论的基调: <ul class="list-paddingleft-2"> <li>了解客户喜欢或不喜欢的产品或服务。</li> <li>了解可能影响新客户购买决策的因素。</li> <li>为企业提供市场意识。</li> <li>尽早解决问题</li> </ul> </li> <li>了解股市情绪,以获得对金融信号预测的见解</li> <li>社交媒体监控</li> <li>品牌/产品/公司人气/声誉/感知监控</li> <li>不满意的客户检测监控和警报</li> <li>营销活动监控/分析</li> <li>客户服务意见监测/分析</li> <li>品牌情绪态度分析</li> <li>客户反馈分析</li> <li>竞争情绪分析</li> <li>品牌影响者监控</li> </ul> <p><span id="more-5523"></span>手动分析客户或潜在客户产生的大量文本是非常耗时的,机器学习使这项工作变得更加有效,并且通过流分析,可以实时提供见解。</p> <p>本文将讨论将流数据与机器学习和快速存储相结合的数据管道的体系结构。在上一篇文章中,我们使用Spark Machine学习数据管道探索了情绪分析,并保存了情绪分析机器学习模型。在本文着重讨论使用保存的情绪分析模型和流数据来对产品情绪进行实时分析,将结果存储在MapR数据库中,并使它们可以快速用于Spark和Drill SQL。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5524" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211534.jpg" width="680" height="440" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211534.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211534-300x194.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>在本文中,我们将讨论以下内容:</p> <ul class="list-paddingleft-2"> <li>流媒体概念概述</li> <li>使用Spark结构化流媒体获取Kafka事件</li> <li>使用机器学习模型丰富事件</li> <li>在MapR数据库中存储事件</li> <li>使用Apache Spark SQL和Apache Drill查询MapR数据库中快速可用的丰富事件</li> </ul> <h1 id='2-流媒体概念' id="boomdevs_2" >2. 流媒体概念</h1> <p>使用适用于Apache Kafka的MapR事件存储发布-订阅事件流</p> <p>Apache Kafka的MapR事件存储是一个分布式发布-订阅事件流系统,它使生产者和消费者能够通过Apache Kafka API以并行和容错的方式实时交换事件。</p> <p>流表示从生产者到消费者的连续事件序列,其中事件被定义为键值对。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5525" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211540.png" width="680" height="185" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211540.png 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211540-300x82.png 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>主题是一个逻辑事件流。主题将事件组织成类别,并将生产者与消费者分离。主题按吞吐量和可伸缩性进行分区。MapR事件存储可以扩展到非常高的吞吐量级别,使用非常适中的硬件可以轻松地每秒传输百万条消息。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5526" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211546.jpg" width="680" height="671" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211546.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211546-300x296.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>你可以将分区看作是事件日志:将新事件附加到末尾,并为其分配一个称为偏移的顺序ID号。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5527" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211554.jpg" width="680" height="679" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211554.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211554-150x150.jpg 150w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211554-300x300.jpg 300w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211554-160x160.jpg 160w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211554-320x320.jpg 320w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>与队列一样,事件按照接收顺序传递。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5528" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211602.jpg" width="680" height="372" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211602.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211602-300x164.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>但是,与队列不同,读取时不会删除消息。它们保留在其他消费者可用的分区上。消息一旦发布,就是不可变的,可以永久保留。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5529" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211607.jpg" width="680" height="290" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211607.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211607-300x128.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>在读取消息时不删除消息允许大规模的高性能以及不同消费者针对不同目的(如,具有多语言持久性的多个视图)处理相同消息。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5530" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211612.jpg" width="680" height="603" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211612.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211612-300x266.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='3-spark数据集-dataframe-sql' id="boomdevs_3" >3. Spark数据集、DataFrame、SQL</h1> <p>Spark数据集是分布在集群中多个节点上的类型化对象的分布式集合。可以使用功能转换(map、flatMap、filter等)和(或)Spark SQL来操纵数据集。DataFrame是Row对象的数据集,表示包含行和列的数据表。</p> <p><img decoding="async" class="img_loading" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" crossorigin="anonymous" data-ratio="0.5352941176470588" data-src="https://mmbiz.qpic.cn/mmbiz_png/mkjCniaBPibTK9ziaic5T5biaKpCpAD6k3UWS2tFnMX8G98jW2ib0qMxozfxf20ZKGs79ERzfw1rraf3Tficx2eFic5jYA/640?wx_fmt=png" data-type="png" data-w="680" /><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5531" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211621.jpg" width="680" height="364" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211621.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211621-300x161.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='4-spark结构化流媒体' id="boomdevs_4" >4. Spark结构化流媒体</h1> <p>结构化流媒体是一种基于Spark SQL引擎的可扩展且容错的流处理引擎。通过Structured Streaming,你可以将发布到Kafka的数据视为无界DataFrame,并使用与批处理相同的DataFrame、Dataset和SQL API处理此数据。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5532" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211626.jpg" width="680" height="535" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211626.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211626-300x236.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>随着流数据的不断传播,Spark SQL引擎会逐步并持续地处理它并更新最终结果。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5533" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211633.jpg" width="680" height="405" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211633.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211633-300x179.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>事件的流处理对于实时ETL,过滤、转换、创建计数器和聚合,关联值,丰富其他数据源或机器学习,持久化到文件或数据库以及发布到管道的不同主题非常有用。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5534" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211639.jpg" width="680" height="388" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211639.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211639-300x171.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='5-spark结构化流示例代码' id="boomdevs_5" >5. Spark结构化流示例代码</h1> <p>以下是亚马逊产品评论数据的情绪分析用例的数据处理管道,以检测正面和负面评论。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5524" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211534.jpg" width="680" height="440" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211534.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211534-300x194.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <ol class="list-paddingleft-2"> <li>Amazon产品评论使用Kafka API将 JSON格式的事件发布到MapR事件存储主题。</li> <li>Spark Streaming应用程序订阅了该主题: <ul class="list-paddingleft-2"> <li>获取产品评论事件流</li> <li>使用部署的机器学习模型,通过正面或负面的情绪预测来丰富评论事件</li> <li>以JSON格式将转换和丰富的数据存储在MapR数据库中。</li> </ul> </li> </ol> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5536" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211650.jpg" width="680" height="361" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211650.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211650-300x159.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='6-用例数据示例' id="boomdevs_6" >6. 用例数据示例</h1> <p>示例数据集是来自之前文章的亚马逊产品评论数据(插入链接)。传入的数据采用JSON格式,示例如下:</p><pre class="crayon-plain-tag">{"reviewerID": "A3V52OTJHKIJZX", "asin": "2094869245","reviewText": "Light just installed on bike, seems to be well built.", "overall": 5.0, "summary": "Be seen", "unixReviewTime": 1369612800}</pre><p>我们使用情绪预测来丰富这些数据,删除一些列,然后将其转换为以下JSON对象:</p><pre class="crayon-plain-tag">{"reviewerID": "A3V52OTJHKIJZX", "_id":"2094869245_1369612800", "reviewText": "Light just installed on bike, seems to be well built.", "overall": 5.0, "summary": "Be seen", "label":"1", "prediction":"1"}</pre><p></p> <h1 id='7-加载spark-pipeline模型' id="boomdevs_7" >7. 加载Spark Pipeline模型</h1> <p>Spark PipelineModel类用于加载管道模型,该模型安装在历史产品评审数据上,然后保存到MapR-XD文件系统。</p><pre class="crayon-plain-tag">// Directory to read the saved ML model from var modeldirectory ="/user/mapr/sentmodel/" // load the saved model from the distributed file system val model = PipelineModel.load(modeldirectory)</pre><p></p> <h1 id='8-从kafka主题中读取数据' id="boomdevs_8" >8. 从Kafka主题中读取数据</h1> <p>为了从Kafka读取,我们必须首先指定流格式,主题和偏移选项。</p><pre class="crayon-plain-tag">var topic: String = "/user/mapr/stream:reviews" val df1 = spark.readStream.format("kafka") .option("kafka.bootstrap.servers", "maprdemo:9092") .option("subscribe", topic) .option("group.id", "testgroup") .option("startingOffsets", "earliest") .option("failOnDataLoss", false) .option("maxOffsetsPerTrigger", 1000) .load()</pre><p>这将返回具有以下架构的DataFrame:</p><pre class="crayon-plain-tag">df1.printSchema() result: root |-- key: binary (nullable = true) |-- value: binary (nullable = true) |-- topic: string (nullable = true) |-- partition: integer (nullable = true) |-- offset: long (nullable = true) |-- timestamp: timestamp (nullable = true) |-- timestampType: integer (nullable = true)</pre><p></p> <h1 id='9-将消息值解析为dataframe' id="boomdevs_9" >9. 将消息值解析为DataFrame</h1> <p>下一步是使用产品评审模式将二进制值列解析并转换为DataFrame。我们将使用Spark from_json从上面看到的Kafka DataFrame值字段中提取JSON数据。Spark SQL from_json( )函数使用指定的输入模式将输入JSON字符串转换为Spark结构。</p> <p>首先,我们使用Spark Structype来定义与传入的JSON消息值对应的模式,</p><pre class="crayon-plain-tag">val schema = StructType(Array( StructField("asin", StringType, true), StructField("helpful", ArrayType(StringType), true), StructField("overall", DoubleType, true), StructField("reviewText", StringType, true), StructField("reviewTime", StringType, true), StructField("reviewerID", StringType, true), StructField("reviewerName", StringType, true), StructField("summary", StringType, true), StructField("unixReviewTime", LongType, true) ))</pre><p>在下面的代码中,select表达式中使用from_json() Spark SQL函数,该表达式具有df1列值的字符串,其返回指定模式的DataFrame。</p><pre class="crayon-plain-tag">import spark.implicits._ val df2 = df1.select($"value" cast "string" as "json") .select(from_json($"json", schema) as "data") .select("data.*")</pre><p>返回具有以下架构的DataFrame:</p><pre class="crayon-plain-tag">df2.printSchema() result: root |-- asin: string (nullable = true) |-- helpful: array (nullable = true) | |-- element: string (containsNull = true) |-- overall: double (nullable = true) |-- reviewText: string (nullable = true) |-- reviewTime: string (nullable = true) |-- reviewerID: string (nullable = true) |-- reviewerName: string (nullable = true) |-- summary: string (nullable = true) |-- unixReviewTime: long (nullable = true)</pre><p>在下面的代码中:</p> <ul class="list-paddingleft-2"> <li>使用withColumn方法添加一个将审阅摘要与审阅文本相结合的列。</li> <li>过滤去除中性评级(= 3)</li> <li>Spark Bucketizer用于将标签0/1列添加到数据集中以获得Positive(总体评级> = 4)而不是正数(总评级<4)评论。(注意标签用于测试预测)</li> </ul> <p></p><pre class="crayon-plain-tag">// combine summary reviewText into one column val df3 = df2.withColumn("reviewTS", concat($"summary",lit(" "),$"reviewText" )) // remove neutral ratings val df4 = df3.filter("overall !=3") // add label column val bucketizer = new Bucketizer() .setInputCol("overall") .setOutputCol("label") .setSplits(Array(Double.NegativeInfinity,3.0,Double.PositiveInfinity)) val df5= bucketizer.transform(df4)</pre><p></p> <h1 id='10-用情感预测丰富评论的数据框架' id="boomdevs_10" >10. 用情感预测丰富评论的数据框架</h1> <p>接下来,使用模型管道转换DataFrame,模型管道将根据管道转换要素、预测,然后在新DataFrame的列中返回预测。</p><pre class="crayon-plain-tag">// transform the DataFrame with the model pipeline val predictions = model.transform(df5)</pre><p>返回具有以下架构的DataFrame:</p><pre class="crayon-plain-tag">predictions.printSchema() result: root |-- asin: string (nullable = true) |-- helpful: array (nullable = true) | |-- element: string (containsNull = true) |-- overall: double (nullable = true) |-- reviewText: string (nullable = true) |-- reviewTime: string (nullable = true) |-- reviewerID: string (nullable = true) |-- reviewerName: string (nullable = true) |-- summary: string (nullable = true) |-- unixReviewTime: long (nullable = true) |-- reviewTS: string (nullable = true) |-- label: double (nullable = true) |-- reviewTokensUf: array (nullable = true) | |-- element: string (containsNull = true) |-- reviewTokens: array (nullable = true) | |-- element: string (containsNull = true) |-- cv: vector (nullable = true) |-- features: vector (nullable = true) |-- rawPrediction: vector (nullable = true) |-- probability: vector (nullable = true) |-- prediction: double (nullable = false)</pre><p></p> <h1 id='11-为mapr数据库添加唯一id' id="boomdevs_11" >11. 为MapR数据库添加唯一ID</h1> <p>在下面的代码中:</p> <ul class="list-paddingleft-2"> <li>删除我们不想存储的列</li> <li>创建一个由产品ID和审核时间戳组成的唯一ID“_id”,作为用于存储在MapR数据库中的行键。</li> </ul> <p></p><pre class="crayon-plain-tag">// drop the columns that we do not want to store val df6 = predictions.drop("cv","probability", "features", "reviewTokens", "helpful", "reviewTokensUf", "rawPrediction") // create column with unique id for MapR Database val df7 = df6.withColumn("_id", concat($"asin",lit("_"), $"unixReviewTime"))</pre><p>返回具有以下架构的DataFrame:</p><pre class="crayon-plain-tag">df7.printSchema() Result: root |-- asin: string (nullable = true) |-- overall: double (nullable = true) |-- reviewText: string (nullable = true) |-- reviewTime: string (nullable = true) |-- reviewerID: string (nullable = true) |-- reviewerName: string (nullable = true) |-- summary: string (nullable = true) |-- unixReviewTime: long (nullable = true) |-- label: double (nullable = true) |-- reviewTokens: array (nullable = true) | |-- element: string (containsNull = true) |-- prediction: double (nullable = false) |-- _id: string (nullable = true)</pre><p> </p> <h1 id='12-spark-streaming写入mapr数据库' id="boomdevs_12" >12. Spark Streaming写入MapR数据库</h1> <p>Apache Spark的MapR数据库连接器可以将MapR数据库用作</p> <p>Spark Structure Streaming或Spark Streaming的接收器。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5537" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211655.jpg" width="680" height="292" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211655.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211655-300x129.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>当处理大量流数据时,其中一个挑战是:想在哪里存储它?对于此应用程序,选择MapR数据库(一种高性能NoSQL数据库),因为它具有JSON的可扩展性和灵活易用性。</p> <h1 id='13-json模式灵活性' id="boomdevs_13" >13. JSON模式灵活性</h1> <p>MapR数据库支持JSON文档作为本机数据存储。MapR数据库使用JSON文档轻松存储,查询和构建应用程序。Spark连接器可以轻松地在JSON数据和MapR数据库之间构建实时或批处理管道,并在管道中利用Spark。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5538" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211700.jpg" width="680" height="397" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211700.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211700-300x175.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>使用MapR数据库,表按键范围自动按群集分区为平板电脑,提供按行键可扩展和快速读写。在此示例中,行健_id由群集ID和反向时间戳组成,因此表将自动分区,并按最新的第一个群集ID进行排序。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5539" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211706.jpg" width="680" height="507" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211706.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211706-300x224.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>Spark MapR数据库连接器体系结构在每个Spark Executor中都有一个连接对象,允许使用MapR数据库平板电脑(分区)进行分布并行写入、读取和扫描。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5540" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211713.jpg" width="680" height="399" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211713.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211713-300x176.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='14-写入mapr数据库接收器' id="boomdevs_14" >14. 写入MapR数据库接收器</h1> <p>要将Spark Stream写入MapR数据库,请使用tablePath,idFieldPath,createTable,bulkMode和sampleSize参数指定格式。以下示例将df7 DataFrame写出到MapR数据库并启动流。</p><pre class="crayon-plain-tag">import com.mapr.db.spark.impl._ import com.mapr.db.spark.streaming._ import com.mapr.db.spark.sql._ import com.mapr.db.spark.streaming.MapRDBSourceConfig var tableName: String = "/user/mapr/reviewtable" val writedb = df7.writeStream .format(MapRDBSourceConfig.Format) .option(MapRDBSourceConfig.TablePathOption, tableName) .option(MapRDBSourceConfig.IdFieldPathOption, "_id") .option(MapRDBSourceConfig.CreateTableOption, false) .option("checkpointLocation", "/tmp/reviewdb") .option(MapRDBSourceConfig.BulkModeOption, true) .option(MapRDBSourceConfig.SampleSizeOption, 1000) writedb.start()</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5541" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211719.jpg" width="680" height="580" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211719.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211719-300x256.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='15-使用spark-sql查询mapr数据库json' id="boomdevs_15" >15. 使用Spark SQL查询MapR数据库JSON</h1> <p>Spark MapR数据库连接器使用户能够使用Spark数据集在MapR数据库之上执行复杂的SQL查询和更新,同时,应用投影和过滤器, 自定义分区和数据位置等关键技术。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5542" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211727.jpg" width="680" height="334" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211727.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211727-300x147.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='16-将数据从mapr数据库加载到spark数据集' id="boomdevs_16" >16. 将数据从MapR数据库加载到Spark数据集</h1> <p>要将MapR数据库JSON表中的数据加载到Apache Spark数据集中,我们在SparkSession对象上调用loadFromMapRDB方法,提供tableName、schema和case类。将返回带有产品评论模式的数据集:</p><pre class="crayon-plain-tag">val schema = StructType(Array( StructField("_id", StringType, true), StructField("asin", StringType, true), StructField("overall", DoubleType, true), StructField("reviewText", StringType, true), StructField("reviewTime", StringType, true), StructField("reviewerID", StringType, true), StructField("reviewerName", StringType, true), StructField("summary", StringType, true), StructField("label", StringType, true), StructField("prediction", StringType, true), StructField("unixReviewTime", LongType, true) )) var tableName: String = "/user/mapr/reviewtable" val df = spark .loadFromMapRDB(tableName, schema) df.createOrReplaceTempView("reviews")</pre><p> </p> <h1 id='17-使用spark-sql探索和查询产品评论数据' id="boomdevs_17" >17. 使用Spark SQL探索和查询产品评论数据</h1> <p>现在,我们可以直接查询连续流入MapR数据库的数据,以使用Spark DataFrame特定于域的语言或使用Spark SQL来提问。</p> <p>下面,我们使用DataFrame select和show方法以表格的格式显示前5行评论摘要、总体评级、标签和预测</p><pre class="crayon-plain-tag">df.select("summary","overall","label","prediction").show(5) result: +--------------------+-------+-----+----------+ | summary|overall|label|prediction| +--------------------+-------+-----+----------+ | Excellent Ammo Can| 5.0| 1.0| 1.0| | Glad I bought it| 5.0| 1.0| 1.0| |WILL BUY FROM AGA...| 5.0| 1.0| 1.0| |looked brand new ...| 5.0| 1.0| 1.0| | I LOVE THIS THING| 5.0| 1.0| 1.0| +--------------------+-------+-----+----------+</pre><p>什么是评级最高的产品?</p><pre class="crayon-plain-tag">df.filter($"overall" === 5.0) .groupBy("overall","asin") .count .orderBy(desc("count")).show(2) result: +-------+----------+-----+ |overall| asin|count| +-------+----------+-----+ | 5.0|B004TNWD40| 242| | 5.0|B004U8CP88| 201| +-------+----------+-----+</pre><p>在SQL中评分最高的产品是什么?</p><pre class="crayon-plain-tag">%sql SELECT asin,overall, count(overall) FROM reviews where overall=5.0 GROUP BY asin, overall order by count(overall) desc limit 2</pre><p>显示评分最高的产品评论文字:</p><pre class="crayon-plain-tag">df.select("summary","reviewText","overall","label","prediction").filter("asin='B004TNWD40'").show(5) result: +--------------------+--------------------+-------+-----+----------+ | summary| reviewText|overall|label|prediction| +--------------------+--------------------+-------+-----+----------+ | Awesome|This is the perfe...| 5.0| 1.0| 1.0| |for the price you...|Great first knife...| 5.0| 1.0| 1.0| |Great Mora qualit...|I have extensive ...| 4.0| 1.0| 1.0| | Amazing knife|All I can say is ...| 5.0| 1.0| 1.0| |Swedish Mil. Mora...|Overall a nice kn...| 4.0| 1.0| 1.0| +--------------------+--------------------+-------+-----+----------+</pre><p>或者在SQL中:</p><pre class="crayon-plain-tag">%sql select summary, label, prediction, overall from reviews where asin='B004TNWD40' order by overall desc</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5543" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211731.jpg" width="680" height="276" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211731.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211731-300x122.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>什么是评级最低的产品?</p><pre class="crayon-plain-tag">df.filter($"overall" === 1.0) .groupBy("overall","asin") .count.orderBy(desc("count")).show(2) result: +-------+----------+-----+ |overall| asin|count| +-------+----------+-----+ | 1.0|B00A17I99Q| 18| | 1.0|B00BGO0Q9O| 17| +-------+----------+-----+</pre><p>显示评级最低的产品评论文本:</p><pre class="crayon-plain-tag">df.select("summary","reviewText","overall","label","prediction") .filter("asin='B00A17I99Q'") .orderBy("overall").show(8) result: +--------------------+--------------------+-------+-----+----------+ | summary| reviewText|overall|label|prediction| +--------------------+--------------------+-------+-----+----------+ | DO NOT BUY!|Do your research ...| 1.0| 0.0| 0.0| | Returned it|I could not get t...| 1.0| 0.0| 0.0| | didn't do it for me|didn't like it. ...| 1.0| 0.0| 0.0| |Fragile, just lik...|Update My second....| 1.0| 0.0| 0.0|</pre><p>下面,我们计算连续存储在MapR数据库中的流数据的一些预测评估指标。真/假的数量:</p> <ul class="list-paddingleft-2"> <li>真正的积极因素是模型正确的积极情绪</li> <li>误报是模型错误积极情绪的频率</li> <li>真实的否定表明模型正常负面情绪的频率</li> <li>假阴性表示模型错误负面情绪的频率</li> </ul> <p></p><pre class="crayon-plain-tag">val lp = predictions.select("label", "prediction") val counttotal = predictions.count() val correct = lp.filter($"label" === $"prediction").count() val wrong = lp.filter(not($"label" === $"prediction")).count() val ratioWrong = wrong.toDouble / counttotal.toDouble val lp = predictions.select( "prediction","label") val counttotal = predictions.count().toDouble val correct = lp.filter($"label" === $"prediction") .count() val wrong = lp.filter("label != prediction") .count() val ratioWrong=wrong/counttotal val ratioCorrect=correct/counttotal val truen =( lp.filter($"label" === 0.0) .filter($"label" === $"prediction") .count()) /counttotal val truep = (lp.filter($"label" === 1.0) .filter($"label" === $"prediction") .count())/counttotal val falsen = (lp.filter($"label" === 0.0) .filter(not($"label" === $"prediction")) .count())/counttotal val falsep = (lp.filter($"label" === 1.0) .filter(not($"label" === $"prediction")) .count())/counttotal val precision= truep / (truep + falsep) val recall= truep / (truep + falsen) val fmeasure= 2 * precision * recall / (precision + recall) val accuracy=(truep + truen) / (truep + truen + falsep + falsen) results: counttotal: Double = 84160.0 correct: Double = 76925.0 wrong: Double = 7235.0 truep: Double = 0.8582461977186312 truen: Double = 0.05578659695817491 falsep: Double = 0.014543726235741445 falsen: Double = 0.07142347908745247 ratioWrong: Double = 0.08596720532319392 ratioCorrect: Double = 0.9140327946768061</pre><p></p> <h1 id='18-投影和过滤器下推到mapr数据库' id="boomdevs_18" >18. 投影和过滤器下推到MapR数据库</h1> <p>可以通过调用以下显示的explain方法来查看DataFrame查询的物理计划。在红色中,我们看到投影和过滤器下推,这意味着整个列和总结列的扫描以及整个列上的过滤器被下推到MapR数据库中,这意味着在将数据返回给Spark之前,扫描和过滤将在MapR数据库中进行。</p> <p>通过胜率表扫描中不必要的字段,投影下推可最大限度的减少MapR数据库和Spark引擎之间的数据传输。当表包含许多列时,他尤为有用。过滤器下推通过减少过滤数据是MapR数据库和Spark引擎之间传递的数据量来提高性能。</p><pre class="crayon-plain-tag">// notice projection of selected fields [summary] // notice PushedFilters: overall df.filter("overall > 3").select("summary").explain result: == Physical Plan == *(1) Project [summary#7] +- *(1) Filter (isnotnull(overall#2) && (overall#2 > 3.0)) +- *(1) Scan MapRDBRelation MapRDBTableScanRDD [summary#7,overall#2] PushedFilters: [IsNotNull(overall), GreaterThan(overall,3.0)], ReadSchema: struct<summary:string,overall:double></pre><p><strong>使用Apache Drill查询数据<br /> </strong></p> <p>Apache Drill是一个开源,低延迟的大数据查询引擎,可提供PB级的交互式SQL分析。Drill提供了一个大规模并行处理执行引擎,用于跨集群中的各个节点执行分布式查询处理。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5544" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211736.jpg" width="680" height="450" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211736.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211736-300x199.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>使用Drill,可以使用SQL以交互式方式查询和连接JSON,Parquet或CSV格式的文件,Hive和NoSQL存储中的数据,包括HBase,MapR-DB和Mongo,而不需要定义模式。MapR提供了一个Drill JDBC驱动程序,可用于将Java应用程序,BI工具(如SquirreL和Spotfire)连接到Drill。</p> <p>以下是使用Drill shell的示例SQL查询:</p> <p>使用命令启动Drill shell:</p><pre class="crayon-plain-tag">sqlline -u jdbc:drill:zk=localhost:5181 -n mapr -p mapr</pre><p>MapR数据库中存储了多少个流式产品评论?</p><pre class="crayon-plain-tag">select count(_id) as totalreviews from dfs.`/user/mapr/reviewtable`; result: +---------------+ | totalreviews | +---------------+ | 84160 | +---------------+</pre><p>每个评级有多少评论?</p><pre class="crayon-plain-tag">select overall, count(overall) as countoverall from dfs.`/user/mapr/reviewtable` group by overall order by overall desc; result: +----------+---------------+ | overall | countoverall | +----------+---------------+ | 5.0 | 57827 | | 4.0 | 20414 | | 2.0 | 3166 | | 1.0 | 2753 | +----------+---------------+</pre><p>哪个是评价最高的产品?</p><pre class="crayon-plain-tag">select overall, asin, count(*) as ratingcount, sum(overall) as ratingsum from dfs.`/user/mapr/reviewtable` group by overall, asin order by sum(overall) desc limit 5; result: +----------+-------------+--------------+------------+ | overall | asin | ratingcount | ratingsum | +----------+-------------+--------------+------------+ | 5.0 | B004TNWD40 | 242 | 1210.0 | | 5.0 | B004U8CP88 | 201 | 1005.0 | | 5.0 | B006QF3TW4 | 186 | 930.0 | | 5.0 | B006X9DLQM | 183 | 915.0 | | 5.0 | B004RR0N8Q | 165 | 825.0 | +----------+-------------+--------------+------------+</pre><p>哪些产品具有最积极的评价预测?</p><pre class="crayon-plain-tag">select prediction, asin, count(*) as predictioncount, sum(prediction) as predictionsum from dfs.`/user/mapr/reviewtable` group by prediction, asin order by sum(prediction) desc limit 5; result: +-------------+-------------+------------------+----------------+ | prediction | asin | predictioncount | predictionsum | +-------------+-------------+------------------+----------------+ | 1.0 | B004TNWD40 | 263 | 263.0 | | 1.0 | B004U8CP88 | 252 | 252.0 | | 1.0 | B006X9DLQM | 218 | 218.0 | | 1.0 | B006QF3TW4 | 217 | 217.0 | | 1.0 | B004RR0N8Q | 193 | 193.0 | +-------------+-------------+------------------+----------------+</pre><p>显示评价最高的产品评论摘要:</p><pre class="crayon-plain-tag">select summary, prediction from dfs.`/user/mapr/reviewtable` where asin='B004TNWD40' limit 5; result: +---------------------------------------------------+-------------+ | summary | prediction | +---------------------------------------------------+-------------+ | Awesome | 1.0 | | for the price you cant go wrong with this knife | 1.0 | | Great Mora quality and economy | 1.0 | | Amazing knife | 1.0 | | Swedish Mil. Mora Knife | 1.0 | +---------------------------------------------------+-------------+</pre><p>按照最积极的评价显示产品的评论标记:</p><pre class="crayon-plain-tag">select reviewTokens from dfs.`/user/mapr/reviewtable` where asin='B004TNWD40' limit 1; [ "awesome", "perfect", "belt/pocket/neck", "knife", "carbon", "steel", "blade", "last", "life", "time!", "handle", "sheath", "plastic", "cheap", "kind", "plastic", "durable", "also", "last", "life", "time", "everyone", "loves", "doors", "this!", "yes", "ones", "bone", "handles", "leather", "sheaths", "$100+" ]</pre><p>什么是评价最低的产品?</p><pre class="crayon-plain-tag">SELECT asin,overall, count(overall) as rcount FROM dfs.`/user/mapr/reviewtable` where overall=1.0 GROUP BY asin, overall order by count(overall) desc limit 2 result: +-------------+----------+---------+ | asin | overall | rcount | +-------------+----------+---------+ | B00A17I99Q | 1.0 | 18 | | B008VS8M58 | 1.0 | 17 | +-------------+----------+---------+</pre><p>什么是负面评论预测最多的产品?</p><pre class="crayon-plain-tag">select prediction, asin, count(*) as predictioncount, sum(prediction) as predictionsum from dfs.`/user/mapr/reviewtable` group by prediction, asin order by sum(prediction) limit 2; result: +-------------+-------------+------------------+----------------+ | prediction | asin | predictioncount | predictionsum | +-------------+-------------+------------------+----------------+ | 0.0 | B007QEUWSI | 4 | 0.0 | | 0.0 | B007QTHPX8 | 4 | 0.0 | +-------------+-------------+------------------+--------------+</pre><p>显示评价最低的产品的评论摘要</p><pre class="crayon-plain-tag">select summary from dfs.`/user/mapr/reviewtable` where asin='B00A17I99Q' and prediction=0.0 limit 5; result: +---------------------------------------------------------+ | summary | +---------------------------------------------------------+ | A comparison to Fitbit One -- The Holistic Wrist | | Fragile, just like the first Jawbone UP! Overpriced | | Great concept, STILL horrible for daily use | | Excellent idea, bad ergonomics, worse manufacturing... | | get size larger | +---------------------------------------------------------+</pre><p></p> <h1 id='19-使用mapr数据库shell查询数据' id="boomdevs_19" >19. 使用MapR数据库Shell查询数据</h1> <p>mapr dbshell是一个工具,可以创建和执行JSON表和文档的基本操作。在登录到MapR集群中的节点后,通过在命令行上键入mapr dbshell来运行dbshell。以下是使用MapR dbshell的一些示例查询:</p> <p>显示评价最高的产品的评论摘要、ID、预测(_id以B004TNWD40开头)。</p><pre class="crayon-plain-tag">find /user/mapr/reviewtable --where '{"$and":[{"$eq":{"overall":5.0}}, { "$like" : {"_id":"%B004TNWD40%"} }]}' --f _id,prediction,summary --limit 5 result: {"_id":"B004TNWD40_1256083200","prediction":1,"summary":"Awesome"} {"_id":"B004TNWD40_1257120000","prediction":1,"summary":"for the price you cant go wrong with this knife"} {"_id":"B004TNWD40_1279065600","prediction":1,"summary":"Amazing knife"} {"_id":"B004TNWD40_1302393600","prediction":1,"summary":"Great little knife"} {"_id":"B004TNWD40_1303257600","prediction":1,"summary":"AWESOME KNIFE"}</pre><p>显示带有负面情绪预测和标签的10种产品的评论摘要ID</p><pre class="crayon-plain-tag">find /user/mapr/reviewtable --where '{"$and":[{"$eq":{"prediction":0.0}},{"$eq":{"label":0.0}} ]}' --f _id,summary --limit 10 result: {"_id":"B003Y64RBA_1312243200","summary":"A $3.55 rubber band!"} {"_id":"B003Y64RBA_1399334400","summary":"cheap not worthy"} {"_id":"B003Y71V2C_1359244800","summary":"Couple of Problems"} {"_id":"B003Y73EPY_1349740800","summary":"Short Term Pedals - Eggbeaters 1"} {"_id":"B003Y9CMGY_1306886400","summary":"Expensive batteries."} {"_id":"B003YCWFRM_1336089600","summary":"Poor design"} {"_id":"B003YCWFRM_1377043200","summary":"Great while it lasted"} {"_id":"B003YD0KZU_1321920000","summary":"No belt clip!!! Just like the other reviewer..."} {"_id":"B003YD0KZU_1338768000","summary":"Useless"} {"_id":"B003YD1M5M_1354665600","summary":"Can't recomend this knife."}</pre><p></p> <h1 id='20-结语' id="boomdevs_20" >20. 结语</h1> <p>在本文中,学习了如何使用以下内容:</p> <ul class="list-paddingleft-2"> <li>Spark结构化流应用程序中的Spark机器学习模型</li> <li>Spark结构化流与MapR事件存储使用Kafka API来摄取消息</li> <li>Spark Structured Streaming可以持久保存到MapR数据库,以便持续快速地进行SQL分析</li> </ul> <p>刚讨论过的用例体系结构的所有组件都可以与MapR数据平台在同一个集群上运行。该MAPR数据平台继承了全球性的活动流,实时数据库的功能和可扩展的企业级存储和Spark 、Drill和机器学习库权限的下一代智能应用,内部搭载现代计算范式的发展优势基础设施。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5545" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211742.jpg" width="680" height="346" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211742.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_211742-300x153.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p> </p> <p>[source]<a href="https://mp.weixin.qq.com/s?__biz=MzU0OTE4MzYzMw==&mid=2247486706&idx=2&sn=2c83b938845fdd9eb32933cbbeb417c1&chksm=fbb2850cccc50c1a8f4227efbc97161ac9b1dcf083f450f0e92edccb3f5d22de31d3dfadced2&mpshare=1&scene=24&srcid=#rd">基于大数据的情绪分析(二)</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%ba%8c%ef%bc%89/">[转]基于大数据的情绪分析(二)</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%ba%8c%ef%bc%89/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item> <title>[转]基于大数据的情绪分析(一)</title> <link>http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%b8%80%ef%bc%89/</link> <comments>http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%b8%80%ef%bc%89/#respond</comments> <dc:creator><![CDATA[leelight]]></dc:creator> <pubDate>Mon, 17 Jun 2019 20:38:45 +0000</pubDate> <category><![CDATA[Theory & Solution]]></category> <category><![CDATA[Big Data]]></category> <guid isPermaLink="false">http://www.mobabel.net/?p=5505</guid> <description><![CDATA[<p>1. 导语 社交媒体、电子邮件、聊天、产品评论和推荐的文本挖掘和分析已经成为几乎所有行业垂直行业研究数据模式的宝贵资源,它能够帮助企业获得更多信息、更加了解客户、预测和增强客户体验、量身定制营销活动,并协助做决策。情感分析使用机器学习算法来确定文本内容是正面或负面。情感分析用例包括: 快速了解客户评论的基调 了解客户喜欢或不喜欢的产品或服务 了解可能影响新客户购买决策的因素 为企业提供市场意识 尽早解决问题 了解股市情绪以获得对金融信号预测的见解 确定人们对客户支持的看法 社交媒体监控 品牌/产品/公司人气/声誉/感知监控 不满意的客户检测监控和警报 营销活动监控/分析 客户服务意见监测/分析 品牌情绪态度分析 客户反馈分析 竞争情绪分析 品牌影响者监控 手动分析客户或潜在客户产生的大量文本非常耗时,机器学习更有效,通过流分析,可以实现并提供预测。 本文是系列文章中的第一篇,在这篇文章中我们讨论了将流数据与机器学习和快速存储相结合的数据管道的体系结构。在第一部分,将使用Spark机器学习数据管道探索情绪分析。我们将使用亚马逊产品评论的数据集,并构建机器学习模型,将评论分类为正面或负面。在本文的第二部分中,我们将使用机器学习模型与流数据实时分类文档。 在这篇文章中,我们将讨论以下内容: 分类和情感分析概念概述 从文本文档构建特征向量 培训机器学习模型,使用逻辑回归对正面和负面评论进行分类 评估和保存机器学习模型 2. 分类 分类是一种监督学习算法,它是基于标记数据(如电子邮件主题和消息文本)来识别所属类别(如,电子邮件是否为垃圾邮件)。用于分类的一些常见用例包括信用卡欺诈、垃圾电子邮件的检测和情绪分析。 分类使用的是有标签和预定特征的一组数据,并基于该信息学习如何标记新的记录,主要功能是进行预测。要构建分类器模型,需要探索和提取最有助于分类的特征。 让我们通过一个案例来分析正面或负面的文本分类。 想要预测什么? 在此示例中,客户评论评级用于将评论标记为肯定评论。4至5星的评论被认为是积极的评论,1至2星的评论被认为是负面评论。 可以使用哪些属性进行预测?...</p> The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%b8%80%ef%bc%89/">[转]基于大数据的情绪分析(一)</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></description> <content:encoded><![CDATA[<h1 id='1-导语' id="boomdevs_1" >1. 导语</h1> <p>社交媒体、电子邮件、聊天、产品评论和推荐的文本挖掘和分析已经成为几乎所有行业垂直行业研究数据模式的宝贵资源,它能够帮助企业获得更多信息、更加了解客户、预测和增强客户体验、量身定制营销活动,并协助做决策。<span id="more-5505"></span>情感分析使用机器学习算法来确定文本内容是正面或负面。情感分析用例包括:</p> <ul class="list-paddingleft-2"> <li>快速了解客户评论的基调 <ul class="list-paddingleft-2"> <li>了解客户喜欢或不喜欢的产品或服务</li> <li>了解可能影响新客户购买决策的因素</li> <li>为企业提供市场意识</li> <li>尽早解决问题</li> </ul> </li> <li>了解股市情绪以获得对金融信号预测的见解</li> <li>确定人们对客户支持的看法</li> <li>社交媒体监控</li> <li>品牌/产品/公司人气/声誉/感知监控</li> <li>不满意的客户检测监控和警报</li> <li>营销活动监控/分析</li> <li>客户服务意见监测/分析</li> <li>品牌情绪态度分析</li> <li>客户反馈分析</li> <li>竞争情绪分析</li> <li>品牌影响者监控</li> </ul> <p>手动分析客户或潜在客户产生的大量文本非常耗时,机器学习更有效,通过流分析,可以实现并提供预测。</p> <p>本文是系列文章中的第一篇,在这篇文章中我们讨论了将流数据与机器学习和快速存储相结合的数据管道的体系结构。在第一部分,将使用Spark机器学习数据管道探索情绪分析。我们将使用亚马逊产品评论的数据集,并构建机器学习模型,将评论分类为正面或负面。在本文的第二部分中,我们将使用机器学习模型与流数据实时分类文档。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5506" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203848.jpg" width="680" height="440" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203848.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203848-300x194.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>在这篇文章中,我们将讨论以下内容:</p> <ul class="list-paddingleft-2"> <li>分类和情感分析概念概述</li> <li>从文本文档构建特征向量</li> <li>培训机器学习模型,使用逻辑回归对正面和负面评论进行分类</li> <li>评估和保存机器学习模型</li> </ul> <h1 id='2-分类' id="boomdevs_2" >2. 分类</h1> <p>分类是一种监督学习算法,它是基于标记数据(如电子邮件主题和消息文本)来识别所属类别(如,电子邮件是否为垃圾邮件)。用于分类的一些常见用例包括信用卡欺诈、垃圾电子邮件的检测和情绪分析。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5507" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203854.jpg" width="680" height="289" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203854.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203854-300x128.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>分类使用的是有标签和预定特征的一组数据,并基于该信息学习如何标记新的记录,主要功能是进行预测。要构建分类器模型,需要探索和提取最有助于分类的特征。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5508" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203858.jpg" width="680" height="440" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203858.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203858-300x194.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>让我们通过一个案例来分析正面或负面的文本分类。</p> <ul class="list-paddingleft-2"> <li>想要预测什么? <ul class="list-paddingleft-2"> <li>在此示例中,客户评论评级用于将评论标记为肯定评论。4至5星的评论被认为是积极的评论,1至2星的评论被认为是负面评论。</li> </ul> </li> <li>可以使用哪些属性进行预测? <ul class="list-paddingleft-2"> <li>审核文本单词用作发现正面或负面相似性的功能,以便将客户文本情绪分类为正面或负面。</li> </ul> </li> </ul> <h1 id='3-机器学习工作流程' id="boomdevs_3" >3. 机器学习工作流程</h1> <p>使用机器学习是一个迭代过程,涉及:</p> <ol class="list-paddingleft-2"> <li>数据发现和模型创建 <ul class="list-paddingleft-2"> <li>分析历史数据</li> <li>由于格式,大小或结构,识别传统分析或数据库未使用的新数据源</li> <li>跨多个数据源收集,关联和分析数据</li> <li>了解并应用正确类型的机器学习算法以从数据中获取价值</li> <li>培训,测试和评估机器学习算法的结果以构建模型</li> </ul> </li> <li>在生产中使用模型进行预测</li> <li>数据发现和使用新数据更新模型</li> </ol> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5509" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203906.jpg" width="680" height="530" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203906.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203906-300x234.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='4-特征提取' id="boomdevs_4" >4. 特征提取</h1> <p>特征是数据中可用于进行预测的有趣属性。特征提取是将原始数据转换为机器学习算法的输入过程。为了在Spark机器学习算法中使用,必须将特征放入特征向量中,特征向量是表示每个特征的值的数字向量。要构建分类器模型,需要提取并测试,从而找到最有助于分类的感兴趣的特征。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5510" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203914.jpg" width="680" height="406" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203914.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203914-300x179.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h2 id='4-1-apache-spark用于文本特征提取' id="boomdevs_5" >4-1. Apache Spark用于文本特征提取</h2> <p>SparkMLlib中的TF-IDF(术语频率-逆文档频率)特征提取器可用于将文本转换为特征向量。与文档集合相比,TF-IDF计算单个文档中最重要的单词。对于文档集合中的每个单词,它计算:</p> <ul class="list-paddingleft-2"> <li>术语频率(TF),是特定文档中单词出现的次数</li> <li>文档频率(DF),它是文档集合中单词出现的次数</li> <li>术语频率 – 逆文档频率(TF-IDF),它测量文档中单词的重要性(该单词在该文档中出现很多,但在文档集合中很少见)</li> </ul> <p>例如,如果有关于自行车配件的评论集合,那么评论中的“returned”一词对于该文档而言比“bike”这个词更重要。在下面的简单示例中,有一个正面文本文档和一个负面文本文档,其中包含“love”、“bike”和“returned”(在过滤后删除无关紧要的单词,如“我”)。显示了TF、DF和TF-IDF计算。单词“bike”的TF为1:2文档(每个文档中的字数),文档频率为2(文档集中的字数),TF-IDF为1/2(TF除以DF)。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5511" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203919.png" width="680" height="367" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203919.png 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203919-300x162.png 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='5-logistic回归' id="boomdevs_6" >5. Logistic回归</h1> <p>逻辑回归是预测二元响应的常用方法,这是广义线性模型的一个特例,它可以预测结果的概率。逻辑回归通过使用逻辑函数估计概率来测量“Y”标签和X“特征”之间的关系。该模型预测概率,用于预测标签类。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5512" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203926.png" width="680" height="414" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203926.png 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203926-300x183.png 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>在我们的文本分类案例中,给定TF-IDF值的标签和特征向量,逻辑回归试图预测评论文本为正或负的概率。Logistic回归通过将每个TF-IDF特征乘以权重并将总和通过sigmoid函数找到文本集合中每个单词的最佳拟合权重,该函数将输入x转换为输出y,即得到0到1之间的数字,逻辑回归可以理解为找到最合适的参数:</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5513" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203932.png" width="680" height="379" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203932.png 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203932-300x167.png 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5514" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203939.jpg" width="680" height="409" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203939.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203939-300x180.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>Logistic回归具有以下优点:</p> <ul class="list-paddingleft-2"> <li>可以处理稀疏数据</li> <li>快速训练</li> <li>重量可以解释 <ul class="list-paddingleft-2"> <li>正权重将对应于积极的词</li> <li>负权重将对应于否定的词</li> </ul> </li> </ul> <h1 id='6-数据探索和特征提取' id="boomdevs_7" >6. 数据探索和特征提取</h1> <p>我们将使用亚马逊体育和户外产品评论数据的数据集,数据集有以下模式,对显示的字段进行情绪分析:</p> <p>reviewerID – 评论者的ID,例如,A2SUAM1J3GNN3B<br /> asin – 产品的ID,例如,0000013714<br /> reviewerName – 评论者的名称<br /> 有用 – 评论的有用性评级,例如,2/3<br /> reviewText – 评论文本<br /> overall – 产品评级<br /> summary -审查摘要<br /> unixReviewTime – 审查时间(Unix时间)<br /> reviewTime – 审查时间(原始)</p> <p>数据集具有以下JSON格式:</p><pre class="crayon-plain-tag">{ "reviewerID": "A1PUWI9RTQV19S", "asin": "B003Y5C132", "reviewerName": "kris", "helpful": [0, 1], "reviewText": "A little small in hind sight, but I did order a .30 cal box. Good condition, and keeps my ammo organized.", "overall": 5.0, "summary": "Nice ammo can", "unixReviewTime": 1384905600, "reviewTime": "11 20, 2013" }</pre><p>在这种情况下,我们将使用逻辑回归来预测正面或负面的标签,基于以下内容:</p> <p>标签:</p> <ul class="list-paddingleft-2"> <li>总体而言 – 产品评级4-5 = 1正面</li> <li>总体而言 – 产品评级1-2 = 0否定</li> </ul> <p>特征:</p> <ul class="list-paddingleft-2"> <li>reviewText +评论摘要→TF-IDF功能</li> </ul> <h1 id='7-使用spark-ml' id="boomdevs_8" >7. 使用Spark ML</h1> <p>Spark ML提供了一套基于DataFrames的统一的高级API,旨在使机器学习变得简单易用。在DataFrames之上构建ML API可以提供分区数据处理的可扩展性,并且易于使用SQL进行数据操作。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5515" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203944.jpg" width="680" height="532" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203944.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203944-300x235.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>我们将使用ML Pipeline通过变换器传递数据以提取特征和估计器来生成模型。</p> <ul class="list-paddingleft-2"> <li>变压器:变压器是一种将一种变换<code>DataFrame</code>为另一种变换器的算法<code>DataFrame</code>。我们将使用变换器来获得<code>DataFrame</code>具有特征向量列的变量。</li> <li>估计器:估算器是一种算法,可以适合<code>DataFrame</code>生成变压器。我们将使用估计器来训练模型,该模型可以转换输入数据以获得预测。</li> <li>管道:管道将多个变换器和估算器链接在一起以指定ML工作流程。</li> </ul> <h1 id='8-将文本中的数据加载到dataframe' id="boomdevs_9" >8. 将文本中的数据加载到DataFrame</h1> <p>第一步将数据加载到DataFrame,我们指定数据源格式和加载到的路径,接着使用withColum方法添加一个将审阅摘要与审阅文本相结合的示例,然后删除不需要的列。</p><pre class="crayon-plain-tag">import org.apache.spark._ import org.apache.spark.ml._ import org.apache.spark.sql._ var file ="/user/mapr/data/revsporttrain.json" val df0 = spark.read.format("json") .option("inferSchema", "true") .load(file) val df = df0.withColumn("reviewTS", concat($"summary", lit(" "),$"reviewText")) .drop("helpful") .drop("reviewerID") .drop("reviewerName") .drop("reviewTime")</pre><p>该DataFrame printSchema显示模式:</p><pre class="crayon-plain-tag">df.printSchema root |-- asin: string (nullable = true) |-- overall: double (nullable = true) |-- reviewText: string (nullable = true) |-- summary: string (nullable = true) |-- unixReviewTime: long (nullable = true) |-- reviewTS: string (nullable = true)</pre><p>该DataFrame show方法显示前20行或指定的行数:</p><pre class="crayon-plain-tag">df.show(5)<img class="img_loading" src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==" crossorigin="anonymous" data-ratio="0.16842105263157894" data-src="https://mmbiz.qpic.cn/mmbiz_png/mkjCniaBPibTIlfg0ELYypDbibEXu5umu0eyS3aGsGV5FUEicN7YnpMv6yMysPic72QplCQHN71LJOTNImHGvvp4YQg/640?wx_fmt=png" data-type="png" data-w="570" /></pre><p></p> <h1 id='9-摘要统计' id="boomdevs_10" >9. 摘要统计</h1> <p>Spark DataFrames包含一些用于统计处理的内置函数。该describe()函数对所有数字列执行摘要统计计算,并将它们作为a返回DataFrame。下面,我们分析产品评级:</p><pre class="crayon-plain-tag">df.describe("overall").show result: +-------+------------------+ |summary| overall| +-------+------------------+ | count| 200000| | mean| 4.395105| | stddev|0.9894654790262587| | min| 1.0| | max| 5.0| +-------+------------------+</pre><p>在下面的代码中,我们过滤去除中性评价(=3),然后使用Spark Bucketizer将标签0/1列添加到数据集中为Positive(总评级>=4)而不是正数(总评级<4)评论。然后,显示结果总计数。通过标签列对数据进行分组并计算每个分组中的实例数量,显示正样本的数量大约是负样本的13倍。</p><pre class="crayon-plain-tag">val df1 = df.filter("overall !=3") val bucketizer = new Bucketizer() .setInputCol("overall") .setOutputCol("label") .setSplits(Array(Double.NegativeInfinity, 4.0, Double.PositiveInfinity)) val df2= bucketizer.transform(df1) df2.groupBy("overall","label").count.show result: +-------+-----+------+ |overall|label| count| +-------+-----+------+ | 2.0| 0.0| 6916| | 5.0| 1.0|127515| | 1.0| 0.0| 6198| | 4.0| 1.0| 43303| +-------+-----+------+</pre><p></p> <h1 id='10-分层抽样' id="boomdevs_11" >10. 分层抽样</h1> <p>为了确保模型对负样本敏感,我们可以使用分层抽样将两种样本类型放在同一个基础上。sampleBy()当提供要返回的每个样本类型的分数时,DataFrames函数执行此操作。在这里,保留所有负实例,但将负实例下采样到10%,然后显示结果。</p><pre class="crayon-plain-tag">val fractions = Map(1.0 -> .1, 0.0 -> 1.0) val df3 = df2.stat.sampleBy("label", fractions, 36L) df3.groupBy("label").count.show result: +-----+-----+ |label|count| +-----+-----+ | 0.0|13114| | 1.0|17086| +-----+-----+</pre><p>下面,数据被分成训练集和测试集:80%的数据用于训练模型,20%用于测试。</p><pre class="crayon-plain-tag">// split into training and test dataset val splitSeed = 5043 val Array(trainingData, testData) = df3.randomSplit(Array(0.8, 0.2), splitSeed)</pre><p></p> <h1 id='11-特征提取与流水线' id="boomdevs_12" >11. 特征提取与流水线</h1> <p>ML需要将标签和特征向量作为列添加到输入中DataFrame,我们设置一个管道来通过变换器传递数据,以便提取特征和标签。</p> <p>在RegexTokenizer采用输入文本列,并返回一个DataFrame通过使用所提供的正则表达式模式与文本分割的一个附加列的矩阵。将StopWordsRemover过滤出的话,应被排除在外,因为词频繁出现,不进行尽可能多的意义,如“I”、“is”。</p> <p>在下面的代码中,RegexTokenizer将使用review和summary文本将列拆分为一个包含单词数组的列,然后由以下内容过滤StopWordsRemover:</p><pre class="crayon-plain-tag">val tokenizer = new RegexTokenizer() .setInputCol("reviewTS") .setOutputCol("reviewTokensUf") .setPattern("\\s+|[,.()\"]") val remover = new StopWordsRemover() .setStopWords(StopWordsRemover .loadDefaultStopWords("english")) .setInputCol("reviewTokensUf") .setOutputCol("reviewTokens")</pre><p>作为输入栏review以及将所述reviewTokens的过滤柱的话,如下所示:</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5516" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203952.png" width="680" height="213" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203952.png 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203952-300x94.png 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>CountVectorizer用于将前一步骤中的单词标记数组转换为单词标记计数的向量。该CountVectorizer正在执行的TF-IDF特征提取的TF部分。</p><pre class="crayon-plain-tag">val cv = new CountVectorizer() .setInputCol("reviewTokens") .setOutputCol("cv") .setVocabSize(200000)</pre><p>下面显示了CountVectorizer作为输入列reviewTokens并添加cv矢量化字数列的结果的示例。在cv专栏中:56004是TF词汇的大小;第二个数组是是词语的位置,该词汇按语料库中的术语频率排序;第三组数组是reviewTokens文本中单词(TF)的计数。</p> <p>在cv列下方,由CountVectorizer(TF-IDF特征提取的TF部分)创建,是IDF的输入。IDF采用从CountVectorizer向下和向下权重特征创建的特征向量,这些特征向量经常出现在文本集合中(TF-IDF特征提取的IDF部分)。输出features列是TF-IDF特征向量,逻辑回归函数将使用该向量。</p><pre class="crayon-plain-tag">// list of feature columns val idf = new IDF() .setInputCol("cv") .setOutputCol("features")</pre><p>以下显示了IDF的结果,它作为输入列cv并添加了features矢量化TF-IDF的列。在cv专栏中,56004是单词词汇的大小;第二个数组是词语词汇中词语的位置,该词汇按语料库中术语频率排序;第三个数组是reviewTokens文本中单词的TF-IDF。</p> <p>管道中的最后一个元素是估算器,一个逻辑回归分类器,它将训练标签和特征向量并返回模型。</p><pre class="crayon-plain-tag">// create Logistic Regression estimator // regularizer parameters avoid overfitting val lr = new LogisticRegression() .setMaxIter(100) .setRegParam(0.02) .setElasticNetParam(0.3)</pre><p>下面,我们把Tokenizer、CountVectorizer、IDF和Logistic回归分类的管道。管道将多个变换器和估计器链接在一起,以指定用于训练和使用模型的ML工作流程。</p><pre class="crayon-plain-tag">val steps = Array( tokenizer, remover, cv, idf,lr) val pipeline = new Pipeline().setStages(steps)</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5517" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203956.jpg" width="680" height="327" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203956.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_203956-300x144.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <h1 id='12-训练模型' id="boomdevs_13" >12. 训练模型</h1> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5518" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204001.jpg" width="680" height="478" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204001.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204001-300x211.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>接下来,我们使用弹性网络正则化训练逻辑回归模型。通过在输入特征和与这些特征相关联的标记输出之间建立关联来训练模型。该pipeline.fit方法返回适合的管道模型。</p><pre class="crayon-plain-tag">val model = pipeline.fit(trainingData)</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5519" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204007.jpg" width="680" height="509" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204007.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204007-300x225.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>注意:训练模型的另一个选项是使用网络搜索调整参数,并选择最佳模型,使用Spark CrossValidator和ParamGridBuilder进行K交叉验证。</p> <p>接下来,我们可以从拟合的管道模型中获取CountVectorizer和LogisticRegression模型,以便打印醋文本词汇中单词的系数权重(单词特征重要性)。</p><pre class="crayon-plain-tag">// get vocabulary from the CountVectorizer val vocabulary = model.stages(2) .asInstanceOf[CountVectorizerModel] .vocabulary // get the logistic regression model val lrModel = model.stages.last .asInstanceOf[LogisticRegressionModel] // Get array of coefficient weights val weights = lrModel.coefficients.toArray // create array of word and corresponding weight val word_weight = vocabulary.zip(weights) // create a dataframe with word and weights columns val cdf = sc.parallelize(word_weight) .toDF("word","weights")</pre><p>回想一下,逻辑回归生成公式的系数权重,以预测特征x(在本例中为单词)的出现概率,以最大化结果Y,1或0的概率(在这种情况下,正面或负面文本)情绪。权重可以解释为:</p> <ul class="list-paddingleft-2"> <li>正权重将对应于积极的词</li> <li>负权重将对应于否定的词</li> </ul> <p>下面我们按降序对权重进行排序,以显示最积极的单词。结果表明</p> <p>“great”、“perfect”、“easy”、“works”和“excellent”是最重要的积极语言。</p><pre class="crayon-plain-tag">// show the most positive weighted words cdf.orderBy(desc("weights")).show(10) result: +---------+-------------------+ | word| weight| +---------+-------------------+ | great| 0.6078697902359276| | perfect|0.34404726951273945| |excellent|0.28217372351853814| | easy|0.26293906850341764| | love|0.23518819188672227| | works| 0.229342771859023| | good| 0.2116386469012886| | highly| 0.2044040462730194| | nice|0.20088266981583622| | best|0.18194893152633945| +---------+-------------------+</pre><p>下面,我们按照升序对权重进行排序,以显示最负面的词。结果表明“returned”、</p> <p>“poor”、“waste”和“useless”是最重要的否定词。</p><pre class="crayon-plain-tag">// show the most negative sentiment words cdf.orderBy("weights").show(10) result: +-------------+--------------------+ | word| weight| +-------------+--------------------+ | returned|-0.38185206877117467| | poor|-0.35366409294425644| | waste| -0.3159724826017525| | useless| -0.2914292653060789| | return| -0.2724012497362986| |disappointing| -0.2666580559444479| | broke| -0.2656765359468423| | disappointed|-0.23852780960293438| | returning|-0.22432617475366876| | junk|-0.21457169691127467| +-------------+--------------------+</pre><p></p> <h1 id='13-预测和模型评估' id="boomdevs_14" >13. 预测和模型评估</h1> <p>可以使用尚未用于任何训练的测试数据集来确定模型的性能。我们DataFrame使用管道模型转换测试,管道模型将根据管道步骤传递测试数据,通过特征提取阶段,使用逻辑回归模型进行估计,然后将标签预测返回到新的列中DataFrame。</p><pre class="crayon-plain-tag">val predictions = model.transform(testData)</pre><p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5520" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204014.jpg" width="980" height="987" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204014.jpg 980w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204014-150x150.jpg 150w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204014-298x300.jpg 298w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204014-768x773.jpg 768w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204014-160x160.jpg 160w" sizes="(max-width: 980px) 100vw, 980px" /></p> <p>BinaryClassificationEvaluator提供了一个度量标准来衡量拟合模型对测试数据的影响程度。此评估程序的默认度量标准是ROC曲线下的区域,该区域测量了测试从误报中正确分类正面的能力。值越接近1,预测越好。</p> <p><img loading="lazy" decoding="async" class="alignnone size-full wp-image-5521" src="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204022.jpg" width="680" height="509" srcset="http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204022.jpg 680w, http://www.mobabel.net/wp-content/uploads/2019/06/17/4ffce04d92a4d6cb21c1494cdfcd6dc1_204022-300x225.jpg 300w" sizes="(max-width: 680px) 100vw, 680px" /></p> <p>下面,我们将预测DataFrame(具有rawPrediction列和标签列)传递给</p> <p>BinaryClassificationEvaluator,返回0.93作为ROC曲线下的区域。</p><pre class="crayon-plain-tag">val evaluator = new BinaryClassificationEvaluator() val areaUnderROC = evaluator.evaluate(predictions) result: 0.9350783400583272</pre><p>下面,计算更多的指标。</p> <ul class="list-paddingleft-2"> <li>真正的积极因素是模型正确预测积极情绪的频率。</li> <li>误报是模型错误地预测积极情绪的频率。</li> <li>真实的否定表明模型正确预测负面情绪的频率。</li> <li>假阴性表示模型错误地预测负面情绪的频率。</li> </ul> <p></p><pre class="crayon-plain-tag">val lp = predictions.select("label", "prediction") val counttotal = predictions.count() val correct = lp.filter($"label" === $"prediction").count() val wrong = lp.filter(not($"label" === $"prediction")).count() val ratioWrong = wrong.toDouble / counttotal.toDouble val lp = predictions.select( "prediction","label") val counttotal = predictions.count().toDouble val correct = lp.filter($"label" === $"prediction") .count() val wrong = lp.filter("label != prediction") .count() val ratioWrong=wrong/counttotal val ratioCorrect=correct/counttotal val truen =( lp.filter($"label" === 0.0) .filter($"label" === $"prediction") .count()) /counttotal val truep = (lp.filter($"label" === 1.0) .filter($"label" === $"prediction") .count())/counttotal val falsen = (lp.filter($"label" === 0.0) .filter(not($"label" === $"prediction")) .count())/counttotal val falsep = (lp.filter($"label" === 1.0) .filter(not($"label" === $"prediction")) .count())/counttotal val precision= truep / (truep + falsep) val recall= truep / (truep + falsen) val fmeasure= 2 precision recall / (precision + recall) val accuracy=(truep + truen) / (truep + truen + falsep + falsen) result: counttotal: 6112.0 correct: 5290.0 wrong: 822.0 ratioWrong: 0.13448952879581152 ratioCorrect: 0.8655104712041884 truen: 0.3417866492146597 truep: 0.5237238219895288 falsen: 0.044829842931937175 falsep: 0.08965968586387435 precision: 0.8538276873833023 recall: 0.9211510791366907 fmeasure: 0.8862126245847176 accuracy: 0.8655104712041886</pre><p> </p> <p>下面,打印出摘要并查看评论中负面情绪概率最高的:</p><pre class="crayon-plain-tag">predictions.filter($"prediction" === 0.0) .select("summary","reviewTokens","overall","prediction") .orderBy(desc("rawPrediction")).show(5) result: +--------------------+--------------------+-------+----------+ | summary| reviewTokens|overall|prediction| +--------------------+--------------------+-------+----------+ | Worthless Garbage!|[worthless, garba...| 1.0| 0.0| |Decent but failin...|[decent, failing,...| 1.0| 0.0| |over rated and po...|[rated, poorly, m...| 2.0| 0.0| |dont waste your m...|[dont, waste, mon...| 1.0| 0.0| |Cheap Chinese JUNK! |[cheap, chinese,....| 1.0| 0.0| +--------------------+--------------------+-------+----------+</pre><p>下面,打印出正面情绪概率最高的评论:</p><pre class="crayon-plain-tag">predictions.filter($"prediction" === 1.0) .select("summary","reviewTokens","overall","prediction") .orderBy("rawPrediction").show(5) result: +--------------------+--------------------+-------+----------+ | summary| reviewTokens|overall|prediction| +--------------------+--------------------+-------+----------+ | great|[great, excellent...| 5.0| 1.0| |Outstanding Purchase|[outstanding, pur...| 5.0| 1.0| |A fantastic stov....|[fantastic, stov....| 5.0| 1.0| |Small But Delight...|[small, delightfu...| 5.0| 1.0| |Kabar made a good...|[kabar, made, goo...| 5.0| 1.0| +--------------------+--------------------+-------+----------+</pre><p></p> <h1 id='14-保存模型' id="boomdevs_15" >14. 保存模型</h1> <p>现在可以将适合的管道模型保存到分布式文件存储中,以便以后在生产中使用。这样可以在管道中保存特征提取和逻辑回归模型。</p><pre class="crayon-plain-tag">var dir = "/user/mapr/sentmodel/" model.write.overwrite().save(dir)</pre><p>保存管道模型的结果是用于元数据的JSON文件和用于数据的Parquet文件。我们可以使用load命令重新加载模型,原始和重新加载的模型是相同的:</p><pre class="crayon-plain-tag">val sameModel = org.apache.spark.ml.PipelineModel.load(modeldirectory)</pre><p></p> <h1 id='15-结语' id="boomdevs_16" >15. 结语</h1> <p>有很多很棒的工具来构建分类模型。Apache Spark提供了一个出色的框架,用于构建可以从大量分布式数据,集中提取有价值的业务问题的解决方案。机器学习算法无法完美地回答所有问题。但是,它确实为人类在预测结果时提供了依据。</p> <p> </p> <p>[source]<a href="https://mp.weixin.qq.com/s?__biz=MzUzNDQ0OTY1Ng==&mid=2247483847&idx=1&sn=6c349e9f45304ffc9f599667e6aedba3&chksm=fa95dd50cde25446f1c6f0dc69766df6d8670a8ec19231a7cf86d2be3d283dbec7544720bf07&mpshare=1&scene=24&srcid=#rd">基于大数据的情绪分析(一)</a></p>The post <a href="http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%b8%80%ef%bc%89/">[转]基于大数据的情绪分析(一)</a> first appeared on <a href="http://www.mobabel.net">Mobabel</a>.]]></content:encoded> <wfw:commentRss>http://www.mobabel.net/%e8%bd%ac%e5%9f%ba%e4%ba%8e%e5%a4%a7%e6%95%b0%e6%8d%ae%e7%9a%84%e6%83%85%e7%bb%aa%e5%88%86%e6%9e%90%ef%bc%88%e4%b8%80%ef%bc%89/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> </channel> </rss> <!-- Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/ Page Caching using Disk: Enhanced (Requested URI contains query) Served from: www.mobabel.net @ 2024-12-03 21:45:38 by W3 Total Cache -->