├── CONTRIBUTING.rst ├── LICENSE ├── README.md ├── SUMMARY.md ├── book.json ├── deploying ├── running-spark-on-yarn.md ├── spark-standalone-mode.md └── submitting-applications.md ├── graphx-programming-guide ├── README.md ├── examples.md ├── getting-started.md ├── graph-algorithms.md ├── graph-builders.md ├── graph-operators.md ├── optimized-representation.md ├── pregel-api.md ├── property-graph.md └── vertex-and-edge-rdds.md ├── img ├── data_parallel_vs_graph_parallel.png ├── flume.png ├── graph_analytics_pipeline.png ├── property_graph.png ├── streaming-arch.png ├── streaming-dstream-ops.png ├── streaming-dstream-window.png ├── streaming-dstream.png ├── streaming-flow.png ├── streaming-kinesis-arch.png ├── tables_and_graphs.png └── triplet.png ├── mllib ├── README.md ├── basic_statistics.md ├── binary_classification.md ├── classification_and_regression.md ├── coordinatematrix.md ├── correlations.md ├── data_type.md ├── distributed_matrix.md ├── evaluation_metrics.md ├── examples.md ├── hypothesis_testing.md ├── indexedrowmatrix.md ├── labeled_point.md ├── linear_methods.md ├── local_matrix.md ├── local_vector.md ├── logistic_regression.md ├── loss_function.md ├── mathematical_formulation.md ├── optimization.md ├── random_data_generation.md ├── regularizers.md ├── rowmatrix.md ├── stratified_sampling.md ├── summary_statistics.md └── svms.md ├── more └── spark-configuration.md ├── programming-guide ├── README.md ├── from-here.md ├── initializing-spark.md ├── linking-with-spark.md ├── rdds │ ├── README.md │ ├── actions.md │ ├── external-datasets.md │ ├── parallelized-collections.md │ ├── passing-functions-to-spark.md │ ├── rdd-operations.md │ ├── rdd-persistences.md │ ├── rdd_persistence.md │ ├── transformations.md │ └── working-with-key-value-pairs.md └── shared-variables.md ├── quick-start ├── README.md ├── self-contained-applications.md ├── standalone-applications.md ├── using-spark-shell.md └── where-to-go-from-here.md ├── spark-sql ├── README.md ├── compatibility-with-other-systems │ ├── compatibility-with-apache-hive.md │ └── migration-guide-shark-user.md ├── data-sources │ ├── README.md │ ├── hive-tables.md │ ├── jSON-datasets.md │ ├── parquet-files.md │ └── rdds.md ├── getting-started.md ├── other-sql-interfaces.md ├── performance-tuning.md ├── spark-sql-dataType-reference.md └── writing-language-integrated-relational-queries.md ├── spark-streaming ├── README.md ├── a-quick-example.md ├── basic-concepts │ ├── README.md │ ├── caching-persistence.md │ ├── checkpointing.md │ ├── custom-receiver.md │ ├── deploying-applications.md │ ├── discretized-streams.md │ ├── flume-integration-guide.md │ ├── initializing-StreamingContext.md │ ├── input-DStreams.md │ ├── kafka-integration-guide.md │ ├── kinesis-integration.md │ ├── linking.md │ ├── monitoring-applications.md │ ├── output-operations-on-DStreams.md │ └── transformations-on-DStreams.md ├── fault-tolerance-semantics │ └── README.md └── performance-tuning │ ├── README.md │ ├── memory-tuning.md │ ├── reducing-processing-time.md │ └── setting-right-batch-size.md └── translation.json /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | * 參考[swift正體中文專案](https://github.com/tommy60703/the-swift-programming-language-in-traditional-chinese)的貢獻方式 2 | 貢獻力量 3 | -------- 4 | 如果想做出貢獻的話,你可以: 5 | 6 | - 幫忙校對,挑錯別字、語病等等 7 | - 提出修改建議 8 | - 提出術語翻譯建議 9 | 10 | 翻譯建議 11 | -------- 12 | 如果你願意一起校對的話,請仔細閱讀: 13 | 14 | - 使用 markdown 進行翻譯,文件名必須使用英文,因為中文的話 gitbook 編譯的時候會出問題 15 | - 引號請使用「」和『』 16 | - fork 過去之後新建一個分支進行翻譯,完成後 pull request 這個分支,沒問題的話我會合併到 master 分支中 17 | - 有其他任何問題都歡迎發 issue 18 | 19 | 關於術語 20 | -------- 21 | 翻譯術語的時候請參考這個流程: 22 | 23 | - 盡量保證與台灣習慣術語和已翻譯的內容一致 24 | - 盡量先搜尋,一般來說程式語言的大部分術語是一樣的,可以參考[這個網站](http://jjhou.boolan.com/terms.htm) 25 | - 如果以上兩條都沒有找到合適的結果,請自己決定一個合適的翻譯或者直接使用英文原文,後期校對的時候會進行統一 26 | - 校稿時,若有發現沒有被翻譯成台灣術語的大陸術語,可以將它新增到 translation.json 中 27 | - 可以主動提交替換過的文本給我,或是僅提交新增過的 translation.json 也可,我會再進行文本的替換 28 | - 請務必確定提交的翻譯對照組不會造成字串循環替代(ex: 因為「類」->「類別」,造成下次再執行自動翻譯時「類別」又變成「類別別」) 29 | 30 | 對翻譯有任何意見都歡迎發 issue,我看到了會盡快回覆 31 | 32 | 參考流程 33 | -------- 34 | 有些朋友可能不太清楚如何幫忙翻譯,我這裡寫一個簡單的流程,大家可以參考一下: 35 | 36 | 1. 首先 fork 我的項目(在專案畫面右上角) 37 | 2. 把 fork 過去的項目也就是你的項目 clone 到你的本機端 38 | 3. 在命令行運行 ``git branch develop`` 來創建一個新分支 39 | 4. 運行 ``git checkout develop`` 來切換到新分支 40 | 5. 運行 ``git remote add upstream https://github.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw.git`` 把我的庫添加為遠端庫 41 | 6. 運行 ``git remote update`` 更新 42 | 7. 運行 ``git fetch upstream`` 拉取我的庫的更新到本地 43 | 8. 運行 ``git merge upstream/master`` 將我的更新合併到你的分支 44 | 45 | 這是一個初始化流程,只需要做一遍就行,之後請一直在 develop 分支進行修改(可以針對不同的修改開不同的分支)。 46 | 47 | 每次要修改貢獻之前,請重複 6、7、8 步,將master更新到最新狀態之後,再開新的分支進行修改。 48 | 49 | 提交新版本 50 | -------- 51 | 如果你有更新項目需要提交,請參考以下流程: 52 | 53 | 1. commit 你的更新,並於更新訊息簡單說明更新的內容。如果更新很多檔案,請分批提交 54 | 2. 首先 push 到你的Github 55 | 3. 然後登錄 GitHub,在你的 repo 的首頁可以看到一個 ``pull request`` 按鈕,點擊它,填寫一些說明資訊,然後提交即可。 56 | 57 | 常見問題 58 | -------- 59 | Q: merge 衝突啦 怎麼辦 60 | A: 每次要修改前,請先重新pull遠端的程式庫,保證目前是最新的版本。如果發生錯誤請參考[這篇](https://github.com/doggy8088/Learn-Git-in-30-days/blob/master/docs/17%20%E9%97%9C%E6%96%BC%E5%90%88%E4%BD%B5%E7%9A%84%E5%9F%BA%E6%9C%AC%E8%A7%80%E5%BF%B5%E8%88%87%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F.markdown) 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | License 2 | 3 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. 4 | 5 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 6 | 7 | 1. Definitions 8 | 9 | "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. 10 | "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. 11 | "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. 12 | "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. 13 | "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. 14 | "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. 15 | "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. 16 | "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. 17 | "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 18 | 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 19 | 20 | 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: 21 | 22 | to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; 23 | to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; 24 | to Distribute and Publicly Perform the Work including as incorporated in Collections; and, 25 | to Distribute and Publicly Perform Adaptations. 26 | The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved, including but not limited to the rights set forth in Section 4(d). 27 | 28 | 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: 29 | 30 | You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. 31 | You may not exercise any of the rights granted to You in Section 3 above in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. The exchange of the Work for other copyrighted works by means of digital file-sharing or otherwise shall not be considered to be intended for or directed toward commercial advantage or private monetary compensation, provided there is no payment of any monetary compensation in connection with the exchange of copyrighted works. 32 | If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. 33 | For the avoidance of doubt: 34 | 35 | Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; 36 | Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License if Your exercise of such rights is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(b) and otherwise waives the right to collect royalties through any statutory or compulsory licensing scheme; and, 37 | Voluntary License Schemes. The Licensor reserves the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License that is for a purpose or use which is otherwise than noncommercial as permitted under Section 4(c). 38 | Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 39 | 5. Representations, Warranties and Disclaimer 40 | 41 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 42 | 43 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 44 | 45 | 7. Termination 46 | 47 | This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. 48 | Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 49 | 8. Miscellaneous 50 | 51 | Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. 52 | Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. 53 | If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 54 | No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. 55 | This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. 56 | The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. 57 | Creative Commons Notice 58 | 59 | Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of Licensor. 60 | 61 | Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of the License. 62 | 63 | Creative Commons may be contacted at http://creativecommons.org/. 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spark 編程指南繁體中文版 2 | ============================= 3 | 如果你是個讀者,這邊有更容易閱讀的[Gitbook版本](http://taiwansparkusergroup.gitbooks.io/spark-programming-guide-zh-tw/) 4 | 5 | ## 貢獻方式 6 | 請有意願加入的同好參考(https://github.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/blob/master/CONTRIBUTING.rst) 7 | 8 | ## 大綱 9 | 10 | * [簡介](README.md) 11 | * [快速上手](quick-start/README.md) 12 | * [Spark Shell](quick-start/using-spark-shell.md) 13 | * [獨立應用程序](quick-start/self-contained-applications.md) 14 | * [開始翻滾吧!](quick-start/where-to-go-from-here.md) 15 | * [編程指南](programming-guide/README.md) 16 | * [引入 Spark](programming-guide/linking-with-spark.md) 17 | * [初始化 Spark](programming-guide/initializing-spark.md) 18 | * [Spark RDDs](programming-guide/rdds/README.md) 19 | * [並行集合](programming-guide/rdds/parallelized-collections.md) 20 | * [外部數據集](programming-guide/rdds/external-datasets.md) 21 | * [RDD 操作](programming-guide/rdds/rdd-operations.md) 22 | * [傳遞函數到 Spark](programming-guide/rdds/passing-functions-to-spark.md) 23 | * [使用鍵值對](programming-guide/rdds/working-with-key-value-pairs.md) 24 | * [轉換](programming-guide/rdds/transformations.md) 25 | * [行動](programming-guide/rdds/actions.md) 26 | * [RDD持續化](programming-guide/rdds/rdd-persistences.md) 27 | * [共享變數](programming-guide/shared-variables.md) 28 | * [從這裡開始](programming-guide/from-here.md) 29 | * [Spark Streaming](spark-streaming/README.md) 30 | * [一個快速的例子](spark-streaming/a-quick-example.md) 31 | * [基本概念](spark-streaming/basic-concepts/README.md) 32 | * [連接](spark-streaming/basic-concepts/linking.md) 33 | * [初始化StreamingContext](spark-streaming/basic-concepts/initializing-StreamingContext.md) 34 | * [離散化串流](spark-streaming/basic-concepts/discretized-streams.md) 35 | * [输入DStreams](spark-streaming/basic-concepts/input-DStreams.md) 36 | * [DStream中的轉換](spark-streaming/basic-concepts/transformations-on-DStreams.md) 37 | * [DStream的輸出操作](spark-streaming/basic-concepts/output-operations-on-DStreams.md) 38 | * [暫存或持續化](spark-streaming/basic-concepts/caching-persistence.md) 39 | * [Checkpointing](spark-streaming/basic-concepts/checkpointing.md) 40 | * [部署應用程序](spark-streaming/basic-concepts/deploying-applications.md) 41 | * [監控應用程序](spark-streaming/basic-concepts/monitoring-applications.md) 42 | * [性能優化](spark-streaming/performance-tuning/README.md) 43 | * [減少處理時間](spark-streaming/performance-tuning/reducing-processing-time.md) 44 | * [設置正確的的批次大小](spark-streaming/performance-tuning/setting-right-batch-size.md) 45 | * [記憶體優化](spark-streaming/performance-tuning/memory-tuning.md) 46 | * [容錯語意](spark-streaming/fault-tolerance-semantics/README.md) 47 | * [Spark SQL](spark-sql/README.md) 48 | * [開始](spark-sql/getting-started.md) 49 | * [資料來源](spark-sql/data-sources/README.md) 50 | * [RDDs](spark-sql/data-sources/rdds.md) 51 | * [parquet文件](spark-sql/data-sources/parquet-files.md) 52 | * [JSON數據集](spark-sql/data-sources/jSON-datasets.md) 53 | * [Hive表](spark-sql/data-sources/hive-tables.md) 54 | * [性能優化](spark-sql/performance-tuning.md) 55 | * [其它SQL接口](spark-sql/other-sql-interfaces.md) 56 | * [編寫語言集成(Language-Integrated)的相關查詢](spark-sql/writing-language-integrated-relational-queries.md) 57 | * [Spark SQL術劇類型](spark-sql/spark-sql-dataType-reference.md) 58 | * [MLlib](mllib/README.md) 59 | * [數據類型](mllib/data_type.md) 60 | * [本地向量](mllib/local_vector.md) 61 | 62 | * [GraphX編程指南](graphx-programming-guide/README.md) 63 | * [開始](graphx-programming-guide/getting-started.md) 64 | * [屬性圖](graphx-programming-guide/property-graph.md) 65 | * [圖操作](graphx-programming-guide/graph-operators.md) 66 | * [Pregel API](graphx-programming-guide/pregel-api.md) 67 | * [圖建立者](graphx-programming-guide/graph-builders.md) 68 | * [頂點和邊RDDs](graphx-programming-guide/vertex-and-edge-rdds.md) 69 | * [圖算法](graphx-programming-guide/graph-algorithms.md) 70 | * [例子](graphx-programming-guide/examples.md) 71 | * [部署](deploying/submitting-applications.md) 72 | * [提交應用程序](deploying/submitting-applications.md) 73 | * [獨立運行Spark](deploying/spark-standalone-mode.md) 74 | * [在yarn上運行Spark](deploying/running-spark-on-yarn.md) 75 | * [更多文檔](more/spark-configuration.md) 76 | * [Spark配置](more/spark-configuration.md) 77 | * [RDD持續化](programming-guide/rdds/rdd_persistence.md) 78 | 79 | ## Copyright 80 | 81 | 本文翻譯自 82 | 83 | [Spark 官方手冊](https://spark.apache.org/docs/latest/) 84 | 85 | * Reference: 86 | 87 | [Spark 编程指南简体中文版](https://github.com/aiyanbo/spark-programming-guide-zh-cn) 88 | 89 | ## License 90 | 91 | 本文使用的許可請查看[這裡](LICENSE) 92 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | * [簡介](README.md) 4 | * [快速上手](quick-start/README.md) 5 | * [Spark Shell](quick-start/using-spark-shell.md) 6 | * [獨立應用程序](quick-start/standalone-applications.md) 7 | * [開始翻滾吧!](quick-start/where-to-go-from-here.md) 8 | * [编程指南](programming-guide/README.md) 9 | * [引入 Spark](programming-guide/linking-with-spark.md) 10 | * [初始化 Spark](programming-guide/initializing-spark.md) 11 | * [Spark RDDs](programming-guide/rdds/README.md) 12 | * [并行集合](programming-guide/rdds/parallelized-collections.md) 13 | * [外部數據集](programming-guide/rdds/external-datasets.md) 14 | * [RDD 操作](programming-guide/rdds/rdd-operations.md) 15 | * [傳遞函數到 Spark](programming-guide/rdds/passing-functions-to-spark.md) 16 | * [使用键值對](programming-guide/rdds/working-with-key-value-pairs.md) 17 | * [轉換](programming-guide/rdds/transformations.md) 18 | * [行動](programming-guide/rdds/actions.md) 19 | * [RDD持久化](programming-guide/rdds/rdd-persistences.md) 20 | * [共享變量](programming-guide/shared-variables.md) 21 | * [從這裏開始](programming-guide/from-here.md) 22 | * [Spark Streaming](spark-streaming/README.md) 23 | * [一個快速的例子](spark-streaming/a-quick-example.md) 24 | * [基本概念](spark-streaming/basic-concepts/README.md) 25 | * [關聯](spark-streaming/basic-concepts/linking.md) 26 | * [初始化StreamingContext](spark-streaming/basic-concepts/initializing-StreamingContext.md) 27 | * [離散流](spark-streaming/basic-concepts/discretized-streams.md) 28 | * [輸入DStreams](spark-streaming/basic-concepts/input-DStreams.md) 29 | * [DStream中的轉換](spark-streaming/basic-concepts/transformations-on-DStreams.md) 30 | * [DStream的輸出操作](spark-streaming/basic-concepts/output-operations-on-DStreams.md) 31 | * [缓存或持久化](spark-streaming/basic-concepts/caching-persistence.md) 32 | * [Checkpointing](spark-streaming/basic-concepts/checkpointing.md) 33 | * [部署應用程序](spark-streaming/basic-concepts/deploying-applications.md) 34 | * [監控應用程序](spark-streaming/basic-concepts/monitoring-applications.md) 35 | * [性能優化](spark-streaming/performance-tuning/README.md) 36 | * [減少處理時間](spark-streaming/performance-tuning/reducing-processing-time.md) 37 | * [設置正確的批次大小](spark-streaming/performance-tuning/setting-right-batch-size.md) 38 | * [記憶體優化](spark-streaming/performance-tuning/memory-tuning.md) 39 | * [容錯語意](spark-streaming/fault-tolerance-semantics/README.md) 40 | * [Spark SQL](spark-sql/README.md) 41 | * [開始](spark-sql/getting-started.md) 42 | * [資料來源](spark-sql/data-sources/README.md) 43 | * [RDDs](spark-sql/data-sources/rdds.md) 44 | * [parquet文件](spark-sql/data-sources/parquet-files.md) 45 | * [JSON數據集](spark-sql/data-sources/jSON-datasets.md) 46 | * [Hive表](spark-sql/data-sources/hive-tables.md) 47 | * [性能優化](spark-sql/performance-tuning.md) 48 | * [其它SQL接口](spark-sql/other-sql-interfaces.md) 49 | * [編寫語言集成(Language-Integrated)的相關查詢](spark-sql/writing-language-integrated-relational-queries.md) 50 | * [Spark SQL數據類型](spark-sql/spark-sql-dataType-reference.md) 51 | * [MLlib](mllib/README.md) 52 | * [數據類型(Data Type)](mllib/data_type.md) 53 | * [本地向量(Local vector)](mllib/local_vector.md) 54 | * [標記點(Labeled point)](mllib/labeled_point.md) 55 | * [本地矩陣(Local matrix)](mllib/local_matrix.md) 56 | * [分布矩陣(Distributed matrix)](mllib/distributed_matrix.md) 57 | * [RowMatrix](mllib/rowmatrix.md) 58 | * [IndexedRowMatrix](mllib/indexedrowmatrix.md) 59 | * [CoordinateMatrix](mllib/coordinatematrix.md) 60 | * [基本統計分析(Basic Statistics)](mllib/basic_statistics.md) 61 | * [概述統計量(Summary Statistics)](mllib/summary_statistics.md) 62 | * [相關性(Correlations)](mllib/correlations.md) 63 | * [分層抽樣(Stratified sampling)](mllib/stratified_sampling.md) 64 | * [假設檢定(Hypothesis testing)](mllib/hypothesis_testing.md) 65 | * [隨機數據生成(Random data generation)](mllib/random_data_generation.md) 66 | * [分類與迴歸(Classification and Regression)](mllib/classification_and_regression.md) 67 | * [線性方法(Linear Methods)](mllib/linear_methods.md) 68 | * [數學公式(Mathematical formulation)](mllib/mathematical_formulation.md) 69 | * [損失函數(Loss Function)](mllib/loss_function.md) 70 | * [正則化(Regularizers)](mllib/regularizers.md) 71 | * [ 最佳化 (Optimization)](mllib/optimization.md) 72 | * [二元分類(Binary classification)](mllib/binary_classification.md) 73 | * [線性支持向量機(SVMs)](mllib/svms.md) 74 | * [邏輯斯迴歸(Logistic regression)](mllib/logistic_regression.md) 75 | * [評估指標(Evaluation metrics)](mllib/evaluation_metrics.md) 76 | * [示例(Examples)](mllib/examples.md) 77 | * [GraphX编程指南](graphx-programming-guide/README.md) 78 | * [開始](graphx-programming-guide/getting-started.md) 79 | * [屬性圖](graphx-programming-guide/property-graph.md) 80 | * [圖操作](graphx-programming-guide/graph-operators.md) 81 | * [Pregel API](graphx-programming-guide/pregel-api.md) 82 | * [圖建立者](graphx-programming-guide/graph-builders.md) 83 | * [頂點和邊RDDs](graphx-programming-guide/vertex-and-edge-rdds.md) 84 | * [圖算法](graphx-programming-guide/graph-algorithms.md) 85 | * [例子](graphx-programming-guide/examples.md) 86 | * [部署](deploying/submitting-applications.md) 87 | * [提交應用程序](deploying/submitting-applications.md) 88 | * [獨立運行Spark](deploying/spark-standalone-mode.md) 89 | * [在yarn上運行Spark](deploying/running-spark-on-yarn.md) 90 | * [更多文件](more/spark-configuration.md) 91 | * [Spark配置](more/spark-configuration.md) 92 | * [RDD 持久化](programming-guide/rdds/rdd_persistence.md) 93 | 94 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["mathjax"] 3 | } -------------------------------------------------------------------------------- /deploying/running-spark-on-yarn.md: -------------------------------------------------------------------------------- 1 | # 在YARN上執行Spark 2 | 3 | ## 配置 4 | 5 | 大部分是`Spark on YARN`模式提供的配置與其它部署模式提供的配置相同。下面這些是`Spark on YARN`模式提供的配置選擇。 6 | 7 | ### Spark 屬性 8 | 9 | Property Name | Default | Meaning 10 | --- | --- | --- 11 | spark.yarn.applicationMaster.waitTries | 10 | ApplicationMaster等待Spark master的次數以及SparkContext初始化嘗試的次數 12 | spark.yarn.submit.file.replication | HDFS 預設的複製次數(3) | 上傳到HDFS的文件的HDFS複製水準。這些文件包括Spark jar、app jar以及任何分布式記憶體文件/檔案 13 | spark.yarn.preserve.staging.files | false | 預設為true,在作業結束時保留階段性文件(Spark jar、app jar以及任何分布式記憶體文件)而非刪除他們 14 | spark.yarn.scheduler.heartbeat.interval-ms | 5000 | Spark application master給YARN ResourceManager傳送心跳的時間間隔(ms) 15 | spark.yarn.max.executor.failures | numExecutors * 2,最小為3 | 失敗應用程式之前最大的執行失敗次數 16 | spark.yarn.historyServer.address | (none) | Spark歷史服務器(如host.com:18080)的位置。這位置不應該包含一個模式(http://)。預設情況下下沒有設定值,是因為該選項是一個可選選項。當Spark應用程式完成以ResourceManager UI到Spark歷史服務器UI的連結時,這個位址可從YARN ResourceManager得到 17 | spark.yarn.dist.archives | (none) | 抓取逗號分隔的檔案列表到每一個執行器的工作目錄 18 | spark.yarn.dist.files | (none) | 放置逗號分隔的文件列表到每個執行器的工作目錄 19 | spark.yarn.executor.memoryOverhead | executorMemory * 0.07,最小384 | 配給每個執行器的記憶體大小(以MB為單位)。它屬VM 消耗、interned字符串或者其他本地消耗用的記憶體。這往往隨著執行器大小而增加。(典型情況下是6%-10%) 20 | spark.yarn.driver.memoryOverhead | driverMemory * 0.07,最小384 | 分配给每個driver的記憶體大小(以MB為單位)。它屬VM 消耗、interned字符串或者其他本地消耗用的記憶體。這會隨著執行器大小而增加。(典型情況下是6%-10%) 21 | spark.yarn.queue | default | 應用程式傳送到的YARN 隊列的名稱 22 | spark.yarn.jar | (none) | Spark jar文件的位置,會覆蓋預設的位置。預設情況下,Spark on YARN將會用到本地安裝的Spark jar。但是Spark jar也可以是HDFS中的一個公共位置。這讓YARN記憶體它到節點上,而不用在每次運作應用程式時都需要分配。指向HDFS中的jar包,可以這個參數為"hdfs:///some/path" 23 | spark.yarn.access.namenodes | (none) | 你的Spark應用程式訪問的HDFS namenode列表。例如,`spark.yarn.access.namenodes=hdfs://nn1.com:8032,hdfs://nn2.com:8032`,Spark應用程式必須訪問namenode列表,Kerberos必須正確被配置以訪問他們。Spark獲得namenode的安全通過,這樣Spark應用程式就能訪問這些遠端的HDFS集群。 24 | spark.yarn.containerLauncherMaxThreads | 25 | 為了啟動執行者容器,應用程式master用到的最大線程數量 25 | spark.yarn.appMasterEnv.[EnvironmentVariableName] | (none) | 增加通過`EnvironmentVariableName`指定的環境變量到Application Master處理YARN上的啟動。用户可以指定多個設定,從而設定多個環境變數。在yarn-cluster模式下,這控制著Spark driver的環境。在yarn-client模式下,這僅僅控制執行器啟動者的環境。 26 | 27 | ## 在YARN上啟動Spark 28 | 29 | 確保`HADOOP_CONF_DIR`或`YARN_CONF_DIR`指向的目錄包含Hadoop集群的(客戶端)配置文件。這些配置用於寫資料到dfs與YARN ResourceManager。 30 | 31 | 有兩種佈署模式可以用在YARN上啟動Spark應用程式。在yarn-cluster模式下,Spark driver執行在application master中,這個程序被集群中的YARN所管理,客戶端會在初始化應用程式 32 | 之后关闭。在yarn-client模式下,driver运行在客户端进程中,application master仅仅用来向YARN请求资源。 33 | 34 | 和Spark獨立模式以及Mesos模式不同,這些模式中,master的位置由"master"參數指定,而在YARN模式下,ResourceManager的位置從Hadoop配置取得。因此master參數是簡單的`yarn-client`和`yarn-cluster`。 35 | 36 | 在yarn-cluster模式下啟動Spark應用程式。 37 | 38 | ```shell 39 | ./bin/spark-submit --class path.to.your.Class --master yarn-cluster [options] [app options] 40 | ``` 41 | 42 | 例如: 43 | ```shell 44 | $ ./bin/spark-submit --class org.apache.spark.examples.SparkPi \ 45 | --master yarn-cluster \ 46 | --num-executors 3 \ 47 | --driver-memory 4g \ 48 | --executor-memory 2g \ 49 | --executor-cores 1 \ 50 | --queue thequeue \ 51 | lib/spark-examples*.jar \ 52 | 10 53 | ``` 54 | 55 | 以上啟動了一個YARN客戶端程式用來啟動預設的 Application Master,然後SparkPi會作為Application Master的子線程運作。客戶端會定期訪問Application Master作狀態更新並且江更新顯示在控制台上。一旦應用程式執行完畢,客戶端就會退出。 56 | 57 | 在yarn-client模式下啟動Spark應用程式,執行下面的shell脚本 58 | 59 | ```shell 60 | $ ./bin/spark-shell --master yarn-client 61 | ``` 62 | 63 | ### 增加其他的jar 64 | 65 | 在yarn-cluster模式下,driver執行在不同機器上,所以離開了保存在本地客戶端的文件,`SparkContext.addJar`將不會作事。為了讓`SparkContext.addJar`用到保存在客戶端的文件, 66 | 可以在啟動命令列上加`--jars`選項。 67 | ```shell 68 | $ ./bin/spark-submit --class my.main.Class \ 69 | --master yarn-cluster \ 70 | --jars my-other-jar.jar,my-other-other-jar.jar 71 | my-main-jar.jar 72 | app_arg1 app_arg2 73 | ``` 74 | 75 | ## 注意事項 76 | 77 | - 在Hadoop 2.2之前,YARN不支持容器核的資源請求。因此,當執行早期版本時,通過命令行參數指定CPU的數量無法傳遞到YARN。在調度決策中,CPU需求是否成功兌現取決於用哪個調度器以及如何配置他 78 | - Spark executors使用的本地目錄將會YARN配置(yarn.nodemanager.local-dirs)的本地目錄。如果用戶指定了`spark.local.dir`,他將被忽略。 79 | - `--files`和`--archives`選項支援指定帶有 * # * 符號的文件名稱。例如,你能夠指定`--files localtest.txt#appSees.txt`,它上傳你在本地命名為`localtest.txt`的文件到HDFS,但是將會連結道明稱為`appSees.txt`。當你的應用程式執行在YARN上時,你應該用`appSees.txt`去引用該文件。 80 | - 如果你在yarn-cluster模式下執行`SparkContext.addJar`,並且用到了本地文件, `--jars`選項允許`SparkContext.addJar`函數能夠工作。如果你正在使用 HDFS, HTTP, HTTPS或FTP,你不需要用到該選項 81 | 82 | -------------------------------------------------------------------------------- /deploying/spark-standalone-mode.md: -------------------------------------------------------------------------------- 1 | # Spark獨立佈署模式 2 | 3 | ## 安裝Spark獨立模式集群 4 | 5 | 安装Spark獨立模式,你只需要將Spark的編譯版本简单的放到集群的每個節點。你可以獲得每個穩定版本的預編譯版本,也可以自己編譯。 6 | 7 | ## 手动启动集群 8 | 9 | 你能夠通過下面的方式啟動獨立的master服務器。 10 | 11 | ```shell 12 | ./sbin/start-master.sh 13 | ``` 14 | 15 | 一旦啟動,master將會為自己打印出`spark://HOST:PORT` URL,你能夠用它連接到workers或者作為"master"参數傳遞给`SparkContext`。你也可以在master web UI上發現这个URL, 16 | master web UI默認的地址是`http://localhost:8080`。 17 | 18 | 相同的,你也可以啟動一个或者多個workers或者将它们連接到master。 19 | 20 | ```shell 21 | ./bin/spark-class org.apache.spark.deploy.worker.Worker spark://IP:PORT 22 | ``` 23 | 24 | 一旦你啟動了一個worker,查看master web UI。你可以看到新的節點列表以及節點的CPU數以及内存。 25 | 26 | 下面的配置参數可以傳遞给master和worker。 27 | 28 | Argument | Meaning 29 | --- | --- 30 | -h HOST, --host HOST | 監聽的主機名 31 | -i HOST, --ip HOST | 同上,已經被淘汰 32 | -p PORT, --port PORT | 監聽的服務的端口(master默認是7077,worker随機) 33 | --webui-port PORT | web UI的端口(master默認是8080,worker默認是8081) 34 | -c CORES, --cores CORES | Spark應用程序可以使用的CPU核數(默認是所有可用);這個選項僅在worker上可用 35 | -m MEM, --memory MEM | Spark應用程序可以使用的内存數(默認情况是你的機器内存数减去1g);這個選項僅在worker上可用 36 | -d DIR, --work-dir DIR | 用於暫存空間和工作輸出日誌的目錄(默認是SPARK_HOME/work);這個選項僅在worker上可用 37 | --properties-file FILE | 自定義的Spark配置文件的加载目录(默認是conf/spark-defaults.conf) 38 | 39 | ## 集群啟動脚本 40 | 41 | 為了用啟動脚本啟動Spark獨立集群,你應該在你的Spark目錄下建立一个名為`conf/slaves`的文件,這个文件必須包含所有你要啟動的Spark worker所在機器的主機名,一行一個。如果 42 | `conf/slaves`不存在,啟動脚本默認为單個機器(localhost),這台機器對於測試是有用的。注意,master機器通過ssh訪問所有的worker。在默認情况下,SSH是並行運行,需要設置無密碼(採用私有密鑰)的訪問。 43 | 如果你沒有設置为無密碼訪問,你可以設置環境變量`SPARK_SSH_FOREGROUND`,為每個worker提供密碼。 44 | 45 | 一旦你設置了這个文件,你就可以通過下面的shell脚本啟動或者停止你的集群。 46 | 47 | - sbin/start-master.sh:在機器上啟動一個master实例 48 | - sbin/start-slaves.sh:在每台機器上啟動一個slave实例 49 | - sbin/start-all.sh:同時啟動一個master实例和所有slave实例 50 | - sbin/stop-master.sh:停止master实例 51 | - sbin/stop-slaves.sh:停止所有slave实例 52 | - sbin/stop-all.sh:停止master实例和所有slave实例 53 | 54 | 注意,這些脚本必须在你的Spark master运行的机器上执行,而不是在你的本地机器上面。 55 | 56 | 你可以在`conf/spark-env.sh`中設置環境變量進一步配置集群。利用`conf/spark-env.sh.template`創建這个文件,然後將它複製到所有的worker機器上使設置有效。下面的設置可以起作用: 57 | 58 | Environment Variable | Meaning 59 | --- | --- 60 | SPARK_MASTER_IP | 绑定master到一個指定的ip地址 61 | SPARK_MASTER_PORT | 在不同的端口上啟動master(默認是7077) 62 | SPARK_MASTER_WEBUI_PORT | master web UI的端口(默認是8080) 63 | SPARK_MASTER_OPTS | 應用到master的配置属性,格式是 "-Dx=y"(默认是none),查看下面的表格的選項以组成一個可能的列表 64 | SPARK_LOCAL_DIRS | Spark中暂存空間的目錄。包括map的輸出文件和存储在磁盘上的RDDs(including map output files and RDDs that get stored on disk)。这必须在一个快速的、你的系统的本地磁盘上。它可以是一个逗号分隔的列表,代表不同磁盘的多个目录 65 | SPARK_WORKER_CORES | Spark应用程序可以用到的核心数(默認是所有可用) 66 | SPARK_WORKER_MEMORY | Spark应用程序用到的内存总数(默認是内存總數减去1G)。注意,每个應用程序个体的内存通過`spark.executor.memory`設置 67 | SPARK_WORKER_PORT | 在指定的端口上啟動Spark worker(默認是随機) 68 | SPARK_WORKER_WEBUI_PORT | worker UI的端口(默认是8081) 69 | SPARK_WORKER_INSTANCES | 每台机器運行的worker实例数,默認是1。如果你有一台非常大的機器并且希望運行多个worker,你可以設置这个數大於1。如果你設置了這個環境變量,確保你也設置了`SPARK_WORKER_CORES`環境變量用於限制每個worker的核數或者每个worker嘗試使用所有的核。 70 | SPARK_WORKER_DIR | Spark worker運行目錄,該目錄包括日誌和暫存空間(默默是SPARK_HOME/work) 71 | SPARK_WORKER_OPTS | 應用到worker的配置屬性,格式是 "-Dx=y"(默認是none),查看下面表格的選項以组成一個可能的列表 72 | SPARK_DAEMON_MEMORY | 分配给Spark master和worker守护进程的内存(默认是512m) 73 | SPARK_DAEMON_JAVA_OPTS | Spark master和worker守护进程的JVM選項,格式是"-Dx=y"(默認为none) 74 | SPARK_PUBLIC_DNS | Spark master和worker公共的DNS名(默認是none) 75 | 76 | 注意,啟動脚本還不支持windows。为了在windows上啟動Spark集群,需要手動啟動master和workers。 77 | 78 | `SPARK_MASTER_OPTS`支持以下的系统屬性: 79 | 80 | Property Name | Default | Meaning 81 | --- | --- | --- 82 | spark.deploy.retainedApplications | 200 | 展示完成的應用程序的最大数目。老的應用程序會被删除以滿足該限制 83 | spark.deploy.retainedDrivers | 200 | 展示完成的drivers的最大數目。老的應用程序會被删除以滿足該限制 84 | spark.deploy.spreadOut | true | 这个選項控制獨立的集群管理器是應該跨節點傳遞应用程序還是應努力將程序整合到儘可能少的節點上。在HDFS中,傳遞程序是數據本地化更好的選擇,但是,對於計算密集型的負載,整合會更有效率。 85 | spark.deploy.defaultCores | (infinite) | 在Spark獨立模式下,給應用程序的默認核数(如果没有設置`spark.cores.max`)。如果没有設置,應用程序總數獲得所有可用的核,除非设置了`spark.cores.max`。在共享集群上設置較低的核數,可用防止用户默認抓住整個集群。 86 | spark.worker.timeout | 60 | 獨立佈署的master认为worker失敗(没有收到心跳信息)的間隔時間。 87 | 88 | `SPARK_WORKER_OPTS`支持的系统屬性: 89 | 90 | Property Name | Default | Meaning 91 | --- | --- | --- 92 | spark.worker.cleanup.enabled | false | 周期性的清空worker/應用程序目錄。注意,這僅僅影響獨立佈署模式。不管應用程序是否還在執行,用於程序目錄都會被清空 93 | spark.worker.cleanup.interval | 1800 (30分) | 在本地机器上,worker清空老的應用程序工作目錄的時間間隔 94 | spark.worker.cleanup.appDataTtl | 7 * 24 * 3600 (7天) | 每個worker中應用程序工作目錄的保留時間。這個時間依賴於你可用磁盘空间的大小。應用程序日誌和jar包上傳到每個應用程序的工作目錄。随著時間的推移,工作目錄會很快的填滿磁盘空間,特别是如果你運行的作業很頻繁。 95 | 96 | ## 連接一個應用程序到集群中 97 | 98 | 為了在Spark集群中運行一個應用程序,簡單地傳遞`spark://IP:PORT` URL到[SparkContext](http://spark.apache.org/docs/latest/programming-guide.html#initializing-spark) 99 | 100 | 為了在集群上運行一個交互式的Spark shell,運行一下命令: 101 | 102 | ```shell 103 | ./bin/spark-shell --master spark://IP:PORT 104 | ``` 105 | 你也可以傳遞一個選項`--total-executor-cores `去控制spark-shell的核數。 106 | 107 | ## 啟動Spark應用程序 108 | 109 | [spark-submit脚本](submitting-applications.md)支持最直接的提交一个Spark應用程序到集群。對於獨立部署的集群,Spark目前支持兩種佈署模式。在`client`模式中,driver啟動进程與 110 | 客户端提交應用程序所在的进程是同一个进程。然而,在`cluster`模式中,driver在集群的某個worker进程中啟動,只有客户端进程完成了提交任务,它不會等到应用程序完成就會退出。 111 | 112 | 如果你的應用程序通过Spark submit啟動,你的應用程序jar包將會自動分發到所有的worker節點。對於你的應用程序依賴的其它jar包,你應該用`--jars`符號指定(如` --jars jar1,jar2`)。 113 | 114 | 另外,`cluster`模式支持自動的重啟你的應用程序(如果程序一非零的退出碼退出)。為了用這个特徵,當啟動應用程序時,你可以傳遞`--supervise`符号到`spark-submit`。如果你想殺死反覆失敗的應用, 115 | 你可以通過如下的方式: 116 | 117 | ```shell 118 | ./bin/spark-class org.apache.spark.deploy.Client kill 119 | ``` 120 | 121 | 你可以在獨立部署的Master web UI(http://:8080)中找到driver ID。 122 | 123 | ## 資源調度 124 | 125 | 獨立佈署的集群模式仅仅支持简单的FIFO調度器。然而,为了允许多個並行的用户,你能夠控制每個應用程序能用的最大資源數。在默認情况下,它將獲得集群的所有核,這只有在某一時刻只 126 | 允許一个應用程序才有意義。你可以通過`spark.cores.max`在[SparkConf](http://spark.apache.org/docs/latest/configuration.html#spark-properties)中設置核數。 127 | 128 | ```scala 129 | val conf = new SparkConf() 130 | .setMaster(...) 131 | .setAppName(...) 132 | .set("spark.cores.max", "10") 133 | val sc = new SparkContext(conf) 134 | ``` 135 | 另外,你可以在集群的master进程中配置`spark.deploy.defaultCores`来改變默認的值。在`conf/spark-env.sh`添加下面的行: 136 | 137 | ```properties 138 | export SPARK_MASTER_OPTS="-Dspark.deploy.defaultCores=" 139 | ``` 140 | 141 | 這在用户没有配置最大核數的共享集群中是有用的。 142 | 143 | ## 高可用 144 | 145 | 默認情况下,獨立的調度集群對worker失敗是有彈性的(在Spark本身的範圍内是有彈性的,對丢失的工作通過轉移它到另外的worker來解决)。然而,調度器通過master去執行調度决定, 146 | 這會造成單點故障:如果master死了,新的應用程序就無法創建。为了避免這個,我们有兩個高可用的模式。 147 | 148 | ### 用ZooKeeper的備用master 149 | 150 | 利用ZooKeeper去支持领导选举以及一些狀態存储,你能夠在你的集群中啟動多個master,這些master連接到同一個ZooKeeper实例上。一個被選为“领导”,其它的保持備用模式。如果當前 151 | 的领导死了,另一个master將會被選中,恢復老master的狀態,然後恢復調度。整个的恢復过程大概需要1到2分鐘。注意,這個恢復時間僅僅會影響調度新的應用程序-運行在失敗master中的 152 | 應用程序不受影響。 153 | 154 | #### 配置 155 | 156 | 為了開啟這個恢復模式,你可以用下面的屬性在`spark-env`中設置`SPARK_DAEMON_JAVA_OPTS`。 157 | 158 | System property | Meaning 159 | --- | --- 160 | spark.deploy.recoveryMode | 設置ZOOKEEPER去啟動備用master模式(默認为none) 161 | spark.deploy.zookeeper.url | zookeeper集群url(如192.168.1.100:2181,192.168.1.101:2181) 162 | spark.deploy.zookeeper.dir | zookeeper保存恢復狀態的目錄(默認是/spark) 163 | 164 | 可能的陷阱:如果你在集群中有多个masters,但是沒有用zookeeper正确的配置这些masters,這些masters不會發現彼此,會認為它們都是leaders。這將會造成一個不健康的集群狀態(因为所有的master都會獨立的調度)。 165 | 166 | #### 细节 167 | 168 | zookeeper集群啟動之後,開啟高可用是簡單的。在相同的zookeeper配置(zookeeper URL和目錄)下,在不同的節點上簡單地啟動多個master进程。master可以随時添加和删除。 169 | 170 | 為了調度新的應用程序或者添加worker到集群,它需要知道當前leader的IP地址。這可以通過簡單的傳遞一个master列表來完成。例如,你可能啟動你的SparkContext指向`spark://host1:port1,host2:port2`。 171 | 這將造成你的SparkContext同时注册这两个master-如果`host1`死了,這個配置文件將一直是正確的,因为我們將找到新的leader-`host2`。 172 | 173 | "registering with a Master"和正常操作之間有重要的區别。當啟動时,一个應用程序或者worker需要能夠發现和註册當前的leader master。一旦它成功註册,它就在系统中了。如果 174 | 錯誤發生,新的leader將會接觸所有之前註册的應用程序和worker,通知他們领导關係的變化,所以它们甚至不需要事先知道新啟動的leader的存在。 175 | 176 | 由於這個屬性的存在,新的master可以在任何時候創建。你唯一需要擔心的問題是新的應用程序和workers能夠發現它並將它註册進来以防它成為leader master。 177 | 178 | ### 用本地文件系统做單節點恢復 179 | 180 | zookeeper是生產環境下最好的選擇,但是如果你想在master死掉後重啟它,`FILESYSTEM`模式可以解決。當應用程序和worker註册,它们擁有足夠的狀態寫入提供的目錄,以至於在重啟master 181 | 进程時它们能夠恢復。 182 | 183 | #### 配置 184 | 185 | 為了開啟這個恢復模式,你可以用下面的屬性在`spark-env`中設置`SPARK_DAEMON_JAVA_OPTS`。 186 | 187 | System property | Meaning 188 | --- | --- 189 | spark.deploy.recoveryMode | 設置为FILESYSTEM開啟單節點恢復模式(默認为none) 190 | spark.deploy.recoveryDirectory | 用來恢復狀態的目錄 191 | 192 | #### 細節 193 | 194 | - 這个解决方案可以和監控器/管理器(如[monit](http://mmonit.com/monit/))相配合,或者僅僅通過重啟開啟手動恢復。 195 | - 雖然文件系统的恢復似乎比沒有做任何恢復要好,但對於特定的開發或實驗目的,這種模式可能是次優的。特别是,通过`stop-master.sh`殺掉master不會清除它的恢復狀態,所以,不管你何時啟動一個新的master,它都將進入恢復模式。這可能使啟動時間增加到1分鐘。 196 | - 雖然它不是官方支持的方式,你也可以創建一個NFS目錄作为恢復目錄。如果原始的master節點完全死掉,你可以在不同的節點啟動master,它可以正確的恢復之前註册的所有應用程序和workers。未來的應用程序會發現這個新的master。 -------------------------------------------------------------------------------- /deploying/submitting-applications.md: -------------------------------------------------------------------------------- 1 | # 提交應用程式 2 | 3 | 在Spark bin 目錄下的`spark-submit` 讓你在集群上啟動應用程式。它可以通過統一接口使用Spark 支援的所有[ 集群管理器](https://spark.apache.org/docs/latest/cluster-overview.html#cluster-manager-types),所有你不必為每個管理器作相對應的設定。 4 | 5 | ## 用spark-submit 啟動應用程式 6 | 7 | `bin/spark-submit` 指令負責建立包含Spark 以及他所相依的類別路徑(classpath),他支援不同的集群管理器以及Spark 支持的加載模式。 8 | 9 | ```shell 10 | ./bin/spark-submit \ 11 | --class 12 | --master \ 13 | --deploy-mode \ 14 | --conf = \ 15 | ... # other options 16 | \ 17 | [application-arguments] 18 | ``` 19 | 20 | 常用選項如下: 21 | 22 | - `--class`:你的應用程式入口點(如org.apache.spark.examples.SparkPi) 23 | - `--master`:集群的master URL(如spark://23.195.26.187:7077) 24 | - `--deploy-mode`:在worker 節點部署你的driver(cluster) 或者本地作為外部客戶端(client)。預設是client。 25 | - `--conf`:自定的Spark 配置屬性,格式是key=value。 26 | - `application-jar`:包含應用程式以及其相依的jar 包路徑。這個URL 必須在集群中全域可用,例如,存放在所有節點的`hdfs://` 路徑或是`file://` 路徑 27 | - `application-arguments`:傳遞給主類別的參數 28 | 29 | 常見的部署策略是從網路提交你的應用程式。這種設定之下,適合`client` 模式。在`client` 模式,driver直接在`spark-submit` 中啟動。這樣的方式直接作為集群客戶端。由於應用程式的輸入與輸出都與控制台相連,所以也適合與 REPL 的應用程式。 30 | 31 | 另一種選擇,如果你的應用程式從一個與worker 機器距離很遠的機器上提交,一般情況下,用`cluster` 模式可減少drivers和executors 的網路延遲。注意,`cluster` 模式目前不支援獨立集群、mesos集群以及python應用程式。 32 | 33 | 有幾個我們使用的集群管理特有的選項。例如,在Spark讀立即群的`cluster`模式下,你也可以指定`--supervise` 以確保driver 自動重新啟動(如果它因為發生錯誤而退出失敗)。 34 | 為了列出spark-submit 所有可用參數,用`--help` 執行。 35 | 36 | ```shell 37 | # Run application locally on 8 cores 38 | ./bin/spark-submit \ 39 | --class org.apache.spark.examples.SparkPi \ 40 | --master local[8] \ 41 | /path/to/examples.jar \ 42 | 100 43 | 44 | # Run on a Spark Standalone cluster in client deploy mode 45 | ./bin/spark-submit \ 46 | --class org.apache.spark.examples.SparkPi \ 47 | --master spark://207.184.161.138:7077 \ 48 | --executor-memory 20G \ 49 | --total-executor-cores 100 \ 50 | /path/to/examples.jar \ 51 | 1000 52 | 53 | # Run on a Spark Standalone cluster in cluster deploy mode with supervise 54 | ./bin/spark-submit \ 55 | --class org.apache.spark.examples.SparkPi \ 56 | --master spark://207.184.161.138:7077 \ 57 | --deploy-mode cluster 58 | --supervise 59 | --executor-memory 20G \ 60 | --total-executor-cores 100 \ 61 | /path/to/examples.jar \ 62 | 1000 63 | 64 | # Run on a YARN cluster 65 | export HADOOP_CONF_DIR=XXX 66 | ./bin/spark-submit \ 67 | --class org.apache.spark.examples.SparkPi \ 68 | --master yarn-cluster \ # can also be `yarn-client` for client mode 69 | --executor-memory 20G \ 70 | --num-executors 50 \ 71 | /path/to/examples.jar \ 72 | 1000 73 | 74 | # Run a Python application on a Spark Standalone cluster 75 | ./bin/spark-submit \ 76 | --master spark://207.184.161.138:7077 \ 77 | examples/src/main/python/pi.py \ 78 | 1000 79 | ``` 80 | 81 | ## Master URLs 82 | 83 | 傳送給Spark 的url 可用下列模式 84 | 85 | Master URL | Meaning 86 | --- | --- 87 | local | 用一個worker 本地執行Spark 88 | local[K] | 用k 個worker 本地執行Spark (理想情況下,數值為機器CPU 的數量) 89 | local[*] | 有多少worker 就用多少,以本地執行Spark 90 | spark://HOST:PORT | 連結到指定Spark 獨立集群master。端口必須是master 配置的端口,預設是7077 91 | mesos://HOST:PORT | 連結到指定的mesos 集群 92 | yarn-client | 以`client` 模式連結到Yarn 集群。集群位置設定在變數HADOOP_CONF_DIR 93 | yarn-cluster | 以`cluster`模式連結到Yarn 集群。集群位置設定在變數HADOOP_CONF_DIR 94 | -------------------------------------------------------------------------------- /graphx-programming-guide/README.md: -------------------------------------------------------------------------------- 1 | # Spark GraphX 2 | 3 | ## 概觀 4 | GraphX是一個新的(alpha) Spark API,它用於圖形(Graph)和平行圖形(Graph-parallel)的計算。GraphX透過引入[Resilient Distributed Property Graph](property-graph.md):一種帶有頂點和邊屬性的有向多重圖,來擴展Spark RDD。為了支援圖形的運算,GraphX公開一系列基本運算子(例如:subGraph、joinVertices、aggregateMessages)和Pregel API的優化。此外,GraphX也持續增加圖形演算法還有簡化分析圖形的工具(Builder)。 5 | 6 | ## 動機 7 | 從社群媒體到語言模型,數量和重要性不斷成長的圖形結構資料推動了許多`graph-parallel`系統(例如:[Giraph](http://giraph.apache.org/)和 [GraphLab](https://dato.com/products/create/open_source.html))的發展。 8 | 藉由限制可表示的運算型別和帶入新的技術來劃分和分配圖形,這些系統能夠有效率地執行複雜的圖形演算法,比一般的`data-parallel`的系統快很多。 9 | 10 | ![data parallel vs graph parallel](../img/data_parallel_vs_graph_parallel.png) 11 | 12 | 然而,透過這種限制可以大量的提高效能,但是很難表現典型圖形分析流程:建構圖形、修改結構或是表達橫跨多個圖形的運算中很多的重要階段。另外,如何看待資料取決於我們的目標,且相同的原始資料可能有許多不同的表格和圖形。 13 | 14 | ![Table and graph](../img/tables_and_graphs.png) 15 | 16 | 總結來講,圖形和表格之間經常需要夠夠互相轉換。然而,現存的圖形分析流程必須撰寫`graph-parallel`和`data- parallel`系統,導致大量資料的搬移和重複還有複雜的程式模型。 17 | 18 | ![Graph analytics pipeline](../img/graph_analytics_pipeline.png) 19 | 20 | GraphX的目的就是將`graph-parallel`和`data-parallel`整合成一個系統中,而且只有一個整合後的API。GraphX允許使用者將資料視為一個圖形和集合(例如: RDDs),而不需要任何的資料搬移和複製。最新的 `graph-parallel` 系統,使得GraphX能夠優化圖形指令的執行。 21 | 22 | * [入門](getting-started.md) 23 | * [圖形特性](property-graph.md) 24 | * [圖形運算子](graph-operators.md) 25 | * [Pregel API](pregel-api.md) 26 | * [圖形建構式](graph-builders.md) 27 | * [頂點和邊的RDDs](vertex-and-edge-rdds.md) 28 | * [圖形演算法](graph-algorithms.md) 29 | * [範例](examples.md) 30 | -------------------------------------------------------------------------------- /graphx-programming-guide/examples.md: -------------------------------------------------------------------------------- 1 | # 範例 2 | 3 | 假設我們想要利用一些檔案來建構一個圖形,利用重要的關係和使用者來限制圖形,並且在其上面執行page-rank,最後回傳與Top使用者相關的屬性。我們可以透過如下方式實現。 4 | 5 | ```scala 6 | // Connect to the Spark cluster 7 | val sc = new SparkContext("spark://master.amplab.org", "research") 8 | 9 | // Load my user data and parse into tuples of user id and attribute list 10 | val users = (sc.textFile("graphx/data/users.txt") 11 | .map(line => line.split(",")).map( parts => (parts.head.toLong, parts.tail) )) 12 | 13 | // Parse the edge data which is already in userId -> userId format 14 | val followerGraph = GraphLoader.edgeListFile(sc, "graphx/data/followers.txt") 15 | 16 | // Attach the user attributes 17 | val graph = followerGraph.outerJoinVertices(users) { 18 | case (uid, deg, Some(attrList)) => attrList 19 | // Some users may not have attributes so we set them as empty 20 | case (uid, deg, None) => Array.empty[String] 21 | } 22 | 23 | // Restrict the graph to users with usernames and names 24 | val subgraph = graph.subgraph(vpred = (vid, attr) => attr.size == 2) 25 | 26 | // Compute the PageRank 27 | val pagerankGraph = subgraph.pageRank(0.001) 28 | 29 | // Get the attributes of the top pagerank users 30 | val userInfoWithPageRank = subgraph.outerJoinVertices(pagerankGraph.vertices) { 31 | case (uid, attrList, Some(pr)) => (pr, attrList.toList) 32 | case (uid, attrList, None) => (0.0, attrList.toList) 33 | } 34 | 35 | println(userInfoWithPageRank.vertices.top(5)(Ordering.by(_._2._1)).mkString("\n")) 36 | ``` 37 | -------------------------------------------------------------------------------- /graphx-programming-guide/getting-started.md: -------------------------------------------------------------------------------- 1 | # 入門 2 | 3 | 第一步你需要先引入Spark和GraphX到你的專案中,如下面所示 4 | 5 | ```scala 6 | import org.apache.spark._ 7 | import org.apache.spark.graphx._ 8 | // To make some of the examples work we will also need RDD 9 | import org.apache.spark.rdd.RDD 10 | ``` 11 | 如果你没有用到Spark shell,你將會需要SparkContext。若想學習更多Spark的入門知識,可以參考[Spark Quick Start Guide](../quick-start/README.md)。 12 | -------------------------------------------------------------------------------- /graphx-programming-guide/graph-algorithms.md: -------------------------------------------------------------------------------- 1 | # 圖形演算法 2 | 3 | GraphX具備一系列的圖形演算法來簡化圖形分析的任務。這些演算法都在`org.apache.spark.graphx.lib`,可以直接透過`Graph`中的[GraphOps](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.GraphOps)來取得。這章節將會描述如何使用這些圖形演算法。 4 | 5 | ## PageRank 6 | 7 | PageRank是用來衡量一個圖形中每個頂點的重要程度,假設有一條從u到v的邊,這條邊稱為u給v的重要性指標。例如,一個Twitter使用者有許多追隨者,如此一來,可以認為這名使用者相當重要。 8 | 9 | 在GraphX中的[PageRank object](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.lib.PageRank$)實作了靜態和動態PageRank的方法。靜態的PageRank會在固定的次數內運行,而動態的PageRank則會一直運行,直到收斂。[GraphOps](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.GraphOps)允許直接呼叫這些方法。 10 | 11 | GraphX內有一個範例,可以讓我們直接在社群媒體資料集上運行PageRank演算法。 12 | 使用者的資料在`graphx/data/users.txt`,使用者之間的關係在`graphx/data/followers.txt`中。我們可以透過以下來計算出每個使用者的PageRank。 13 | 14 | ```scala 15 | // Load the edges as a graph 16 | val graph = GraphLoader.edgeListFile(sc, "graphx/data/followers.txt") 17 | // Run PageRank 18 | val ranks = graph.pageRank(0.0001).vertices 19 | // Join the ranks with the usernames 20 | val users = sc.textFile("graphx/data/users.txt").map { line => 21 | val fields = line.split(",") 22 | (fields(0).toLong, fields(1)) 23 | } 24 | val ranksByUsername = users.join(ranks).map { 25 | case (id, (username, rank)) => (username, rank) 26 | } 27 | // Print the result 28 | println(ranksByUsername.collect().mkString("\n")) 29 | ``` 30 | 31 | ## 連通分量演算法 32 | 33 | 連通分量演算法利用連通分量中編號最小的頂點的ID來作為其的標籤。例如,在社群媒體中,連通分量可以近似為一個群聚。在GraphX中的[ConnectedComponents object](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.lib.ConnectedComponents$)有這個演算法實作,我們可以透過下面的範例來完成。 34 | 35 | ```scala 36 | // Load the graph as in the PageRank example 37 | val graph = GraphLoader.edgeListFile(sc, "graphx/data/followers.txt") 38 | // Find the connected components 39 | val cc = graph.connectedComponents().vertices 40 | // Join the connected components with the usernames 41 | val users = sc.textFile("graphx/data/users.txt").map { line => 42 | val fields = line.split(",") 43 | (fields(0).toLong, fields(1)) 44 | } 45 | val ccByUsername = users.join(cc).map { 46 | case (id, (username, cc)) => (username, cc) 47 | } 48 | // Print the result 49 | println(ccByUsername.collect().mkString("\n")) 50 | ``` 51 | 52 | ## 三角形計數演算法 53 | 54 | 若一個頂點有兩個相鄰的頂點且和它們有邊相連,那麼這個頂點就是三角形的一部分。GraphX中的[TriangleCount object](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.lib.TriangleCount$)實作了演算法,它計算通過每個頂點的三角形數量,用來衡量群聚。需要注意的`TriangleCount`要求邊的方向是按照規定的方向(srcId < dstId)並且圖形是利用`Graph.partitionBy`所切開的。 55 | 56 | ```scala 57 | // Load the edges in canonical order and partition the graph for triangle count 58 | val graph = GraphLoader.edgeListFile(sc, "graphx/data/followers.txt", true).partitionBy(PartitionStrategy.RandomVertexCut) 59 | // Find the triangle count for each vertex 60 | val triCounts = graph.triangleCount().vertices 61 | // Join the triangle counts with the usernames 62 | val users = sc.textFile("graphx/data/users.txt").map { line => 63 | val fields = line.split(",") 64 | (fields(0).toLong, fields(1)) 65 | } 66 | val triCountByUsername = users.join(triCounts).map { case (id, (username, tc)) => 67 | (username, tc) 68 | } 69 | // Print the result 70 | println(triCountByUsername.collect().mkString("\n")) 71 | ``` 72 | -------------------------------------------------------------------------------- /graphx-programming-guide/graph-builders.md: -------------------------------------------------------------------------------- 1 | # 圖形建構式 2 | 3 | GraphX提供了幾種方式從RDD或是硬碟上的頂點和邊建立圖。在預設情況下,圖形建構式不會為圖的邊重新分割,而是把邊保留在預設的區塊中(例如HDFS的原始區塊)。 4 | [Graph.groupEdges](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph@groupEdges((ED,ED)⇒ED):Graph[VD,ED])要求重新分割圖形,因為它假定相同的邊會被分配到同一個區塊,所以你必須在使用`groupEdges`前使用[Graph.partitionBy](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph@partitionBy(PartitionStrategy):Graph[VD,ED]) 5 | 6 | ```scala 7 | object GraphLoader { 8 | def edgeListFile( 9 | sc: SparkContext, 10 | path: String, 11 | canonicalOrientation: Boolean = false, 12 | minEdgePartitions: Int = 1) 13 | : Graph[Int, Int] 14 | } 15 | ``` 16 | 17 | [GraphLoader.edgeListFile](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.GraphLoader$@edgeListFile(SparkContext,String,Boolean,Int):Graph[Int,Int])提供了一個從硬碟上邊的清單讀取一個圖形的方式。格式如下面範例(起始頂點ID,目標頂點ID),`#`表示註解行。 18 | 19 | ```scala 20 | # This is a comment 21 | 2 1 22 | 4 1 23 | 1 2 24 | ``` 25 | 26 | 從指定的邊建立一個圖,自動建立所有邊提及的所有頂點。所有的頂點和邊的屬性預設都是1。`canonicalOrientation`參數允許重新導向正向(srcId < dstId)的邊。這在[connected components](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.lib.ConnectedComponents$)演算法中需要用到。`minEdgePartitions`參數用來規定邊的分區生成的最小數量。邊分區可能比指定的分區還要多。例如,一個HDFS檔案有更多的區塊。 27 | 28 | ```scala 29 | object Graph { 30 | def apply[VD, ED]( 31 | vertices: RDD[(VertexId, VD)], 32 | edges: RDD[Edge[ED]], 33 | defaultVertexAttr: VD = null) 34 | : Graph[VD, ED] 35 | def fromEdges[VD, ED]( 36 | edges: RDD[Edge[ED]], 37 | defaultValue: VD): Graph[VD, ED] 38 | def fromEdgeTuples[VD]( 39 | rawEdges: RDD[(VertexId, VertexId)], 40 | defaultValue: VD, 41 | uniqueEdges: Option[PartitionStrategy] = None): Graph[VD, Int] 42 | } 43 | ``` 44 | [Graph.apply](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph$@apply[VD,ED](RDD[(VertexId,VD)],RDD[Edge[ED]],VD)(ClassTag[VD],ClassTag[ED]):Graph[VD,ED])允許從頂點和邊的RDD上建立一個圖。重複的頂點會被任意地挑出,而只有從邊RDD出來的頂點才會有預設屬性,頂點RDD並不會有。 45 | 46 | [Graph.fromEdges](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph$@fromEdges[VD,ED](RDD[Edge[ED]],VD)(ClassTag[VD],ClassTag[ED]):Graph[VD,ED])只允許從一個邊的RDD上建立一個圖,且自動地建立邊提及的頂點,並給予這些頂點預設值。 47 | 48 | [Graph.fromEdgeTuples](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph$@fromEdgeTuples[VD](RDD[(VertexId,VertexId)],VD,Option[PartitionStrategy])(ClassTag[VD]):Graph[VD,Int])只允許一個edge tuple組成的RDD上建立一個圖,並給予邊的值為1。自動地建立邊所提及的頂點,並給予這些頂點預設值。它還支援刪除邊,為了刪除邊,需要傳遞一個[PartitionStrategy](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.PartitionStrategy)值為`Some`作為參數`uniqueEdges`的值(如uniqueEdges = some(PartitionStrategy.RandomVertexCut)),要刪除同一分區相同的邊,一個分割策略是必須的。 49 | -------------------------------------------------------------------------------- /graphx-programming-guide/optimized-representation.md: -------------------------------------------------------------------------------- 1 | #Optimized Representation 2 | 3 | 一些較高層次的理解可能對於演算法設計和是用API有幫助,然而在這個章節並不會介紹Optimization太多的細節。GraphX在分散式Graph的分割是採用的Vertex-cut方法: 4 | ![Partitioning Approach](https://spark.apache.org/docs/latest/img/edge_cut_vs_vertex_cut.png) 5 | 而不是沿著Graph的邊來切,GraphX透過頂點來切能夠減少訊息交換和儲存上的負擔。邏輯上,這就像分配邊給機器且允許頂點橫跨多個機器。分配邊的方法是仰賴於[PartitionStrategy]()和多個不同的heuristics的權衡。使用者可以藉由[Graph.partitionBy]()運算子重心切割Graph,來選擇不同的策略。預設的切割策略是使用最初分割的邊當做Graph建立。然而,使用者能夠方便的轉換成2D-partitioning或式其他heuristics。 6 | ![SomeThing](https://spark.apache.org/docs/latest/img/vertex_routing_edge_tables.png) 7 | 一旦邊已經分割了,如何有效率作graph-parallel運算的主要關鍵是有效率的將頂點屬性和邊join起來。因為在現實的圖中,邊的數量多於頂點,我們將頂點的屬性移至邊上。因為並非所有分區都會包含所有頂點相鄰的邊,當要實作join所需的運算子像triplets和aggregateMessages時,我們內部會有一個routing table來識別這些頂點的位置。 8 | -------------------------------------------------------------------------------- /graphx-programming-guide/pregel-api.md: -------------------------------------------------------------------------------- 1 | # Pregel API 2 | 3 | 圖本身是遞迴型的資料結構,因為頂點的屬性依賴於其鄰居的屬性,這些鄰居的屬性又依賴於其鄰居的屬性。結論是,許多重要的圖形演算法,都需要重複的重新計算每個頂點的屬性,直到滿足某個確定的條件。一系列的Graph-parallel抽象體已經背提出來代表這些迭代型演算法。GraphX公開了一個Pregel-like的運算子,它是廣泛使用Pregel和GraphLab抽象的一個融合。 4 | 5 | 在GraphX中,更高階層的Pregel運算子是一個限制到圖拓僕的批量同步(bulk-synchronous)。Pregel運算子執行一系列的super-steps,再這些步驟中,頂點從之前的super-steps中接收進入訊息的總和,為頂點的屬性計算一個新值,然後在下一個super-step中發送訊息到相鄰的頂點。不像Pregel而更像GraphLab,訊息被作為一個邊三元組的函數平行的運算,且訊息運算會存取來源和目標頂點的特徵。在super-step中,未收到訊息的頂點會被跳過。當沒有任何訊息遺留時,Pregel運算子會停止迭代且回傳最後的圖。 6 | 7 | > 注意,不像標準的Pregel實作,GraphX中的頂點只能夠發送訊息給相鄰頂點,且利用使用者自訂的通知函數來平行完成訊息的建立。這些限制允許了GraphX進行額外的優化。 8 | 9 | 以下是[Pregel操作](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.GraphOps@pregel[A](A,Int,EdgeDirection)((VertexId,VD,A)⇒VD,(EdgeTriplet[VD,ED])⇒Iterator[(VertexId,A)],(A,A)⇒A)(ClassTag[A]):Graph[VD,ED])的型別簽章(signature)以及實做的草圖(注意,graph.cache呼叫已經移除了) 10 | 11 | 12 | ```scala 13 | class GraphOps[VD, ED] { 14 | def pregel[A] 15 | (initialMsg: A, 16 | maxIter: Int = Int.MaxValue, 17 | activeDir: EdgeDirection = EdgeDirection.Out) 18 | (vprog: (VertexId, VD, A) => VD, 19 | sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)], 20 | mergeMsg: (A, A) => A) 21 | : Graph[VD, ED] = { 22 | // Receive the initial message at each vertex 23 | var g = mapVertices( (vid, vdata) => vprog(vid, vdata, initialMsg) ).cache() 24 | // compute the messages 25 | var messages = g.mapReduceTriplets(sendMsg, mergeMsg) 26 | var activeMessages = messages.count() 27 | // Loop until no messages remain or maxIterations is achieved 28 | var i = 0 29 | while (activeMessages > 0 && i < maxIterations) { 30 | // Receive the messages: ----------------------------------------------------------------------- 31 | // Run the vertex program on all vertices that receive messages 32 | val newVerts = g.vertices.innerJoin(messages)(vprog).cache() 33 | // Merge the new vertex values back into the graph 34 | g = g.outerJoinVertices(newVerts) { (vid, old, newOpt) => newOpt.getOrElse(old) }.cache() 35 | // Send Messages: ------------------------------------------------------------------------------ 36 | // Vertices that didn't receive a message above don't appear in newVerts and therefore don't 37 | // get to send messages. More precisely the map phase of mapReduceTriplets is only invoked 38 | // on edges in the activeDir of vertices in newVerts 39 | messages = g.mapReduceTriplets(sendMsg, mergeMsg, Some((newVerts, activeDir))).cache() 40 | activeMessages = messages.count() 41 | i += 1 42 | } 43 | g 44 | } 45 | } 46 | ``` 47 | 48 | 注意,Pregel接受兩個參數列表(graph.pregel(list1)(list2))。第一個參數列表包含了配置參數,如初始訊息、最大的迭代數、訊息發送邊的方向(預設向外)。第二個參數列表包含了用來接收訊息(vprog)、計算訊息(sendMsg)、合併訊息(mergeMsg)。 49 | 50 | 以下範例是我們可以使用Pregel運算子來表示單源最短路徑(Single source shortest path)的運算。 51 | 52 | ```scala 53 | import org.apache.spark.graphx._ 54 | // Import random graph generation library 55 | import org.apache.spark.graphx.util.GraphGenerators 56 | // A graph with edge attributes containing distances 57 | val graph: Graph[Int, Double] = 58 | GraphGenerators.logNormalGraph(sc, numVertices = 100).mapEdges(e => e.attr.toDouble) 59 | val sourceId: VertexId = 42 // The ultimate source 60 | // Initialize the graph such that all vertices except the root have distance infinity. 61 | val initialGraph = graph.mapVertices((id, _) => if (id == sourceId) 0.0 else Double.PositiveInfinity) 62 | val sssp = initialGraph.pregel(Double.PositiveInfinity)( 63 | (id, dist, newDist) => math.min(dist, newDist), // Vertex Program 64 | triplet => { // Send Message 65 | if (triplet.srcAttr + triplet.attr < triplet.dstAttr) { 66 | Iterator((triplet.dstId, triplet.srcAttr + triplet.attr)) 67 | } else { 68 | Iterator.empty 69 | } 70 | }, 71 | (a,b) => math.min(a,b) // Merge Message 72 | ) 73 | println(sssp.vertices.collect.mkString("\n")) 74 | ``` 75 | -------------------------------------------------------------------------------- /graphx-programming-guide/property-graph.md: -------------------------------------------------------------------------------- 1 | # [屬性圖](http://spark.apache.org/docs/1.2.0/graphx-programming-guide.html#the-property-graph) 2 | 3 | [屬性圖](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph)是一個有向多重圖,它帶有連接到每個節點和邊的使用者定義的對象。 4 | 有向多重圖中多個平行(parallel)的邊共享相同的來源和目的地節點。支持平行邊的能力簡化了建模場景,這個場景中,相同的節點存在多種關係(例如co-worker和friend)。每個節點由一个 5 | 唯一的64位元的辨識碼(VertexID)作為key。GraphX並沒有對節點辨識碼限制任何的排序。同樣,節點擁有相應的來源和目的節點辨識碼。 6 | 7 | 屬性圖通過vertex(VD)和edge(ED)類型参数化,這些類型是分别與每個節點和邊相關聯的物件類型。 8 | 9 | 在某些情况下,在相同的圖形中,可能希望節點擁有不同的屬性類型。這可以通過繼承完成。例如,將用戶和產品視為一個二分圖,我們可以用以下方式 10 | 11 | ```scala 12 | class VertexProperty() 13 | case class UserProperty(val name: String) extends VertexProperty 14 | case class ProductProperty(val name: String, val price: Double) extends VertexProperty 15 | // The graph might then have the type: 16 | var graph: Graph[VertexProperty, String] = null 17 | ``` 18 | 和RDD一樣,屬性圖是不可變的、分布式的、容错的。圖的值或者結構的改變需要按期望的生成一個新的圖来實現。注意,原始圖的實質的部分(例如:不受影響的結構,屬性和索引)都可以在新圖中重用,用來减少這種內在的功能數據結構的成本。 19 | 執行者使用一系列節點分區試探法來對圖進行分區。如RDD一樣,圖中的每個分區可以在發生故障的情況下被重新創建在不同的機器上。 20 | 21 | 邏輯上的屬性圖對應於一對類型化的集合(RDD),這個集合編碼了每一個節點和邊的屬性。因此,圖類別包含訪問圖中的節點和邊的成員。 22 | 23 | ```scala 24 | class Graph[VD, ED] { 25 | val vertices: VertexRDD[VD] 26 | val edges: EdgeRDD[ED] 27 | } 28 | ``` 29 | 30 | `VertexRDD[VD]`和`EdgeRDD[ED]`類別分别繼承和最佳化自`RDD[(VertexID, VD)]`和`RDD[Edge[ED]]`。`VertexRDD[VD]`和`EdgeRDD[ED]`都支持額外的功能來建立在圖計算和利用内部最佳化。 31 | 32 | ## 屬性圖的例子 33 | 34 | 在GraphX項目中,假設我們想建構以下包括不同合作者的屬性圖。節點屬性可能包含用戶名和職業.我們可以用字串在邊上標記個合作者間的關係。 35 | 36 | ![屬性圖](../img/property_graph.png) 37 | 38 | 所得的圖形將具有類型簽名 39 | 40 | ```scala 41 | val userGraph: Graph[(String, String), String] 42 | ``` 43 | 有很多方式從一個原始文件、RDDs建構一個屬性圖。最一般的方法是利用[Graph object](https://sp變rk.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Graph$)。 44 | 下面的程式從RDDs產生屬性圖。 45 | 46 | ```scala 47 | // Assume the SparkContext has already been constructed 48 | val sc: SparkContext 49 | // Create an RDD for the vertices 50 | val users: RDD[(VertexId, (String, String))] = 51 | sc.parallelize(Array((3L, ("rxin", "student")), (7L, ("jgonzal", "postdoc")), 52 | (5L, ("franklin", "prof")), (2L, ("istoica", "prof")))) 53 | // Create an RDD for edges 54 | val relationships: RDD[Edge[String]] = 55 | sc.parallelize(Array(Edge(3L, 7L, "collab"), Edge(5L, 3L, "advisor"), 56 | Edge(2L, 5L, "colleague"), Edge(5L, 7L, "pi"))) 57 | // Define a default user in case there are relationship with missing user 58 | val defaultUser = ("John Doe", "Missing") 59 | // Build the initial Graph 60 | val graph = Graph(users, relationships, defaultUser) 61 | ``` 62 | 在上面的例子中,我們用到了以下[Edge](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.Edge)案例類別(case class)。邊有一個`srcId`和`dstId`分別對應於來源和目標節點的辨識碼。另外, `Edge` 類別有一個 `attr` 成員用来存儲存邊的屬性。 63 | 64 | 我們可以透過 `graph.vertices` 和 `graph.edges` 成員將一圖圖解構為相應的節點和邊。 65 | 66 | ```scala 67 | val graph: Graph[(String, String), String] // Constructed from above 68 | // Count all users which are postdocs 69 | graph.vertices.filter { case (id, (name, pos)) => pos == "postdoc" }.count 70 | // Count all the edg變s where src > dst 71 | graph.edges.filter(e => e.srcId > e.dstId).count 72 | ``` 73 | 74 | ``` 75 | 注意,graph.vertices 返回一個 VertexRDD[(String, String)],它繼承於 RDD[(VertexID, (String, String))]。所以我們可以用以下scala的case案例類別解構這個元組(tuple)。另一方面, 76 | graph.edges 返回一個包含 Edge[String] 物件的 EdgeRDD。我們也可以使用案例類別建構子,如下例所示。 77 | ``` 78 | 79 | ```scala 80 | graph.edges.filter { case Edge(src, dst, prop) => src > dst }.count 81 | ``` 82 | 83 | 84 | 除了屬性圖的節點和邊的檢視表,GraphX也包含了一個三元組(triplet)的檢視表,三元檢視表邏輯上將節點和邊的屬性保存為一個 `RDD[EdgeTriplet[VD, ED]]` ,它包含[EdgeTriplet](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.EdgeTriplet)類別的實例。 85 | 可以通過下面的Sql表達式表示這個連接(join)。 86 | ```sql 87 | SELECT src.id, dst.id, src.attr, e.attr, dst.attr 88 | FROM edges AS e LEFT JOIN vertices AS src, vertices AS dst 89 | ON e.srcId = src.Id AND e.dstId = dst.Id 90 | ``` 91 | 92 | 或者通過下面的圖來表示。 93 | 94 | ![triplet](../img/triplet.png) 95 | 96 | `EdgeTriplet` 類別繼承於 `Edge` 類別,並且加入了 `srcAttr` 和 `dstAttr` 成员,這兩個成員分別包含來源和目的的屬性。我們可以用以下三元組檢視表產生字串集合用來描述用戶之間的關係。 97 | 98 | ```scala 99 | val graph: Graph[(String, String), String] // Constructed from above 100 | // Use the triplets view to create an RDD of facts. 101 | val facts: RDD[String] = 102 | graph.triplets.map(triplet => 103 | triplet.srcAttr._1 + " is the " + triplet.attr + " of " + triplet.dstAttr._1) 104 | facts.collect.foreach(println(_)變 105 | ``` 106 | 107 | 108 | -------------------------------------------------------------------------------- /graphx-programming-guide/vertex-and-edge-rdds.md: -------------------------------------------------------------------------------- 1 | # 頂點和邊的RDDs 2 | 3 | GraphX提供了儲存在圖中的頂點和邊的RDD。因為GraphX將頂點和邊保存在優化過的資料結構中,這些資料結構提供了額外的功能,分別傳回`VertexRDD`和`EdgeRDD`。這一章節,我們將學習它們一些有用的功能。 4 | 5 | ## VertexRDDs 6 | 7 | `VertexRDD[A]`繼承了`RDD[(VertexID, A)]`並且新增了額外的限制條件,那就是每個`VertexID`只能出現一次。此外,`VertexRDD[A]`代表一組具有型別A特性的頂點。在程式內部,透過將頂點屬性儲存到一個可重複使用的hash-map的資料結構來達成。所以,若兩個`VertexRDDs`是從相同的`VertexRDD`(如藉由`filter`或`mapValues`)基底產生的,它們就能夠在常數時間內完成合併,而避免了hash的計算。為了利用索引式的資料結構,`VertexRDD`提供了下列的附加功能: 8 | 9 | ```scala 10 | class VertexRDD[VD] extends RDD[(VertexID, VD)] { 11 | // Filter the vertex set but preserves the internal index 12 | def filter(pred: Tuple2[VertexId, VD] => Boolean): VertexRDD[VD] 13 | // Transform the values without changing the ids (preserves the internal index) 14 | def mapValues[VD2](map: VD => VD2): VertexRDD[VD2] 15 | def mapValues[VD2](map: (VertexId, VD) => VD2): VertexRDD[VD2] 16 | // Remove vertices from this set that appear in the other set 17 | def diff(other: VertexRDD[VD]): VertexRDD[VD] 18 | // Join operators that take advantage of the internal indexing to accelerate joins (substantially) 19 | def leftJoin[VD2, VD3](other: RDD[(VertexId, VD2)])(f: (VertexId, VD, Option[VD2]) => VD3): VertexRDD[VD3] 20 | def innerJoin[U, VD2](other: RDD[(VertexId, U)])(f: (VertexId, VD, U) => VD2): VertexRDD[VD2] 21 | // Use the index on this RDD to accelerate a `reduceByKey` operation on the input RDD. 22 | def aggregateUsingIndex[VD2](other: RDD[(VertexId, VD2)], reduceFunc: (VD2, VD2) => VD2): VertexRDD[VD2] 23 | } 24 | ``` 25 | 26 | 舉例,`filter`運算子是如何回一個`VertexRDD`。`filter`實際上是由`BitSet`實作,因此重複使用索引值以及保留快速與其他`VertexRDDs`合併的能力。相同的,`mapValues`運算子不允許`map`函數改變`VertexID`,確保相同的`hashMap`的資料結構被重複使用。當合併兩個從相同`hashMap`得到的`VertexRDDs`且利用線性搜尋(linear scan)而非花費時間較長的點查詢(point lookups)來實現合併時,`leftJoin`和`innerJoin`都能夠使用。 27 | 28 | `aggregateUsingIndex`運算子能夠有效率地將一個`RDD[(VertexID, A)]`建造成一個新的`VertexRDD`。概念上,我透過一組為一些`VertexRDD[A]` 的`super-set`頂點建造了`VertexRDD[B]`,那麼我們就能夠在聚合(aggregate)和往後查詢`RDD[(VertexID, A)]`時重複使用索引。例如: 29 | 30 | ```scala 31 | val setA: VertexRDD[Int] = VertexRDD(sc.parallelize(0L until 100L).map(id => (id, 1))) 32 | val rddB: RDD[(VertexId, Double)] = sc.parallelize(0L until 100L).flatMap(id => List((id, 1.0), (id, 2.0))) 33 | // There should be 200 entries in rddB 34 | rddB.count 35 | val setB: VertexRDD[Double] = setA.aggregateUsingIndex(rddB, _ + _) 36 | // There should be 100 entries in setB 37 | setB.count 38 | // Joining A and B should now be fast! 39 | val setC: VertexRDD[Double] = setA.innerJoin(setB)((id, a, b) => a + b) 40 | ``` 41 | 42 | ## EdgeRDDs 43 | 44 | `EdgeRDD[ED]`繼承了`RDD[Edge[ED]]`,使用定義在[PartitionStrategy](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.graphx.PartitionStrategy)眾多分割方法其中一種,將邊作區塊性的分割。在每個分區中,邊的屬性和周遭結構會被個別的儲存,能夠在屬性改變時,最大化重用。 45 | 46 | `EdgeRDD`揭示了三個額外的函數: 47 | 48 | ```scala 49 | // Transform the edge attributes while preserving the structure 50 | def mapValues[ED2](f: Edge[ED] => ED2): EdgeRDD[ED2] 51 | // Revere the edges reusing both attributes and structure 52 | def reverse: EdgeRDD[ED] 53 | // Join two `EdgeRDD`s partitioned using the same partitioning strategy. 54 | def innerJoin[ED2, ED3](other: EdgeRDD[ED2])(f: (VertexId, VertexId, ED, ED2) => ED3): EdgeRDD[ED3] 55 | ``` 56 | 57 | 在多數的應用中,我們會發現`EdgeRDD`的操作可以透過圖形運算子(graph operators)或是定義在基本RDD中的操作來完成。 58 | 59 | ## Optimized Representation 60 | -------------------------------------------------------------------------------- /img/data_parallel_vs_graph_parallel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/data_parallel_vs_graph_parallel.png -------------------------------------------------------------------------------- /img/flume.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/flume.png -------------------------------------------------------------------------------- /img/graph_analytics_pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/graph_analytics_pipeline.png -------------------------------------------------------------------------------- /img/property_graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/property_graph.png -------------------------------------------------------------------------------- /img/streaming-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/streaming-arch.png -------------------------------------------------------------------------------- /img/streaming-dstream-ops.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/streaming-dstream-ops.png -------------------------------------------------------------------------------- /img/streaming-dstream-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/streaming-dstream-window.png -------------------------------------------------------------------------------- /img/streaming-dstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/streaming-dstream.png -------------------------------------------------------------------------------- /img/streaming-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/streaming-flow.png -------------------------------------------------------------------------------- /img/streaming-kinesis-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/streaming-kinesis-arch.png -------------------------------------------------------------------------------- /img/tables_and_graphs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/tables_and_graphs.png -------------------------------------------------------------------------------- /img/triplet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TaiwanSparkUserGroup/spark-programming-guide-zh-tw/6841e863f463e36b408308ab4fe90e60ddb6fed8/img/triplet.png -------------------------------------------------------------------------------- /mllib/README.md: -------------------------------------------------------------------------------- 1 | # MLlib 2 | MLlib is Spark’s scalable machine learning library consisting of common learning algorithms and utilities, including classification, regression, clustering, collaborative filtering, dimensionality reduction, as well as underlying optimization primitives, as outlined below: 3 | MLlib是Spark分擴展的機器學習庫,由常見的學習算法及應用程序 4 | 所構成,其中包含分類(classification),迴歸(regression),群集(clustering),協同過濾(collaborative filtering),降維(dimensionality reduction)以及底層最佳化算法。 5 | -------------------------------------------------------------------------------- /mllib/basic_statistics.md: -------------------------------------------------------------------------------- 1 | # 基本統計分析 2 | 此章節共有以下幾個主題 3 | * 概述統計量(Summary statistics) 4 | * 關聯(Correlations) 5 | * 分層抽樣(Stratified sampling) 6 | * 假設檢定(Hypothesis testing) 7 | * 隨機數據生成(Random data generation) 8 | -------------------------------------------------------------------------------- /mllib/binary_classification.md: -------------------------------------------------------------------------------- 1 | # 二元分類 2 | [二元分類](http://en.wikipedia.org/wiki/Logistic_regression)將數據項分成兩類: 正例及負例。MLlib支持兩種二元分類的線性方法:線性支持向量機與邏輯斯迴歸。對這兩種方法來說,MLlib都支持L1, L2正則化。在MLlib中,訓練數据集用一個LabeledPoint格式的RDD來表示。需要注意,本指南中的數學公式裡,約定訓練標簽$$y$$為+1(正例)或-1(反例),但在MLlib中,為了與多煩標簽保持一致,反例標簽是0, 而不是-1。 3 | -------------------------------------------------------------------------------- /mllib/classification_and_regression.md: -------------------------------------------------------------------------------- 1 | # Classification and Regression 2 | MLlib支特二元分類,多元分類,回歸分析等多種算表,下表列出問題類別及其相關的算法: 3 | 4 | | 問題類別 | 支持算法 | 5 | |---|---| 6 | | 二元分類 |線性支持向量機(linear SVMs),邏輯斯迴歸( logistic regression),決策樹(decision trees), 單純貝氏(naive Bayes) | 7 | |多元分類 |決策樹,單純貝氏 | 8 | | 迴歸 | 線性最小平方法,Lasso,脊迴歸(ridge regression) | 9 | 10 | 這些方法的更多細節請參照下面內容: 11 | * 線性模型 12 | * 二元分類(支特向量機,邏輯斯迴歸) 13 | * 線性迴歸(最小平方法,Lasso,ridge) 14 | * 決策樹 15 | * 單純貝氏 16 | 17 | -------------------------------------------------------------------------------- /mllib/coordinatematrix.md: -------------------------------------------------------------------------------- 1 | # CoordinateMatrix 2 | CoordinateMatrix是一个分布式矩陣,其實體集合是一個 RDD。每一个實體是一个(i:Long, j:Long, value:Double)座標  ,其中i代表行索引,j代表列索引,value代 表實体的值。只有當矩陣的行和列都很巨大,並且矩陣很稀疏时才使用 CoordinateMatrix。 3 | 4 | 一个CoordinateMatrix可從一個RDD[MatrixEntry]實例創建,这里的 MatrixEntry是的(Long, Long, Double)的封裝尸類。通過調用toIndexedRowMatrix可以將一个CoordinateMatrix 轉變为一個IndexedRowMatrix(但其行是稀疏的)。目前暂不支持其他計算操作。 5 | 6 | ```scala 7 | import org.apache.spark.mllib.linalg.distributed.{CoordinateMatrix, MatrixEntry} 8 | 9 | val entries: RDD[MatrixEntry] = ... // an RDD of matrix entries 10 | // Create a CoordinateMatrix from an RDD[MatrixEntry]. 11 | val mat: CoordinateMatrix = new CoordinateMatrix(entries) 12 | 13 | // Get its size. 14 | val m = mat.numRows() 15 | val n = mat.numCols() 16 | 17 | // Convert it to an IndexRowMatrix whose rows are sparse vectors. 18 | val indexedRowMatrix = mat.toIndexedRowMatrix() 19 | ``` 20 | -------------------------------------------------------------------------------- /mllib/correlations.md: -------------------------------------------------------------------------------- 1 | # 相關性(Correlations) 2 | 在統計分析中,計算兩系列數據之間的相關性很常見。在 MLlib 中,我們提供了用於計算多系列數據之間兩兩關系的靈活性。目前支持的相關性算法是 Perarson 和 Spearsman 相關。 3 | 4 | [Statistics](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.stat.Statistics$)提供了計算系列間相關性的方法。根據輸入類型,兩個RDD[Double] 或一個RDD[Vector] ,輸出相應的一个Double或相關矩陣。 5 | ```scala 6 | import org.apache.spark.SparkContext 7 | import org.apache.spark.mllib.linalg._ 8 | import org.apache.spark.mllib.stat.Statistics 9 | 10 | val sc: SparkContext = ... 11 | 12 | val seriesX: RDD[Double] = ... // a series 13 | val seriesY: RDD[Double] = ... // must have the same number of partitions and cardinality as seriesX 14 | 15 | // compute the correlation using Pearson's method. Enter "spearman" for Spearman's method. If a 16 | // method is not specified, Pearson's method will be used by default. 17 | val correlation: Double = Statistics.corr(seriesX, seriesY, "pearson") 18 | 19 | val data: RDD[Vector] = ... // note that each Vector is a row and not a column 20 | 21 | // calculate the correlation matrix using Pearson's method. Use "spearman" for Spearman's method. 22 | // If a method is not specified, Pearson's method will be used by default. 23 | val correlMatrix: Matrix = Statistics.corr(data, "pearson") 24 | ``` 25 | -------------------------------------------------------------------------------- /mllib/data_type.md: -------------------------------------------------------------------------------- 1 | # 數據類型 2 | 3 | MLlib支持本地向量以及矩陣貯存在一台機器上,並且通過一或多個RDD備份分布式的矩陣。本地向量及本地矩陣是作為簡易的數據模型的公用接口。底層的學習算法操作是由Breeze以及jbloas所提供的。在MLlib中,所謂的"labeled point"(標記點)是指監督式學習中的一個訓練例子。 4 | -------------------------------------------------------------------------------- /mllib/distributed_matrix.md: -------------------------------------------------------------------------------- 1 | # 分布矩陣 2 | 3 | 一個分布矩陣有長整數類型(long-typed)的行列索引以及浮點類型的值, 被分散地儲存在一或多個RDD中。選擇一個正確的格式去儲存成巨大且分布的矩陣是非常重要的。將一個分布矩陣轉換成一個不同的格式可能需要請求一個全域洗牌(global shuffle),這是相當昂貴的。目前為止,已經實作三種類型的分布矩陣。 4 | 5 | 6 | 此基本類型被稱為RowMatrix。RowMatrix是一個面向行的分布矩陣,而不是具有意義的行索引,例如:特徵向量的集合。透過一個RDD來表示所有的行,其中每一行都是一個本地向量。 我們假設RowMatrix的列數量並不巨大,所以單一本地向量可以合理的傳達給driver,也可以儲存/操作正在使用的單一節點上。 7 | 8 | 9 | IndexedRowMatrix類似於RowMatrix,但具有行索引,可以被使用在識別行以及執行關聯(join)。 10 | 11 | CoordinateMatrix是儲存在座標列表格式(coordinate list (COO) format)中的分布矩陣,其實體集合是一個RDD。 12 | 13 | 14 | ##### Note 15 | 16 | 由於我們緩存矩陣的大小,所以在分布矩陣的底層RDD必須是確定的,。一般使用非確定的RDD可能會導致錯誤。 17 | -------------------------------------------------------------------------------- /mllib/evaluation_metrics.md: -------------------------------------------------------------------------------- 1 | # 評估指標(Evaluation metrics) 2 | MLlib支持常用的二元分類評估指標方法(在PySpark中不可用)。包括精度,召回率,F度量( [F-measure](http://en.wikipedia.org/wiki/F1_score)),接收者特徵操作曲線([receiver operating characteristic, ROC](http://en.wikipedia.org/wiki/Receiver_operating_characteristic)),精度-召回率曲線以及曲面下面積([ area under the curves, AUC](http://en.wikipedia.org/wiki/Receiver_operating_characteristic#Area_under_the_curve))。AUC常用來比較不同模型的性能,精度/召回率/F度量用來決定閾值(threshold)時為預測指定恰當閾值。 3 | -------------------------------------------------------------------------------- /mllib/examples.md: -------------------------------------------------------------------------------- 1 | # 示例 2 | 下面代碼片段演示了如何加載數據集,運用算法對象的靜態方法執行訓練算法,以及運用模型預測來計算訓練誤差。 3 | ```scala 4 | import org.apache.spark.SparkContext 5 | import org.apache.spark.mllib.classification.SVMWithSGD 6 | import org.apache.spark.mllib.evaluation.BinaryClassificationMetrics 7 | import org.apache.spark.mllib.regression.LabeledPoint 8 | import org.apache.spark.mllib.linalg.Vectors 9 | import org.apache.spark.mllib.util.MLUtils 10 | 11 | // Load training data in LIBSVM format. 12 | val data = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt") 13 | 14 | // Split data into training (60%) and test (40%). 15 | val splits = data.randomSplit(Array(0.6, 0.4), seed = 11L) 16 | val training = splits(0).cache() 17 | val test = splits(1) 18 | 19 | // Run training algorithm to build the model 20 | val numIterations = 100 21 | val model = SVMWithSGD.train(training, numIterations) 22 | 23 | // Clear the default threshold. 24 | model.clearThreshold() 25 | 26 | // Compute raw scores on the test set. 27 | val scoreAndLabels = test.map { point => 28 | val score = model.predict(point.features) 29 | (score, point.label) 30 | } 31 | 32 | // Get evaluation metrics. 33 | val metrics = new BinaryClassificationMetrics(scoreAndLabels) 34 | val auROC = metrics.areaUnderROC() 35 | 36 | println("Area under ROC = " + auROC) 37 | ``` 38 | 預設配置下,SVMWithSGD.train()將正則化參數設置為1.0來進行L2正則化。如果我們想配置算法參數,我們可以直接生一個SVMWithSGD對象,然後調用setter方法。所有其他的MLlib算法都支持這 種自定義方法。舉例來說,下面代碼生了一個用於SVM的L1正則化變量,其正則化參數為0.1,且迭代次數為200。 39 | ```scala 40 | import org.apache.spark.mllib.optimization.L1Updater 41 | 42 | val svmAlg = new SVMWithSGD() 43 | svmAlg.optimizer. 44 | setNumIterations(200). 45 | setRegParam(0.1). 46 | setUpdater(new L1Updater) 47 | val modelL1 = svmAlg.run(training) 48 | ``` 49 | [LogisticRegressionWithSGD](https://spark.apache.org/docs/latest/api/scala/index.html#package)的使用方法與SVMWithSGD相似。 50 | -------------------------------------------------------------------------------- /mllib/hypothesis_testing.md: -------------------------------------------------------------------------------- 1 | # 假設檢定 2 | 在統計分析中,假設檢定是一個強大的工具,用來判斷結果的統計量是否充分,以及結果是否隨機。MLlib目前支持Pearson卡方檢定($$\chi^2$$) 來檢定適配度和獨立性。輸入數據類型決定了是否產生適配度或獨立性,適配度檢定需要Vector輸入類型,而獨立性檢定需要一個Matrix矩陣輸入。 3 | 4 | MLlib也支持RDD[LabeledPoint]輸入類型,然後使用卡方獨立性檢定來進行特徵選擇。 5 | 6 | [Statistics](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.stat.Statistics$)提供了進行Pearson卡方檢定的方法。下面示例演示了怎樣運行和解釋假設定。 7 | ```scala 8 | import org.apache.spark.SparkContext 9 | import org.apache.spark.mllib.linalg._ 10 | import org.apache.spark.mllib.regression.LabeledPoint 11 | import org.apache.spark.mllib.stat.Statistics._ 12 | 13 | val sc: SparkContext = ... 14 | 15 | val vec: Vector = ... // a vector composed of the frequencies of events 16 | 17 | // compute the goodness of fit. If a second vector to test against is not supplied as a parameter, 18 | // the test runs against a uniform distribution. 19 | val goodnessOfFitTestResult = Statistics.chiSqTest(vec) 20 | println(goodnessOfFitTestResult) // summary of the test including the p-value, degrees of freedom, 21 | // test statistic, the method used, and the null hypothesis. 22 | 23 | val mat: Matrix = ... // a contingency matrix 24 | 25 | // conduct Pearson's independence test on the input contingency matrix 26 | val independenceTestResult = Statistics.chiSqTest(mat) 27 | println(independenceTestResult) // summary of the test including the p-value, degrees of freedom... 28 | 29 | val obs: RDD[LabeledPoint] = ... // (feature, label) pairs. 30 | 31 | // The contingency table is constructed from the raw (feature, label) pairs and used to conduct 32 | // the independence test. Returns an array containing the ChiSquaredTestResult for every feature 33 | // against the label. 34 | val featureTestResults: Array[ChiSqTestResult] = Statistics.chiSqTest(obs) 35 | var i = 1 36 | featureTestResults.foreach { result => 37 | println(s"Column $i:\n$result") 38 | i += 1 39 | } // summary of the test 40 | ``` 41 | -------------------------------------------------------------------------------- /mllib/indexedrowmatrix.md: -------------------------------------------------------------------------------- 1 | # IndexedRowMatrix 2 | 3 | IndexedRowMatrix與RowMatrix是相似的,但其行索引具有特定定含義,本質上是一個含有索引訊息的行數據集合(an RDD of indexed rows)。 4 | 每一行由long類型索引和一個本地向量組成。 5 | 6 | 7 | 8 | [IndexedRowMatrix](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.distributed.IndexedRowMatrix)可以由一個RDD[IndexedRow]實例被創建,其中[IndexedRow](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.linalg.distributed.IndexedRow)是一個被包裝過的(Long, Vcetor)。IndexedRowMatrix可以透過刪除行索引被轉換成RowMatrix。 9 | ```scala 10 | import org.apache.spark.mllib.linalg.distributed.{IndexedRow, IndexedRowMatrix, RowMatrix} 11 | import util.Random 12 | 13 | val rows: RDD[IndexedRow] = ... // an RDD of indexed rows 14 | // Create an IndexedRowMatrix from an RDD[IndexedRow]. 15 | val mat: IndexedRowMatrix = new IndexedRowMatrix(rows) 16 | 17 | // Get its size. 18 | val m = mat.numRows() 19 | val n = mat.numCols() 20 | 21 | // Drop its row indices. 22 | val rowMat: RowMatrix = mat.toRowMatrix() 23 | ``` 24 | -------------------------------------------------------------------------------- /mllib/labeled_point.md: -------------------------------------------------------------------------------- 1 | # 標記點(Labeled point) 2 | 3 | 標記點是一個本地向量,無論密集(dense)或稀疏(sparse)均會與一一個標記/響應相關。在MLlib中,標記點被使用在監督式學習算法中。我們使用一個double去儲存一個標記(label),那麼我們將可以在迴歸(regression)及分類(classification)中使用標記點。在二元分類中,標記應該是0或1;而在多類分類中,標記應為從0開始的的索引:0, 1, 2, ... 4 | 5 | 6 | 一個標記點用 [LabeledPoint](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.regression.LabeledPoint)來表示(Scala中它屬於一個case class) 7 | 8 | ```scala 9 | import org.apache.spark.mllib.linalg.Vectors 10 | import org.apache.spark.mllib.regression.LabeledPoint 11 | 12 | // Create a labeled point with a positive label and a dense feature vector. 13 | val pos = LabeledPoint(1.0, Vectors.dense(1.0, 0.0, 3.0)) 14 | 15 | // Create a labeled point with a negative label and a sparse feature vector. 16 | val neg = LabeledPoint(0.0, Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0))) 17 | ``` 18 | 19 | * Sparse data 20 | 21 | 擁有sparse訓練資料是很常見的做法。MLlib支持讀取為LIBSVM格式的訓練實例,它使用[LIBSVM](http://www.csie.ntu.edu.tw/~cjlin/libsvm/)與[LIBLINEAR](http://www.csie.ntu.edu.tw/~cjlin/liblinear/)做為預設格式。它是一種文本格式,每一行表示成一個標記稀疏特徵向量(labeled sparse feature vector),使用以下的格式: 22 | ``` 23 | label index1:value1 index2:value2 ... 24 | ``` 25 | 26 | 其中索引是以1為基索引(one-based)並升序。在讀取後,這些特徵索引會被轉換成以0為基索引。 27 | 28 | 29 | MLUtils.loadLibSVMFile 讀取LIBSVM格式的訓練實例 30 | ```scala 31 | import org.apache.spark.mllib.regression.LabeledPoint 32 | import org.apache.spark.mllib.util.MLUtils 33 | import org.apache.spark.rdd.RDD 34 | 35 | val examples: RDD[LabeledPoint] = MLUtils.loadLibSVMFile(sc, "data/mllib/sample_libsvm_data.txt") 36 | ``` 37 | -------------------------------------------------------------------------------- /mllib/linear_methods.md: -------------------------------------------------------------------------------- 1 | # Linear Methods 2 | * 數學公式 3 | * 損失函數(Loss functions) 4 | * 正則化(Regularizers) 5 | * 最佳化 6 | * 二元分類 7 | * 線性支持向量機(Linear Support Vector Machines, SVMs) 8 | * 邏輯斯迴歸(Logistic regression) 9 | * 評估指標(Evaluation metrics) 10 | * 示例 11 | * 最小平方法(Linear least squares), Lasso, 及 脊迴歸(ridge regression) 12 | * 示例 13 | * 流式線性迴歸(Streaming linear regression) 14 | * 示例 15 | * 實作 (開發者) 16 | -------------------------------------------------------------------------------- /mllib/local_matrix.md: -------------------------------------------------------------------------------- 1 | # 本地矩陣 2 | 3 | 本地矩陣儲存在單一台機器上,它有整數類型行列索引,以及浮點類型的值。MLlib支持密度矩陣(dense matrices),其輸入值是被儲存在單一個以列為主的浮點陣列。舉例來說,以下這個矩陣 4 | $$\begin{pmatrix} 5 | 1.0 & 2.0\\ 6 | 3.0 & 4.0\\ 7 | 5.0 & 6.0\\ 8 | \end{pmatrix}$$ 9 | 10 | 被儲存在一個一維陣列[1.0, 3.0, 5.0, 2.0, 4.0, 6.0]以及大小為(3, 2)的矩陣中。 11 | 12 | 13 | 本地矩陣的基類為Matrix,並且提供一個實作類:DenseMatrix。我㫓建議使用Matrices中的工廠方法去創建本地矩陣。 14 | ```scala 15 | import org.apache.spark.mllib.linalg.{Matrix, Matrices} 16 | 17 | // Create a dense matrix ((1.0, 2.0), (3.0, 4.0), (5.0, 6.0)) 18 | val dm: Matrix = Matrices.dense(3, 2, Array(1.0, 3.0, 5.0, 2.0, 4.0, 6.0)) 19 | ``` 20 | -------------------------------------------------------------------------------- /mllib/local_vector.md: -------------------------------------------------------------------------------- 1 | # 本地向量 2 | 3 | 一個本地向量有intereage, 0-based indices及double類型,貯存在單一機器上。MLlib 支持兩種類型的本地向量: dense及sparse。 4 | 5 | dense向量:透過輸入double陣列回傳。 6 | 7 | sparse向量:透過兩個平行陣列:indices及values回傳 8 | 9 | 舉個例子,向量(1.0, 0.0, 3.0)可以被表示成: 10 | 11 | dense格式-[1.0, 0.0, 3.0] 12 | 13 | sparse格式-(3, [0, 2], [1.0, 3.0]),3表示此vector的大小。 14 | 15 | 16 | 本地向量的基類是Vector,而且提供DenseVector及SparseVector兩個實作。以下介紹使用工廠方法在Vectors中去創建本地向量。 17 | ```scala 18 | import org.apache.spark.mllib.linalg.{Vector, Vectors} 19 | 20 | // Create a dense vector (1.0, 0.0, 3.0). 21 | val dv: Vector = Vectors.dense(1.0, 0.0, 3.0) 22 | // Create a sparse vector (1.0, 0.0, 3.0) by specifying its indices and values corresponding to nonzero entries. 23 | val sv1: Vector = Vectors.sparse(3, Array(0, 2), Array(1.0, 3.0)) 24 | // Create a sparse vector (1.0, 0.0, 3.0) by specifying its nonzero entries. 25 | val sv2: Vector = Vectors.sparse(3, Seq((0, 1.0), (2, 3.0))) 26 | ``` 27 | Note: Scala會預設匯入scala.collection.immutable.Vector,所以你必須自行匯入org.apache.spark.mllib.linalg.Vector,才能使用MLlib的Vector 28 | 29 | -------- 30 | 31 | DenseVector 與 SparseVector源碼 32 | 33 | 34 | ```scala 35 | @SQLUserDefinedType(udt = classOf[VectorUDT]) 36 | class DenseVector(val values: Array[Double]) extends Vector { 37 | 38 | override def size: Int = values.length 39 | 40 | override def toString: String = values.mkString("[", ",", "]") 41 | 42 | override def toArray: Array[Double] = values 43 | 44 | private[mllib] override def toBreeze: BV[Double] = new BDV[Double](values) 45 | 46 | override def apply(i: Int) = values(i) 47 | 48 | override def copy: DenseVector = { 49 | new DenseVector(values.clone()) 50 | } 51 | 52 | private[spark] override def foreachActive(f: (Int, Double) => Unit) = { 53 | var i = 0 54 | val localValuesSize = values.size 55 | val localValues = values 56 | 57 | while (i < localValuesSize) { 58 | f(i, localValues(i)) 59 | i += 1 60 | } 61 | } 62 | } 63 | 64 | ``` 65 | 66 | ```scala 67 | 68 | /** 69 | * A dense vector represented by a value array. 70 | */ 71 | @SQLUserDefinedType(udt = classOf[VectorUDT]) 72 | class DenseVector(val values: Array[Double]) extends Vector { 73 | 74 | override def size: Int = values.length 75 | 76 | override def toString: String = values.mkString("[", ",", "]") 77 | 78 | override def toArray: Array[Double] = values 79 | 80 | private[mllib] override def toBreeze: BV[Double] = new BDV[Double](values) 81 | 82 | override def apply(i: Int) = values(i) 83 | 84 | override def copy: DenseVector = { 85 | new DenseVector(values.clone()) 86 | } 87 | 88 | private[spark] override def foreachActive(f: (Int, Double) => Unit) = { 89 | var i = 0 90 | val localValuesSize = values.size 91 | val localValues = values 92 | 93 | while (i < localValuesSize) { 94 | f(i, localValues(i)) 95 | i += 1 96 | } 97 | } 98 | } 99 | 100 | ``` 101 | -------------------------------------------------------------------------------- /mllib/logistic_regression.md: -------------------------------------------------------------------------------- 1 | # 邏輯斯迴歸(Logistic regression) 2 | 邏輯斯迴歸廣泛被運用於二元變量預測。它是之前"數學公式"一節中竹竹尸一中描述的線性方法,其損失函數是logistic losss: 3 | $$ 4 | L(w;x,y)=log(1+exp(-yx^Tx)) 5 | $$ 6 | 迴輯斯迴歸算法的產出是一個卜口田中土輯斯迴歸模型。給定新數據點$$x$$,該模型運用下面的邏輯函數來預測: 7 | $$ 8 | f(z)=\frac{1}{1+e^{-z}} 9 | $$ 10 | 在這裡,$$z=w^Tx$$。預設情況下,若$$f(w^Tx)\gt0.5$$輸出是正例,否則是反例。與線性SVM不同之處在於,線性迴歸模型$$f(z)$$輸出含有一個機率解釋(即$$x$$)是正例的機率。 11 | -------------------------------------------------------------------------------- /mllib/loss_function.md: -------------------------------------------------------------------------------- 1 | # 損失函數 2 | 下表概述了MLlib支持的損失函數及其梯度和子梯度: 3 | 4 | 5 | | | 損失函數$$L(w;x,y)$$ | 梯度或子梯度 | 6 | |--- |--- |--- | 7 | | hinge loss |$$max\{0, 1-yw^Tx\} $$ | $$ \left\{ \begin{array}{ll} -yx ~~~ 若~yw^Tx \lt1, \\ 0 ~~~~~~~~~otherwise \end{array} \right.$$| 8 | | logistic loss |$$log(1+exp(-yw^Tx)),~y \in \{-1,+1\}$$ |$$-y(1-\frac{1}{1+exp(-yw^Tx)})x$$ | 9 | | squared loss | $$\frac{1}{2}(x^Tx-y)^2,~y \in R$$ | $$(w^Tx-y)x$$ | 10 | -------------------------------------------------------------------------------- /mllib/mathematical_formulation.md: -------------------------------------------------------------------------------- 1 | # 數學公式(Mathematical formulation) 2 | 許多標準機器學習方法可以被轉換為凸優化問題(convex optimization problem), 即一個找到凸函數$$f$$最小值的任務,這個函數$$f$$依賴於一個有d個值的向量變量$$w$$(代碼中的weights)。更正式點,這是一個$$min_{w \in R^d} f(x)$$優化問題,其目標函數$$f$$具有下面形式: 3 | $$ 4 | f(x):=\lambda R(w)+\frac{1}n \sum_{i=1}^{n}L(w; x_i, y_i) 5 | $$ 6 | 向量$$x_i \in R^d$$是訓練數據樣本,其中$$1\leq i\leq n$$。 $$y_i \in R$$是相對應的類標簽,也是我們想要預測的目標。如果$$L(w; x, y)$$能被表述為$$w^Tx$$和$$y$$的一個函數,我們稱該方法為線性的,有機個MLlib分類和迴歸算法屬於該范疇,我們在此一一討論。 7 | 8 | 目標函數$$f$$包括兩部份:控制模型複雜度的正則化因子和度量模型誤差的損失函數。損失函數$$L(w;.)$$是典型與$$w$$相關的凸函數。事先鎖定正則化參數$$\lambda \geq 0$$(代碼中的regParam)承載了我們在最小化損失量(訓練誤差)和最小化模型複雜度(避免過渡擬合)兩個目標之間的權衡取捨。 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /mllib/optimization.md: -------------------------------------------------------------------------------- 1 | # 最佳化 2 | 3 | 4 | 事實上,線性方法使用凸優化方法去最佳化目標函數。MLlib使用SGD及L-BFGS兩個方法,它們將在最佳化的章節被介紹。目前,大多數的算法API支持隨機梯法下降(Stochastic Gradient Descent, SGD),也有一些支持 L-BFGS。在最佳化方法做選擇,可以參照最佳化章節的指南。 5 | -------------------------------------------------------------------------------- /mllib/random_data_generation.md: -------------------------------------------------------------------------------- 1 | # 隨機數據生成 2 | 隨機數據生成對隨機算法,原型及性能測式來說是有用的。MLlib支持指定分類型來生成隨機的RDD,如均勻,標準常態,Possion分布。 3 | 4 | [RandomRDDs](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.mllib.random.RandomRDDs)提供工廠方法來生成隨機double RDD或vector RDDs。下面示例生成一個隨機的dobule RDD,其值服從標準常態分布$$N(0,1)$$,然後將其映射為$$N(0,1)$$。 5 | 6 | ```scala 7 | import org.apache.spark.SparkContext 8 | import org.apache.spark.mllib.random.RandomRDDs._ 9 | 10 | val sc: SparkContext = ... 11 | 12 | // Generate a random double RDD that contains 1 million i.i.d. values drawn from the 13 | // standard normal distribution `N(0, 1)`, evenly distributed in 10 partitions. 14 | val u = normalRDD(sc, 1000000L, 10) 15 | // Apply a transform to get a random double RDD following `N(1, 4)`. 16 | val v = u.map(x => 1.0 + 2.0 * x) 17 | ``` 18 | -------------------------------------------------------------------------------- /mllib/regularizers.md: -------------------------------------------------------------------------------- 1 | # 正則化 2 | 正則化的目標是獲得簡單模型和避免過度似合。在MLilb中,支持下面正則化因子: 3 | 4 | | | 正則化因子$$R(w)$$ | 梯度或子梯度 | 5 | |---|---|---| 6 | | 零(未正則化) | 0 | 0 | 7 | | L2范數 | $$\frac{1}{2} \Vert w\Vert_2^2$$ | $$w$$ | 8 | | L1范數 | $$\Vert w\Vert_1 $$ | $$sign(w)$$ | 9 | 在這裡,$$sign(w)$$是表示$$w$$中所有實體的類標簽( $$sign(\pm 1)$$ )的向量。 10 | 11 | 與L1正則化問題比較,由於L2的平滑性,L2的正則化問題一般較容易解決。但是由於可以強化權重的稀疏性,L1正則化更能產生較小及更容易解釋的模型,而後者在特徵選擇是非常有用的。不正則化而去訓練模型是不恰當的,尤其是在訓練樣本數量較小的時候。 12 | -------------------------------------------------------------------------------- /mllib/rowmatrix.md: -------------------------------------------------------------------------------- 1 | # RowMatrix 2 | 3 | 一个 RowMatrix 是一个面向行的分布式矩阵,其行索引是没有具体的含義。例如:一系列特征向量的一个集合。通過一个 RDD 来代表所有的行,每一行就是一个本地向量。既然 每一行由一个本地向量表示,所以其列数就被整型数据大小所限制,其實實作中列数是一個很小的数值。 4 | 5 | RowMatrix可以由RDD[Vector] 實例被創建。接著我們可以計算列的摘要統計量。 6 | 7 | ```scala 8 | import org.apache.spark.mllib.linalg.Vector 9 | import org.apache.spark.mllib.linalg.distributed.RowMatrix 10 | 11 | 12 | val rows: RDD[Vector] = ... // an RDD of local vectors 13 | // Create a RowMatrix from an RDD[Vector]. 14 | val mat: RowMatrix = new RowMatrix(rows) 15 | 16 | // Get its size. 17 | val m = mat.numRows() 18 | val n = mat.numCols() 19 | ``` 20 | -------------------------------------------------------------------------------- /mllib/stratified_sampling.md: -------------------------------------------------------------------------------- 1 | # 分層抽樣 2 | 在 MLlib 中,不同於其他统計方法,分層抽样方法如 sampleByKey 和 sampleByKeyExact,運行在鍵值對格式的 RDD 上。對分層抽样来说,keys是一个標簽, 值是特定的屬性。比如,key 可以是男人或女人、文檔 ID,其相應的值可以是人口數據中 的年齡列表或者文檔中的詞列表。sampleByKey 方法對每一个觀測擲幣決定是否抽中它, 所以需要對數據進行一次遍歷,也需要輸入期望抽样的大小。而 sampleByKeyExact方法並不是簡單地在每一層中使用sampleByKey 方法随機抽样,它需要更多資源,但將提供信心度高達 99.99%的精確抽样大小。在 Python 中,目前不支持 sampleByKeyExact。 3 | 4 | [sampleByKeyExact()](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.PairRDDFunctions)充許使用者準確抽取 $$[f_k \cdot n_k] 5 | \quad\forall k\in\mathbb K $$個元素,這裡的$$f_k$$是從鍵k中期望抽取的比例,$$n_k$$是從鍵k中抽取的鍵值對數量,而$$\mathbb K$$是鍵的集合。為了確保抽樣大小,無放回抽樣對數據會多一次遍歷;然而,有放回的抽樣會多兩次遍歷。 6 | 7 | ```scala 8 | import org.apache.spark.SparkContext 9 | import org.apache.spark.SparkContext._ 10 | import org.apache.spark.rdd.PairRDDFunctions 11 | 12 | val sc: SparkContext = ... 13 | 14 | val data = ... // an RDD[(K, V)] of any key value pairs 15 | val fractions: Map[K, Double] = ... // specify the exact fraction desired from each key 16 | 17 | // Get an exact sample from each stratum 18 | val approxSample = data.sampleByKey(withReplacement = false, fractions) 19 | val exactSample = data.sampleByKeyExact(withReplacement = false, fractions) 20 | ``` 21 | -------------------------------------------------------------------------------- /mllib/summary_statistics.md: -------------------------------------------------------------------------------- 1 | # 概述統計量(Summary Statistics) 2 | 对 RDD[Vector]格式數據的概述統計量,我们提供 Statistics 中的 colStats 方法来實现。 3 | 4 | colStats()方法返回一個MultivariateStatisticalSummary實例,其中包括面向列的最大值,最小值,平均,變異數,非零值個數以及總數量。 5 | ```scala 6 | import org.apache.spark.mllib.linalg.Vector 7 | import org.apache.spark.mllib.stat.{MultivariateStatisticalSummary, Statistics} 8 | 9 | val observations: RDD[Vector] = sc.textFile("data/mllib/sample_lda_data.txt").map(s=>Vectors.dense(s.split(" ").map(_.toDouble))) 10 | ... // an RDD of Vectors 11 | 12 | // Compute column summary statistics. 13 | val summary: MultivariateStatisticalSummary = Statistics.colStats(observations) 14 | println(summary.mean) // a dense vector containing the mean value for each column 15 | println(summary.variance) // column-wise variance 16 | println(summary.numNonzeros) // number of nonzeros in each column 17 | ``` 18 | -------------------------------------------------------------------------------- /mllib/svms.md: -------------------------------------------------------------------------------- 1 | # 線性支持向量機(SVMs) 2 | 對於大規模的分類任務來說,[線性支持向量機](http://en.wikipedia.org/wiki/Support_vector_machine#Linear_SVM)是標準的方法。它是之前"數學公式"一節中所描述的線性方法,廿金金損失函數是hinge loss: 3 | $$ 4 | L(w;x,y)=\max\{0,1-yw^Tx\}. 5 | $$ 6 | 預設配置下,線性SVM使用L2正則化訓練。我們支持L1正則化。通過這種方式,問題變為線性規劃問題。 7 | 8 | 線性SVM算法是產出一個SVM模型。給定新數據點$$x$$,該模型基於$$w^Tx$$的值來預測。默認情形下,$$w^Tx\geq0$$時為正例,否則為反例。 9 | -------------------------------------------------------------------------------- /more/spark-configuration.md: -------------------------------------------------------------------------------- 1 | # Spark配置 2 | 3 | Spark提供三个位置用来配置系统: 4 | 5 | - Spark properties控制大部分的应用程序参数,可以用SparkConf对象或者java系统属性设置 6 | - Environment variables可以通过每个节点的` conf/spark-env.sh`脚本设置每台机器的设置。例如IP地址 7 | - Logging可以通过log4j.properties配置 8 | 9 | ## Spark属性 10 | 11 | Spark属性控制大部分的应用程序设置,并且为每个应用程序分别配置它。这些属性可以直接在[SparkConf](http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.SparkConf)上配置,然后传递给`SparkContext`。`SparkConf` 12 | 允许你配置一些通用的属性(如master URL、应用程序明)以及通过`set()`方法设置的任意键值对。例如,我们可以用如下方式创建一个拥有两个线程的应用程序。注意,我们用`local[2]`运行,这意味着两个线程-表示最小的 13 | 并行度,它可以帮助我们检测当在分布式环境下运行的时才出现的错误。 14 | 15 | ```scala 16 | val conf = new SparkConf() 17 | .setMaster("local[2]") 18 | .setAppName("CountingSheep") 19 | .set("spark.executor.memory", "1g") 20 | val sc = new SparkContext(conf) 21 | ``` 22 | 23 | 注意,我们在本地模式中拥有超过1个线程。和Spark Streaming的情况一样,我们可能需要一个线程防止任何形式的饥饿问题。 24 | 25 | ### 动态加载Spark属性 26 | 27 | 在一些情况下,你可能想在`SparkConf`中避免硬编码确定的配置。例如,你想用不同的master或者不同的内存数运行相同的应用程序。Spark允许你简单地创建一个空conf。 28 | 29 | ```scala 30 | val sc = new SparkContext(new SparkConf()) 31 | ``` 32 | 然后你在运行时提供值。 33 | 34 | ```shell 35 | ./bin/spark-submit --name "My app" --master local[4] --conf spark.shuffle.spill=false 36 | --conf "spark.executor.extraJavaOptions=-XX:+PrintGCDetails -XX:+PrintGCTimeStamps" myApp.jar 37 | ``` 38 | 39 | Spark shell和`spark-submit`工具支持两种方式动态加载配置。第一种方式是命令行选项,例如`--master`,如上面shell显示的那样。`spark-submit`可以接受任何Spark属性,用`--conf` 40 | 标记表示。但是那些参与Spark应用程序启动的属性要用特定的标记表示。运行`./bin/spark-submit --help`将会显示选项的整个列表。 41 | 42 | `bin/spark-submit`也会从`conf/spark-defaults.conf`中读取配置选项,这个配置文件中,每一行都包含一对以空格分开的键和值。例如: 43 | 44 | ``` 45 | spark.master spark://5.6.7.8:7077 46 | spark.executor.memory 512m 47 | spark.eventLog.enabled true 48 | spark.serializer org.apache.spark.serializer.KryoSerializer 49 | ``` 50 | 51 | 任何标签(flags)指定的值或者在配置文件中的值将会传递给应用程序,并且通过`SparkConf`合并这些值。在`SparkConf`上设置的属性具有最高的优先级,其次是传递给`spark-submit` 52 | 或者`spark-shell`的属性值,最后是`spark-defaults.conf`文件中的属性值。 53 | 54 | ### 查看Spark属性 55 | 56 | 在`http://:4040`上的应用程序web UI在“Environment”标签中列出了所有的Spark属性。这对你确保设置的属性的正确性是很有用的。注意,只有通过spark-defaults.conf, SparkConf以及 57 | 命令行直接指定的值才会显示。对于其它的配置属性,你可以认为程序用到了默认的值。 58 | 59 | ### 可用的属性 60 | 61 | 控制内部设置的大部分属性都有合理的默认值,一些最通用的选项设置如下: 62 | 63 | #### 应用程序属性 64 | 65 | Property Name | Default | Meaning 66 | --- | --- | --- 67 | spark.app.name | (none) | 你的应用程序的名字。这将在UI和日志数据中出现 68 | spark.master | (none) | 集群管理器连接的地方 69 | spark.executor.memory | 512m | 每个executor进程使用的内存数。和JVM内存串拥有相同的格式(如512m,2g) 70 | spark.driver.memory | 512m | driver进程使用的内存数 71 | spark.driver.maxResultSize | 1g | 每个Spark action(如collect)所有分区的序列化结果的总大小限制。设置的值应该不小于1m,0代表没有限制。如果总大小超过这个限制,工作将会终止。大的限制值可能导致driver出现内存溢出错误(依赖于spark.driver.memory和JVM中对象的内存消耗)。设置合理的限制,可以避免出现内存溢出错误。 72 | spark.serializer | org.apache.spark.serializer.JavaSerializer | 序列化对象使用的类。默认的java序列化类可以序列化任何可序列化的java对象但是它很慢。所有我们建议用[org.apache.spark.serializer.KryoSerializer](http://spark.apache.org/docs/latest/tuning.html) 73 | spark.kryo.classesToRegister | (none) | 如果你用Kryo序列化,给定的用逗号分隔的自定义类名列表表示要注册的类 74 | spark.kryo.registrator | (none) | 如果你用Kryo序列化,设置这个类去注册你的自定义类。如果你需要用自定义的方式注册你的类,那么这个属性是有用的。否则`spark.kryo.classesToRegister`会更简单。它应该设置一个继承自[KryoRegistrator](http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.serializer.KryoRegistrator)的类 75 | spark.local.dir | /tmp | Spark中暂存空间的使用目录。在Spark1.0以及更高的版本中,这个属性被SPARK_LOCAL_DIRS(Standalone, Mesos)和LOCAL_DIRS(YARN)环境变量覆盖。 76 | spark.logConf | false | 当SparkContext启动时,将有效的SparkConf记录为INFO。 77 | 78 | #### 运行环境 79 | 80 | Property Name | Default | Meaning 81 | --- | --- | --- 82 | spark.executor.extraJavaOptions | (none) | 传递给executors的JVM选项字符串。例如GC设置或者其它日志设置。注意,在这个选项中设置Spark属性或者堆大小是不合法的。Spark属性需要用SparkConf对象或者`spark-submit`脚本用到的`spark-defaults.conf`文件设置。堆内存可以通过`spark.executor.memory`设置 83 | spark.executor.extraClassPath | (none) | 附加到executors的classpath的额外的classpath实体。这个设置存在的主要目的是Spark与旧版本的向后兼容问题。用户一般不用设置这个选项 84 | spark.executor.extraLibraryPath | (none) | 指定启动executor的JVM时用到的库路径 85 | spark.executor.logs.rolling.strategy | (none) | 设置executor日志的滚动(rolling)策略。默认情况下没有开启。可以配置为`time`(基于时间的滚动)和`size`(基于大小的滚动)。对于`time`,用`spark.executor.logs.rolling.time.interval`设置滚动间隔;对于`size`,用`spark.executor.logs.rolling.size.maxBytes`设置最大的滚动大小 86 | spark.executor.logs.rolling.time.interval | daily | executor日志滚动的时间间隔。默认情况下没有开启。合法的值是`daily`, `hourly`, `minutely`以及任意的秒。 87 | spark.executor.logs.rolling.size.maxBytes | (none) | executor日志的最大滚动大小。默认情况下没有开启。值设置为字节 88 | spark.executor.logs.rolling.maxRetainedFiles | (none) | 设置被系统保留的最近滚动日志文件的数量。更老的日志文件将被删除。默认没有开启。 89 | spark.files.userClassPathFirst | false | (实验性)当在Executors中加载类时,是否用户添加的jar比Spark自己的jar优先级高。这个属性可以降低Spark依赖和用户依赖的冲突。它现在还是一个实验性的特征。 90 | spark.python.worker.memory | 512m | 在聚合期间,每个python worker进程使用的内存数。在聚合期间,如果内存超过了这个限制,它将会将数据塞进磁盘中 91 | spark.python.profile | false | 在Python worker中开启profiling。通过`sc.show_profiles()`展示分析结果。或者在driver退出前展示分析结果。可以通过`sc.dump_profiles(path)`将结果dump到磁盘中。如果一些分析结果已经手动展示,那么在driver退出前,它们再不会自动展示 92 | spark.python.profile.dump | (none) | driver退出前保存分析结果的dump文件的目录。每个RDD都会分别dump一个文件。可以通过`ptats.Stats()`加载这些文件。如果指定了这个属性,分析结果不会自动展示 93 | spark.python.worker.reuse | true | 是否重用python worker。如果是,它将使用固定数量的Python workers,而不需要为每个任务fork()一个Python进程。如果有一个非常大的广播,这个设置将非常有用。因为,广播不需要为每个任务从JVM到Python worker传递一次 94 | spark.executorEnv.[EnvironmentVariableName] | (none) | 通过`EnvironmentVariableName`添加指定的环境变量到executor进程。用户可以指定多个`EnvironmentVariableName`,设置多个环境变量 95 | spark.mesos.executor.home | driver side SPARK_HOME | 设置安装在Mesos的executor上的Spark的目录。默认情况下,executors将使用driver的Spark本地(home)目录,这个目录对它们不可见。注意,如果没有通过` spark.executor.uri`指定Spark的二进制包,这个设置才起作用 96 | spark.mesos.executor.memoryOverhead | executor memory * 0.07, 最小384m | 这个值是`spark.executor.memory`的补充。它用来计算mesos任务的总内存。另外,有一个7%的硬编码设置。最后的值将选择`spark.mesos.executor.memoryOverhead`或者`spark.executor.memory`的7%二者之间的大者 97 | 98 | #### Shuffle行为(Behavior) 99 | 100 | Property Name | Default | Meaning 101 | --- | --- | --- 102 | spark.shuffle.consolidateFiles | false | 如果设置为"true",在shuffle期间,合并的中间文件将会被创建。创建更少的文件可以提供文件系统的shuffle的效率。这些shuffle都伴随着大量递归任务。当用ext4和dfs文件系统时,推荐设置为"true"。在ext3中,因为文件系统的限制,这个选项可能机器(大于8核)降低效率 103 | spark.shuffle.spill | true | 如果设置为"true",通过将多出的数据写入磁盘来限制内存数。通过`spark.shuffle.memoryFraction`来指定spilling的阈值 104 | spark.shuffle.spill.compress | true | 在shuffle时,是否将spilling的数据压缩。压缩算法通过`spark.io.compression.codec`指定。 105 | spark.shuffle.memoryFraction | 0.2 | 如果`spark.shuffle.spill`为“true”,shuffle中聚合和合并组操作使用的java堆内存占总内存的比重。在任何时候,shuffles使用的所有内存内maps的集合大小都受这个限制的约束。超过这个限制,spilling数据将会保存到磁盘上。如果spilling太过频繁,考虑增大这个值 106 | spark.shuffle.compress | true | 是否压缩map操作的输出文件。一般情况下,这是一个好的选择。 107 | spark.shuffle.file.buffer.kb | 32 | 每个shuffle文件输出流内存内缓存的大小,单位是kb。这个缓存减少了创建只中间shuffle文件中磁盘搜索和系统访问的数量 108 | spark.reducer.maxMbInFlight | 48 | 从递归任务中同时获取的map输出数据的最大大小(mb)。因为每一个输出都需要我们创建一个缓存用来接收,这个设置代表每个任务固定的内存上限,所以除非你有更大的内存,将其设置小一点 109 | spark.shuffle.manager | sort | 它的实现用于shuffle数据。有两种可用的实现:`sort`和`hash`。基于sort的shuffle有更高的内存使用率 110 | spark.shuffle.sort.bypassMergeThreshold | 200 | (Advanced) In the sort-based shuffle manager, avoid merge-sorting data if there is no map-side aggregation and there are at most this many reduce partitions 111 | spark.shuffle.blockTransferService | netty | 实现用来在executor直接传递shuffle和缓存块。有两种可用的实现:`netty`和`nio`。基于netty的块传递在具有相同的效率情况下更简单 112 | 113 | #### Spark UI 114 | 115 | Property Name | Default | Meaning 116 | --- | --- | --- 117 | spark.ui.port | 4040 | 你的应用程序dashboard的端口。显示内存和工作量数据 118 | spark.ui.retainedStages | 1000 | 在垃圾回收之前,Spark UI和状态API记住的stage数 119 | spark.ui.retainedJobs | 1000 | 在垃圾回收之前,Spark UI和状态API记住的job数 120 | spark.ui.killEnabled | true | 运行在web UI中杀死stage和相应的job 121 | spark.eventLog.enabled | false | 是否记录Spark的事件日志。这在应用程序完成后,重新构造web UI是有用的 122 | spark.eventLog.compress | false | 是否压缩事件日志。需要`spark.eventLog.enabled`为true 123 | spark.eventLog.dir | file:///tmp/spark-events | Spark事件日志记录的基本目录。在这个基本目录下,Spark为每个应用程序创建一个子目录。各个应用程序记录日志到直到的目录。用户可能想设置这为统一的地点,像HDFS一样,所以历史文件可以通过历史服务器读取 124 | 125 | #### 压缩和序列化 126 | 127 | Property Name | Default | Meaning 128 | --- | --- | --- 129 | spark.broadcast.compress | true | 在发送广播变量之前是否压缩它 130 | spark.rdd.compress | true | 是否压缩序列化的RDD分区。在花费一些额外的CPU时间的同时节省大量的空间 131 | spark.io.compression.codec | snappy | 压缩诸如RDD分区、广播变量、shuffle输出等内部数据的编码解码器。默认情况下,Spark提供了三种选择:lz4, lzf和snappy。你也可以用完整的类名来制定。`org.apache.spark.io.LZ4CompressionCodec`,`org.apache.spark.io.LZFCompressionCodec`,`org.apache.spark.io.SnappyCompressionCodec` 132 | spark.io.compression.snappy.block.size | 32768 | Snappy压缩中用到的块大小。降低这个块的大小也会降低shuffle内存使用率 133 | spark.io.compression.lz4.block.size | 32768 | LZ4压缩中用到的块大小。降低这个块的大小也会降低shuffle内存使用率 134 | spark.closure.serializer | org.apache.spark.serializer.JavaSerializer | 闭包用到的序列化类。目前只支持java序列化器 135 | spark.serializer.objectStreamReset | 100 | 当用`org.apache.spark.serializer.JavaSerializer`序列化时,序列化器通过缓存对象防止写多余的数据,然而这会造成这些对象的垃圾回收停止。通过请求'reset',你从序列化器中flush这些信息并允许收集老的数据。为了关闭这个周期性的reset,你可以将值设为-1。默认情况下,每一百个对象reset一次 136 | spark.kryo.referenceTracking | true | 当用Kryo序列化时,跟踪是否引用同一对象。如果你的对象图有环,这是必须的设置。如果他们包含相同对象的多个副本,这个设置对效率是有用的。如果你知道不在这两个场景,那么可以禁用它以提高效率 137 | spark.kryo.registrationRequired | false | 是否需要注册为Kyro可用。如果设置为true,然后如果一个没有注册的类序列化,Kyro会抛出异常。如果设置为false,Kryo将会同时写每个对象和其非注册类名。写类名可能造成显著地性能瓶颈。 138 | spark.kryoserializer.buffer.mb | 0.064 | Kyro序列化缓存的大小。这样worker上的每个核都有一个缓存。如果有需要,缓存会涨到`spark.kryoserializer.buffer.max.mb`设置的值那么大。 139 | spark.kryoserializer.buffer.max.mb | 64 | Kryo序列化缓存允许的最大值。这个值必须大于你尝试序列化的对象 140 | 141 | #### Networking 142 | 143 | Property Name | Default | Meaning 144 | --- | --- | --- 145 | spark.driver.host | (local hostname) | driver监听的主机名或者IP地址。这用于和executors以及独立的master通信 146 | spark.driver.port | (random) | driver监听的接口。这用于和executors以及独立的master通信 147 | spark.fileserver.port | (random) | driver的文件服务器监听的端口 148 | spark.broadcast.port | (random) | driver的HTTP广播服务器监听的端口 149 | spark.replClassServer.port | (random) | driver的HTTP类服务器监听的端口 150 | spark.blockManager.port | (random) | 块管理器监听的端口。这些同时存在于driver和executors 151 | spark.executor.port | (random) | executor监听的端口。用于与driver通信 152 | spark.port.maxRetries | 16 | 当绑定到一个端口,在放弃前重试的最大次数 153 | spark.akka.frameSize | 10 | 在"control plane"通信中允许的最大消息大小。如果你的任务需要发送大的结果到driver中,调大这个值 154 | spark.akka.threads | 4 | 通信的actor线程数。当driver有很多CPU核时,调大它是有用的 155 | spark.akka.timeout | 100 | Spark节点之间的通信超时。单位是s 156 | spark.akka.heartbeat.pauses | 6000 | This is set to a larger value to disable failure detector that comes inbuilt akka. It can be enabled again, if you plan to use this feature (Not recommended). Acceptable heart beat pause in seconds for akka. This can be used to control sensitivity to gc pauses. Tune this in combination of `spark.akka.heartbeat.interval` and `spark.akka.failure-detector.threshold` if you need to. 157 | spark.akka.failure-detector.threshold | 300.0 | This is set to a larger value to disable failure detector that comes inbuilt akka. It can be enabled again, if you plan to use this feature (Not recommended). This maps to akka's `akka.remote.transport-failure-detector.threshold`. Tune this in combination of `spark.akka.heartbeat.pauses` and `spark.akka.heartbeat.interval` if you need to. 158 | spark.akka.heartbeat.interval | 1000 | This is set to a larger value to disable failure detector that comes inbuilt akka. It can be enabled again, if you plan to use this feature (Not recommended). A larger interval value in seconds reduces network overhead and a smaller value ( ~ 1 s) might be more informative for akka's failure detector. Tune this in combination of `spark.akka.heartbeat.pauses` and `spark.akka.failure-detector.threshold` if you need to. Only positive use case for using failure detector can be, a sensistive failure detector can help evict rogue executors really quick. However this is usually not the case as gc pauses and network lags are expected in a real Spark cluster. Apart from that enabling this leads to a lot of exchanges of heart beats between nodes leading to flooding the network with those. 159 | 160 | #### Security 161 | 162 | Property Name | Default | Meaning 163 | --- | --- | --- 164 | spark.authenticate | false | 是否Spark验证其内部连接。如果不是运行在YARN上,请看`spark.authenticate.secret` 165 | spark.authenticate.secret | None | 设置Spark两个组件之间的密匙验证。如果不是运行在YARN上,但是需要验证,这个选项必须设置 166 | spark.core.connection.auth.wait.timeout | 30 | 连接时等待验证的实际。单位为秒 167 | spark.core.connection.ack.wait.timeout | 60 | 连接等待回答的时间。单位为秒。为了避免不希望的超时,你可以设置更大的值 168 | spark.ui.filters | None | 应用到Spark web UI的用于过滤类名的逗号分隔的列表。过滤器必须是标准的[javax servlet Filter](http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html)。通过设置java系统属性也可以指定每个过滤器的参数。`spark..params='param1=value1,param2=value2'`。例如`-Dspark.ui.filters=com.test.filter1`、`-Dspark.com.test.filter1.params='param1=foo,param2=testing'` 169 | spark.acls.enable | false | 是否开启Spark acls。如果开启了,它检查用户是否有权限去查看或修改job。 Note this requires the user to be known, so if the user comes across as null no checks are done。UI利用使用过滤器验证和设置用户 170 | spark.ui.view.acls | empty | 逗号分隔的用户列表,列表中的用户有查看(view)Spark web UI的权限。默认情况下,只有启动Spark job的用户有查看权限 171 | spark.modify.acls | empty | 逗号分隔的用户列表,列表中的用户有修改Spark job的权限。默认情况下,只有启动Spark job的用户有修改权限 172 | spark.admin.acls | empty | 逗号分隔的用户或者管理员列表,列表中的用户或管理员有查看和修改所有Spark job的权限。如果你运行在一个共享集群,有一组管理员或开发者帮助debug,这个选项有用 173 | 174 | #### Spark Streaming 175 | 176 | Property Name | Default | Meaning 177 | --- | --- | --- 178 | spark.streaming.blockInterval | 200 | 在这个时间间隔(ms)内,通过Spark Streaming receivers接收的数据在保存到Spark之前,chunk为数据块。推荐的最小值为50ms 179 | spark.streaming.receiver.maxRate | infinite | 每秒钟每个receiver将接收的数据的最大记录数。有效的情况下,每个流将消耗至少这个数目的记录。设置这个配置为0或者-1将会不作限制 180 | spark.streaming.receiver.writeAheadLogs.enable | false | Enable write ahead logs for receivers. All the input data received through receivers will be saved to write ahead logs that will allow it to be recovered after driver failures 181 | spark.streaming.unpersist | true | 强制通过Spark Streaming生成并持久化的RDD自动从Spark内存中非持久化。通过Spark Streaming接收的原始输入数据也将清除。设置这个属性为false允许流应用程序访问原始数据和持久化RDD,因为它们没有被自动清除。但是它会造成更高的内存花费 182 | 183 | ## 环境变量 184 | 185 | 通过环境变量配置确定的Spark设置。环境变量从Spark安装目录下的`conf/spark-env.sh`脚本读取(或者windows的`conf/spark-env.cmd`)。在独立的或者Mesos模式下,这个文件可以给机器 186 | 确定的信息,如主机名。当运行本地应用程序或者提交脚本时,它也起作用。 187 | 188 | 注意,当Spark安装时,`conf/spark-env.sh`默认是不存在的。你可以复制`conf/spark-env.sh.template`创建它。 189 | 190 | 可以在`spark-env.sh`中设置如下变量: 191 | 192 | Environment Variable | Meaning 193 | --- | --- 194 | JAVA_HOME | java安装的路径 195 | PYSPARK_PYTHON | PySpark用到的Python二进制执行文件路径 196 | SPARK_LOCAL_IP | 机器绑定的IP地址 197 | SPARK_PUBLIC_DNS | 你Spark应用程序通知给其他机器的主机名 198 | 199 | 除了以上这些,Spark [standalone cluster scripts](http://spark.apache.org/docs/latest/spark-standalone.html#cluster-launch-scripts)也可以设置一些选项。例如 200 | 每台机器使用的核数以及最大内存。 201 | 202 | 因为`spark-env.sh`是shell脚本,其中的一些可以以编程方式设置。例如,你可以通过特定的网络接口计算`SPARK_LOCAL_IP`。 203 | 204 | ## 配置Logging 205 | 206 | Spark用[log4j](http://logging.apache.org/log4j/) logging。你可以通过在conf目录下添加`log4j.properties`文件来配置。一种方法是复制`log4j.properties.template`文件。 207 | -------------------------------------------------------------------------------- /programming-guide/README.md: -------------------------------------------------------------------------------- 1 | # 概論 2 | 在結構中,每隻 Spark 應用程式都由一隻*驅動程式(driver program)*構成,驅動程序在集群上運行用户的 `main` 函數来執行各式各樣的*併行操作(parallel operations)*。Spark 的主要抽象是提供一個*彈性分布式資料庫(RDD)*,RDD 是指能横跨集群所有節點進行併行計算的分區元素集合。RDDs 從 Hadoop 的文件系统中的一個文件中產生而來(或其他 Hadoop 支持的文件系统),或者從一個已有的 Scala 集合轉換得到。用戶可以將 Spark RDD *持久化(persist)*到記憶體中,讓它在併行計算中有效率的被重複使用。而且,RDDs 能在節點失敗中自動恢復。 3 | 4 | Spark 的第二個抽象是*共享變數(shared variables)*,共享變數被運行在併行運算中。默認情況下,當 Spark 運行一個併行函數時,這個併行函數會作為一個任務集在不同的節點上運行,它會把函數裡使用到的每個變數都複製移動到每個任務中。有時,一個變數需被共享到交叉任務中或驅動程式和任務之間。Spark 支持 2 種類型的共享變數:*廣播變數(broadcast variables)*,使用在所有節點的記憶體中快取一個值;累加器(accumulators),只能執行“增加(added)”操作,例如:計數器(counters)和加總(sums)。 5 | 6 | 這個指南會在 Spark 支持的所有語言中展示它的每一個特性。簡單的操作 Spark 互動式 shell - `bin/spark-shell` 操作 Scala shell,或 `bin/pyspark` 啟動一個 Python shell。 7 | 8 | * [引入 Spark](linking-with-spark.md) 9 | * [初始化 Spark](initializing-spark.md) 10 | * [Spark RDDs](rdds/README.md) 11 | * [共享變數](shared-variables.md) 12 | * [從這裡開始](from-here.md) 13 | -------------------------------------------------------------------------------- /programming-guide/from-here.md: -------------------------------------------------------------------------------- 1 | # 從這裡開始 2 | 3 | 你可以從Spark 官網上尋找一些[Spark 執行範例](http://spark.apache.org/examples.html)。另外,Spark的example 目錄提供幾個Spark 例子,你可以利用下列方式執行Java 或是scala範例: 4 | ```shell 5 | ./bin/run-example SparkPi 6 | ``` 7 | 為了優化你的專案, [configuration](https://spark.apache.org/docs/latest/configuration.html)和[tuning](https://spark.apache.org/docs/latest/tuning.html)說明提供許多實用的資訊。因為,確認你RDDs 中的資料是有效格式是非常重要的事情。此外,為了提昇部署效能,[ 集群模式介紹] (https://spark.apache.org/docs/latest/cluster-overview.html) 介紹了許多分布式操作的集群管理說明。 8 | 9 | 最後,完整的API 文件可以從以下網址連結[scala](https://spark.apache.org/docs/latest/api/scala/#org.apache.spark.package), [java](https://spark.apache.org/docs/latest/api/java/), [python](https://spark.apache.org/docs/latest/api/python/) 中查詢。 10 | -------------------------------------------------------------------------------- /programming-guide/initializing-spark.md: -------------------------------------------------------------------------------- 1 | # 初始化 Spark 2 | 3 | 使用 Spark 的第一步是創建[SparkContext](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.SparkContext) 物件,讓 Spark 知道如何找到集群。而建立 `SparkContext` 之前,還需建立 [SparkConf](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.SparkConf) 物件,而這個物件則包含你的應用程式資訊。 4 | 5 | ```scala 6 | val conf = new SparkConf().setAppName(appName).setMaster(master) 7 | new SparkContext(conf) 8 | ``` 9 | 10 | `appName`參數是你自定的程式名稱,會顯示在 cluster UI 上。`master` 是[Spark, Mesos 或 YARN 集群的 URL](https://spark.apache.org/docs/latest/submitting-applications.html#master-urls),或是運行本地模式(standalone) 時可使用 “local”。實際上,當你的程式在集群上運作時,你不需要把 `master` 放在程式中,因此可以[用 spark-submit 啟動你的應用程式](https://spark.apache.org/docs/latest/submitting-applications.html)。當然,你也可以在 Spark 程式中使用 “local” 做本地測試或是單元測試。 11 | 12 | 13 | ## 使用 Shell 14 | 15 | 在 Spark shell 裡,有一個內建的 SparkContext,名為 `sc`。你可以用 `--master` 設定 SparkContext 欲連結的集群,用 `--jars`來指定需要加到 classpath 中的 JAR 包,倘若有多個 JAR,可使用**逗號**分隔符號來連結他們。例如:想在一個擁有 4 個CPU 的環境上執行 `bin/spark-shell`: 16 | 17 | ``` 18 | $ ./bin/spark-shell --master local[4] 19 | ``` 20 | 21 | 或是想在 classpath 中增加 `code.jar`,你可以這樣寫: 22 | 23 | ``` 24 | $ ./bin/spark-shell --master local[4] --jars code.jar 25 | ``` 26 | 27 | 此外,你可以執行 `spark-shell --help` 得到完整的參數列表。目前,使用 `spark-submit` 會比 [spark-shell ](https://spark.apache.org/docs/latest/submitting-applications.html)普遍。 28 | -------------------------------------------------------------------------------- /programming-guide/linking-with-spark.md: -------------------------------------------------------------------------------- 1 | # 引入 Spark 2 | 3 | Spark 1.2.0 使用 Scala 2.10 撰寫應用程式,因此你的Scala 版本必須相容(例如:2.10.X)。 4 | 5 | 撰寫 Spark 應用程式時,你需要添加 Spark 的 Maven 依賴,Spark 可以透過 Maven 中心庫來取得: 6 | 7 | ``` 8 | groupId = org.apache.spark 9 | artifactId = spark-core_2.10 10 | version = 1.2.0 11 | ``` 12 | 13 | 如果你希望連結 HDFS 集群,需要根據你的 HDFS 版本設定 `hadoop-client`的相依性。你可以透過[第三方發行頁面](https://spark.apache.org/docs/latest/hadoop-third-party-distributions.html)找到相對應的版本 14 | 15 | ``` 16 | groupId = org.apache.hadoop 17 | artifactId = hadoop-client 18 | version = 19 | ``` 20 | 21 | 最後,你需要匯入一些 Spark 的類別(class) 和隱式轉換 (implicit conversions) 到你的程式,增加下面幾行即可: 22 | 23 | ```scala 24 | import org.apache.spark.SparkContext 25 | import org.apache.spark.SparkContext._ 26 | import org.apache.spark.SparkConf 27 | ``` 28 | -------------------------------------------------------------------------------- /programming-guide/rdds/README.md: -------------------------------------------------------------------------------- 1 | # 彈性分布式資料集 (RDDs) 2 | 3 | Spark 核心概念是 _Resilient Distributed Dataset (RDD)_ ,你可以將它視為一個可以併型操作、有容錯機制的資料集和。目前有 2 種方式可以建立 RDDs:第一種是在你執行的驅動程式中併行化一個已經存在集合;另外一個方式是引用外部儲存系統的資料集,例如共享文件系統,HDFS,HBase或其他 Hadoop 資料格式的資料來源。 4 | 5 | * [併行集合](parallelized-collections.md) 6 | * [外部資料集](external-datasets.md) 7 | * [RDD 操作](rdd-operations.md) 8 | * [傳送函數到 Spark](passing-functions-to-spark.md) 9 | * [使用鍵值對](working-with-key-value-pairs.md) 10 | * [Transformations](transformations.md) 11 | * [Actions](actions.md) 12 | * [RDD 持久化](rdd_persistence.md) 13 | -------------------------------------------------------------------------------- /programming-guide/rdds/actions.md: -------------------------------------------------------------------------------- 1 | # Actions 2 | 3 | 下面的表格列出 Sparkk 支援且常用的 actions。細節請參考 RDD API 手冊([Scala](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.RDD), [Java](https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/api/java/JavaRDD.html), [Python](https://spark.apache.org/docs/latest/api/python/pyspark.rdd.RDD-class.html)) 和 PairRDDFunctions 手冊([Scala](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.PairRDDFunctions), [Java](https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/api/java/JavaPairRDD.html))。 4 | 5 | Action | Meaning 6 | --- | --- 7 | reduce(func) | Aggregate the elements of the dataset using a function func (which takes two arguments and returns one). The function should be commutative and associative so that it can be computed correctly in parallel. 8 | collect() | Return all the elements of the dataset as an array at the driver program. This is usually useful after a filter or other operation that returns a sufficiently small subset of the data. 9 | count() | Return the number of elements in the dataset. 10 | first() | Return the first element of the dataset (similar to take(1)). 11 | take(n) | Return an array with the first n elements of the dataset. Note that this is currently not executed in parallel. Instead, the driver program computes all the elements. 12 | takeSample(withReplacement, num, [seed]) | Return an array with a random sample of num elements of the dataset, with or without replacement, optionally pre-specifying a random number generator seed. 13 | takeOrdered(n, [ordering]) | Return the first n elements of the RDD using either their natural order or a custom comparator. 14 | saveAsTextFile(path) | Write the elements of the dataset as a text file (or set of text files) in a given directory in the local filesystem, HDFS or any other Hadoop-supported file system. Spark will call toString on each element to convert it to a line of text in the file. 15 | saveAsSequenceFile(path) (Java and Scala) | Write the elements of the dataset as a Hadoop SequenceFile in a given path in the local filesystem, HDFS or any other Hadoop-supported file system. This is available on RDDs of key-value pairs that either implement Hadoop's Writable interface. In Scala, it is also available on types that are implicitly convertible to Writable (Spark includes conversions for basic types like Int, Double, String, etc). 16 | saveAsObjectFile(path) (Java and Scala) | Write the elements of the dataset in a simple format using Java serialization, which can then be loaded using SparkContext.objectFile(). 17 | countByKey() | Only available on RDDs of type (K, V). Returns a hashmap of (K, Int) pairs with the count of each key. 18 | foreach(func) | Run a function func on each element of the dataset. This is usually done for side effects such as updating an accumulator variable (see below) or interacting with external storage systems. 19 | -------------------------------------------------------------------------------- /programming-guide/rdds/external-datasets.md: -------------------------------------------------------------------------------- 1 | ## 外部資料集 2 | 3 | Spark 支援任何一個 Hadoop 的文件系統建立分布式資料集,例如,HDFS,Cassandra,HBase,[Amazon S3](http://wiki.apache.org/hadoop/AmazonS3)等。此外, Spark 也支援文字文件(text files),[SequenceFiles](http://hadoop.apache.org/docs/current/api/org/apache/hadoop/mapred/SequenceFileInputFormat.html) 和其他 Hadoop [InputFormat](http://hadoop.apache.org/docs/stable/api/org/apache/hadoop/mapred/InputFormat.html)。 4 | 5 | 文字文件 RDDs 可以由 SparkContext 的 `textFile` 函數建立。只需要在這函數中標示文件的 URI (機器上的本地路徑或是 `hdfs://`,`s3n://` 等), Spark 會將文件讀取寫入成一個集合。以下是一個範例: 6 | 7 | ```scala 8 | scala> val distFile = sc.textFile("data.txt") 9 | distFile: RDD[String] = MappedRDD@1d4cee08 10 | ``` 11 | 12 | 當上述步驟建立完成後,`distFiile` 就可以針對資料集做操作。例如,使用下面的方法使用 `map` 和 `reduce` 加總所有行的長度:`distFile.map(s => s.length).reduce((a, b) => a + b)`。 13 | 14 | 注意,Spark 讀取資料時: 15 | 16 | - 如果使用本地文件系統路徑,文件必須能在 work 節點上同一個路徑中找到,所以你需要複製文件到所有的 workers,或者使用網路的方法分享文件。 17 | - 所有 Spark 內關於連結文件的方法,除了 `textFile`,還有針對壓縮過的檔案,例如 `textFile("/my/文件目錄")`,`textFile("/my/文件目錄/*.txt")` 和`textFile("/my/文件目錄/*.gz")`。 18 | - `textFile` 函數中,有第二個選擇參數來設定切片(_slices_) 的數目。預設情況下,Spark 為每一塊文件(HDFS 預設文件大小是 64M) 建立一個切片(_slice_)。但是你也可以修改成一個更大的值来設定一個更高的切片數目。值得注意,你不能設定一個小於文件數目的切片值。 19 | 20 | 除了文本文件,Spark 的 Scala API 支持其他幾種資料格式: 21 | 22 | - `SparkContext.wholeTextFiles` 可以讀取一個包含多個檔案的文件目錄,並且返回每一個(filename, content)。和 `textFile` 的差異是:它記錄的是每一個檔案中的每一行。 23 | - 關於 [SequenceFiles](http://hadoop.apache.org/docs/current/api/org/apache/hadoop/mapred/SequenceFileInputFormat.html),你可以使用 SparkContext 的 `sequenceFile[K, V]` 方法產生,K 和 V 分別就是 key 和 values 。像 [IntWritable](http://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/IntWritable.html) 與 [Text](http://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/Text.html),他們必須是Hadoop 的[Writable](http://hadoop.apache.org/docs/current/api/org/apache/hadoop/io/Writable.html) 的子類。另外對於幾種通用的 Writables,Spark 允許你指定原本類型來替代。例如: `sequenceFile[Int, String]` 會自動讀取 IntWritables 和 Text。 24 | 25 | - 至於其他的 Hadoop InputFormats,可以使用 `SparkContext.hadoopRDD` 方法,它可以指定任意的 `JobConf`,輸入格式(InputFormat),key 類型,values 類型。你可以跟設定 Hadoop job 一樣的方法設定輸入來源。你還可以在新的的 MapReduce 接口(org.apache.hadoop.mapreduce) 基礎上使用 `SparkContext.newAPIHadoopRDD` (譯者提醒:原生的接口是 `SparkContext.newHadoopRDD`)。 26 | - `RDD.saveAsObjectFile` 和 `SparkContext.objectFile` 可以保存一個RDD,保存格式是一個簡單的 Java 物件序列化格式。這是一種效率不高的特有格式,如 Avro,它提供簡單的方法來保存任何一個 RDD。 27 | -------------------------------------------------------------------------------- /programming-guide/rdds/parallelized-collections.md: -------------------------------------------------------------------------------- 1 | ## 平行集合 2 | 3 | 建立平行集合 (_Parallelized collections_) 的方法是利用一個已存在的序列(Scala `Seq`)上使用 SparkContext 的 `parallelize`。序列中的元素會被複製到一個併行操作的分布式資料集合裡。例如:如何建立一個包含數字1 到5 的併行集合: 4 | 5 | ```scala 6 | val data = Array(1, 2, 3, 4, 5) 7 | val distData = sc.parallelize(data) 8 | ``` 9 | 10 | 當平行集合建立完成,這樣的分布式資料集(`distData`) 就可以被操作。例如,可以使用 `distData.reduce((a, b) => a + b)` 將裡面的元素數字加總。後續會再對這部份進行操作上的介紹。 11 | 12 | 值得注意,在併行集合裡有一個重要的概念,就是切片數(_slices_),即一個資料集被切的份數。Spark 會在集群上指派每一個切片執行任務。你也可以在集群上替每個 CPU 設定 2-4 個切片(slices)。一般情況下, Spark 會常識基於你的集群狀態自動設定切片的數量。當然,你也可以利用 `parallelize` 參數的第二個位置做設定,例如`sc.parallelize(data, 10)`。 13 | 14 | -------------------------------------------------------------------------------- /programming-guide/rdds/passing-functions-to-spark.md: -------------------------------------------------------------------------------- 1 | # 傳遞函數到 Spark 2 | 3 | Spark 的 API 大多數是依靠在驅動程式裡傳遞函數到集群上運作,目前有兩種推薦方式: 4 | 5 | - [ 匿名函數 (Anonymous function syntax)](http://docs.scala-lang.org/tutorials/tour/anonymous-function-syntax.html),可在較短的程式碼中使用。 6 | - 全局單例物件裡的靜態方法。例如,定義 `object MyFunctions` 然後傳遞 `MyFounctions.func1`,例如: 7 | 8 | ```scala 9 | object MyFunctions { 10 | def func1(s: String): String = { ... } 11 | } 12 | 13 | myRdd.map(MyFunctions.func1) 14 | ``` 15 | 16 | 注意,它可能傳遞的是一個類別實例裡面的一個方法(非一個單例物件),在這裡,必須傳送包含方法的整個物件。例如: 17 | 18 | ```scala 19 | class MyClass { 20 | def func1(s: String): String = { ... } 21 | def doStuff(rdd: RDD[String]): RDD[String] = { rdd.map(func1) } 22 | } 23 | ``` 24 | 25 | 如果我們建立一個 `new MyClass` 物件,並且使用它的 `doStuff`,`map` 裡面引用了這個 `MyClass` 中的 `func1` 方法,所以這個物件必須也傳送到集群上。類似寫成 `rdd.map(x => this.func1(x))`。 26 | 27 | 以同樣的方式,存取外部物件的變數將會引用整個物件: 28 | 29 | ```scala 30 | class MyClass { 31 | val field = "Hello" 32 | def doStuff(rdd: RDD[String]): RDD[String] = { rdd.map(x => field + x) } 33 | } 34 | ``` 35 | 36 | 等於寫成 `rdd.map(x => this.field + x)`,引用整個 `this` 物件。為了避免這個問題,最簡單的方式是複製 `field` 到一個本地變數而不是從外部取用: 37 | 38 | ```scala 39 | def doStuff(rdd: RDD[String]): RDD[String] = { 40 | val field_ = this.field 41 | rdd.map(x => field_ + x) 42 | } 43 | ``` 44 | -------------------------------------------------------------------------------- /programming-guide/rdds/rdd-operations.md: -------------------------------------------------------------------------------- 1 | # RDD 操作 2 | 3 | RDDs 支持兩種類型的操作:_轉換(transformations)_ 從已經存在的資料集裡面建立一個新的資料集:_動作(actions)_ 在資料集上做運算之後返回一個值到驅動程式。例如,`map` 是一個轉換操作,它會將每一個資料集內的元素傳遞給函數並且返回新的 RDD。另一方面,`reduce` 是一個動作,它會使用相同的函數來對 RDD 內所有的元素做整合,並且將最後的結果送回驅動程式(不過也有一個函數 `reduceByKey` 功能是返回一個分布式資料集)。 4 | 5 | 在 Spark 中,所有的转换(transformations)都是惰性(lazy)的,它们不会马上计算它们的结果。相反的,它们仅仅记录转换操作是应用到哪些基础数据集(例如一个文件)上的。转换仅仅在这个时候计算:当动作(action) 需要一个结果返回给驱动程序的时候。这个设计能够让 Spark 运行得更加高效。例如,我们可以实现:通过 `map` 创建一个新数据集在 `reduce` 中使用,并且仅仅返回 `reduce` 的结果给 driver,而不是整个大的映射过的数据集。 6 | 7 | 預設情況,每一個轉換過後的 RDD 會在每次執行動作(action) 的時候重新計算。當然,你也可以使用 `persist` (或 `cache`) 方式做持久化(`persist`) 一個 RDD 到緩存裡。在這情況之下, Spark 會在集群上保存相關的訊息,在你下次查詢時,縮短運行時間,同時也支援 RDD 持久化到硬碟,或是在其他節點上做複製動作。 8 | 9 | ## 基礎 10 | 11 | 為了理解 RDD 基本知識,可以先了解下面的簡單程式: 12 | 13 | ```scala 14 | val lines = sc.textFile("data.txt") 15 | val lineLengths = lines.map(s => s.length) 16 | val totalLength = lineLengths.reduce((a, b) => a + b) 17 | ``` 18 | 19 | 第一行是定義外部文件的 RDD。這個資料集並沒有收到緩存或做其他的操作:`lines` 單單只是一個指向文件位置的動作。第二行是定義 `lineLengths`,它是 `map` 轉換(transformation) 的結果。同樣,因為 Spark 的 lazy 模式,`lineLengths` _不會_被立刻計算。最後,當我們執行 `reduce`,因為這是一個動作(action), Spark 會把計算分成多個任務(task),並且讓他們運作在多個機器上。每台機器都會執行自己的 map 或是本地 reduce ,最後將結果送回給驅動程式。 20 | 21 | 如果想再次使用 `lineLengths`,我們可以這樣做: 22 | 23 | ```scala 24 | lineLengths.persist() 25 | ``` 26 | 27 | 在 `reduce` 之前,它會把 `lineLengths` 在第一次運算結束後將內容保存到緩存中,以利後續再次使用。 28 | -------------------------------------------------------------------------------- /programming-guide/rdds/rdd-persistences.md: -------------------------------------------------------------------------------- 1 | # RDD 永續儲存 2 | 3 | Spark 最重要的一個功能是它可以通過各種操作(operations)永續儲存(或者緩存)一個集合到記憶體中。當你想儲存一個RDD 的時候,每一個節點都會參與計算的所有分區資料儲存到記憶體中,而且這些資料可以被這個集合(以及這個集合衍生出的其他集合)的動作( action )來重複使用。這樣的設計會使後續的動作速度加快(通常快10 倍以上)。對迭代算法與快速的交互操作來說,記憶體是一個關鍵點。 4 | 5 | 你可以用`persist()`或`cache()` 方法來儲存一個RDD。首先,在action 中運算後取得RDD;接著,將它保存在每個節點的記憶體裡。Spark 的緩存是一個容錯的機制-如果RDD 的任何一個分區內不見,它可以透過原本的(transformations )操作,自動重複計算並且建立到這個分區內補足。 6 | 7 | 此外,可以利用不同的儲存機制來儲存每一個被永續化的 RDD 。例如,Spark 允許使用者將RDD 儲存在硬碟上、將集合序列化的 Java 物件永續儲存到記憶體、在節點之間複製或是儲存到[Tachyon](http://tachyon-project.org/)中。我們可以傳遞`StorageLevel` 物件給`persist()`方法來設定這些選項。`cache()` 方法則是預設儲存位置為—`StorageLevel.MEMORY_ONLY`。完整的設定說明如下: 8 | 9 | Storage Level | Meaning 10 | --- | --- 11 | MEMORY_ONLY | 把RDD 作為非序列化的Java 物件儲存在在jvm中。如果RDD 不適合放在記憶體,一些分區將不會被儲存在記憶體內,而是在每次需要在分區內時重新計算。這是系統預設的儲存方式。 12 | MEMORY_AND_DISK | 將RDD 作為非序列化的Java 物件儲存在jvm中。如果RDD 不適合放在記憶體,將這些分區儲存在硬碟,需要再取出。 13 | MEMORY_ONLY_SER | 將RDD 作為序列化的Java 對象儲存(每個分區一個byte 數組)。這種方式比非序列化方式更加節省空間,特別是用快速序列化方式,只是更耗費cpu 資源—密集的讀取操作。 14 | MEMORY_AND_DISK_SER | 和MEMORY_ONLY_SER类似,但不是在每次需要时重复计算这些不适合存储到内存中的分区,而是将这些分区存储到磁盘中。 15 | DISK_ONLY | 僅僅將RDD 分區儲存在硬碟內 16 | MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. | 和上面的儲存方式類似,但是複製每個分區到集群的兩個節點上面 17 | OFF_HEAP (experimental) | 以序列化的格式儲存RDD 到[Tachyon](http://tachyon-project.org/)中。相較於MEMORY_ONLY_SER,OFF_HEAP 減少回收垃圾的耗損,允許更小的執行者分享記憶體。這使得在擁有大量記憶體的環境下或者多開發空間的環境中更具威力。 18 | 19 | NOTE:在python中,儲存的物件都是通過Pickle 做序列化,所以是否選擇序列化並不重要。 20 | 21 | Spark 也會自動永續儲存一些shuffle 操作(如`reduceByKey`)的中間資料,即使用戶沒有使用`persist` 方法。好處是避免在shuffle 發生錯誤情況下,需要重新計算整個輸入。如果使用者計畫重算過程中的RDD ,建議使用`persist`。 22 | 23 | ## 如何選擇儲存方式 24 | 25 | Spark 提供多種儲存方式意味在記憶體利用率和 cpu 利用率之間的平衡。我們推薦透過下列的步驟選擇一個合適的儲存方式: 26 | 27 | - 如果你的RDD 適合預設的儲存方式(MEMORY_ONLY),就使用預設方式。因為這是cpu 利用率最好的的選擇,RDD 上的操作會比較快。 28 | 29 | - 如果不適用系統預設的方式,選擇MEMORY_ONLY_SER。這是一個更快的序列化物件的空間使用率,速度也不錯。 30 | 31 | - 除非計算 RDD 耗損資源多,或是資料量過於龐大,不要將RDD 儲存在硬碟上,否則,重新計算一個分區就會和讀取硬碟資料一樣慢。 32 | 33 | - 如果希望錯誤恢復速度加快,可以利用重複(replicated) 儲存方式。所有的儲存方式都可以通過重複計算遺失的資料來支援容錯機制。 34 | 35 | - 在擁有大量記憶體的環境或者多應用程式的環境,OFF_HEAP 擁有下列優勢: 36 | - 它運行多個執行者共享Tachyon 中相同的記憶體池 ( memory pool) 37 | - 它明顯減少收垃圾的花費 38 | - 如果單個執行者毀損,記憶體的數據不會遺失 39 | 40 | ## 刪除資料 41 | 42 | Spark 自動監控每個節點記憶體使用情況,利用最近最少使用原則來刪除老舊資料。如果你想手動刪除 RDD ,可以用`RDD.unpersist()` 43 | 44 | 45 | -------------------------------------------------------------------------------- /programming-guide/rdds/rdd_persistence.md: -------------------------------------------------------------------------------- 1 | # RDD 永續儲存 2 | 3 | Spark 有一個最重要的功能是在記憶體中_永續儲存_ (或 _緩存_) 一個資料集。 4 | -------------------------------------------------------------------------------- /programming-guide/rdds/transformations.md: -------------------------------------------------------------------------------- 1 | # Transformations 2 | 3 | 下面的表格列了 Sparkk 支援且常用 transformations。細節請參考 RDD API 手冊([Scala](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.RDD), [Java](https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/api/java/JavaRDD.html), [Python](https://spark.apache.org/docs/latest/api/python/pyspark.rdd.RDD-class.html)) 和 PairRDDFunctions 文档([Scala](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.rdd.PairRDDFunctions), [Java](https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/api/java/JavaPairRDD.html))。 4 | 5 | Transformation | Meaning 6 | --- | --- 7 | map(_func_) | 回傳一個新的分布式資料集,將資料來源的每一個元素傳遞給函數 _func_ 映射組成。 8 | filter(_func_) | 回傳一個新的資料集,從資料來源中篩選元素通過函數 _func_ 回傳 true。 9 | flatMap(_func_) | 類似 map,但是每個輸入元素能被映射成多個輸出選項(所以 _func_ 必須回傳一個 Seq,而非單一 item)。 10 | mapPartitions(_func_) | 類似 map,但分別運作在 RDD 的每個分區上,火以 _func_ 的類型必須為 `Iterator => Iterator` 當執行在類型是 T 的 RDD 上。 11 | mapPartitionsWithIndex(_func_) | 類似 mapPartitions,但 _func_ 需要提供一個 integer 值描述索引(index),所以 _func_ 的類型必須是 (Int, Iterator) => Iterator 當執行在類型為 T 的 RDD 上。 12 | sample(withReplacement, fraction, seed) | 對資料做抽樣。 13 | union(otherDataset) | Return a new dataset that contains the union of the elements in the source dataset and the argument. 14 | intersection(otherDataset) | Return a new RDD that contains the intersection of elements in the source dataset and the argument. 15 | distinct([numTasks])) | Return a new dataset that contains the distinct elements of the source dataset. 16 | groupByKey([numTasks]) | When called on a dataset of (K, V) pairs, returns a dataset of (K, Iterable) pairs. Note: If you are grouping in order to perform an aggregation (such as a sum or average) over each key, using reduceByKey or combineByKey will yield much better performance. Note: By default, the level of parallelism in the output depends on the number of partitions of the parent RDD. You can pass an optional numTasks argument to set a different number of tasks. 17 | reduceByKey(func, [numTasks]) | When called on a dataset of (K, V) pairs, returns a dataset of (K, V) pairs where the values for each key are aggregated using the given reduce function func, which must be of type (V,V) => V. Like in groupByKey, the number of reduce tasks is configurable through an optional second argument. 18 | aggregateByKey(zeroValue)(seqOp, combOp, [numTasks]) | When called on a dataset of (K, V) pairs, returns a dataset of (K, U) pairs where the values for each key are aggregated using the given combine functions and a neutral "zero" value. Allows an aggregated value type that is different than the input value type, while avoiding unnecessary allocations. Like in groupByKey, the number of reduce tasks is configurable through an optional second argument. 19 | sortByKey([ascending], [numTasks]) | When called on a dataset of (K, V) pairs where K implements Ordered, returns a dataset of (K, V) pairs sorted by keys in ascending or descending order, as specified in the boolean ascending argument. 20 | join(otherDataset, [numTasks]) | When called on datasets of type (K, V) and (K, W), returns a dataset of (K, (V, W)) pairs with all pairs of elements for each key. Outer joins are also supported through leftOuterJoin and rightOuterJoin. 21 | cogroup(otherDataset, [numTasks]) | When called on datasets of type (K, V) and (K, W), returns a dataset of (K, Iterable, Iterable) tuples. This operation is also called groupWith. 22 | cartesian(otherDataset) | When called on datasets of types T and U, returns a dataset of (T, U) pairs (all pairs of elements). 23 | pipe(command, [envVars]) | Pipe each partition of the RDD through a shell command, e.g. a Perl or bash script. RDD elements are written to the process's stdin and lines output to its stdout are returned as an RDD of strings. 24 | coalesce(numPartitions) | Decrease the number of partitions in the RDD to numPartitions. Useful for running operations more efficiently after filtering down a large dataset. 25 | repartition(numPartitions) | Reshuffle the data in the RDD randomly to create either more or fewer partitions and balance it across them. This always shuffles all data over the network. 26 | -------------------------------------------------------------------------------- /programming-guide/rdds/working-with-key-value-pairs.md: -------------------------------------------------------------------------------- 1 | # 使用鍵值對 2 | 3 | 即使很多 Spark 的操作視在任意類型物件的 RDDs 上,仍有少數幾個特殊操作僅在鍵值(key-value) 對 RDDs 上使用。最常見的是分布式 "shuffle" 操作,例如根據一個 key 對一組資料進行分組跟整合。 4 | 5 | 在 Scala 中,這些操作包含[二元组(Tuple2)](http://www.scala-lang.org/api/2.10.4/index.html#scala.Tuple2)(在語言的內建元祖中,簡單的寫 (a, b) 即可建立) 的 RDD 上自動變成可用元素,只要在你的程式中匯入 `org.apache.spark.SparkContext._` 來啟動 Spark 的隱式轉換(implicit conversions)。在 PairRDDFunctions 的類別鍵值對操作中可以使用,如果你匯入隱式轉換,它會自動包成元組 (tuple) RDD。 6 | 7 | 舉例,下面的程式在鍵值對上使用 `reduceByKey` 來統計一個文件裡面每一行內容出現的次數: 8 | 9 | ```scala 10 | val lines = sc.textFile("data.txt") 11 | val pairs = lines.map(s => (s, 1)) 12 | val counts = pairs.reduceByKey((a, b) => a + b) 13 | ``` 14 | 15 | 也可以使用 `counts.sortByKey()`,例如,將鍵值對按照字母做排序,最後用 `counts.collect()` 輸出成結果陣列送回驅動程式。 16 | 17 | 注意:使用一個自行定義物件作為 key 在使用鍵值對操作的時候,需要確保定義的`equals()` 方法和 `hashCode()` 方法是匹配的。細節請看 [Object.hashCode() 文件](http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#hashCode()) 內的描述。 18 | -------------------------------------------------------------------------------- /programming-guide/shared-variables.md: -------------------------------------------------------------------------------- 1 | # 共享變數 2 | 3 | 一般情況,當傳遞一個操作函數( 例如 map 或 reduce) 給 Spark時,Spark 實際上是操作這個函數變數的副本。這些變數被複製到每台機器上,而且這些變數在遠端機器上的更新都不會傳送回驅動程式。一般來說,跨任務的讀寫操作變數效率不高,但 Spark 還是為了兩種常用模式提供共享變數:廣播變數 ( broadcast variable) 與累加器 ( accumulator) 4 | 5 | ## 廣播變數 6 | 7 | 廣播變數允許程式將一個可讀變數存在每台機器的記憶體裡,而不是每個任務都存有一份副本。例如,利用廣播變數,我們能將一個大資料量輸入的集合副本發送到每個節點上。( Broadcast variables allow the programmer to keep a read-only variable cached on each machine rather than shipping a copy of it with tasks.They can be used, for example, 8 | to give every node a copy of a large input dataset in an efficient manner.) Spark 也會試著利用有效率的廣播算法來分配廣播變數,以減少傳遞之間的資源成本。 9 | 10 | 一個廣播變數可以利用`SparkContext.broadcast(v)` 方法從一個初始化變數v 中產生。廣播變數是v的一個包裝變數,它的值可以透過`value` 方法取得,可參考下列程式: 11 | 12 | ```scala 13 | scala> val broadcastVar = sc.broadcast(Array(1, 2, 3)) 14 | broadcastVar: spark.Broadcast[Array[Int]] = spark.Broadcast(b5c40191-a864-4c7d-b9bf-d87e1a4e787c) 15 | scala> broadcastVar.value 16 | res0: Array[Int] = Array(1, 2, 3) 17 | ``` 18 | 广播变量创建以后,我们就能够在集群的任何函数中使用它来代替变量v,这样我们就不需要再次传递变量v到每个节点上。另外,为了保证所有的节点得到广播变量具有相同的值,对象v不能在广播之后被修改。 19 | 20 | 廣播變數建好以後,就能夠在集群的任何函數中使用它,來代替變數v,這樣一來,就不用再次傳送變數v 到其他節點上。此外,為了保障所有節點得到的廣播變數值相同,廣播之後就不能對物件v再做修改。 21 | 22 | ## 累加器 23 | 24 | 顧名思義,累加器是一種只能利用關連操作做“加” 操作的變數,因此他能夠快速的執行併行操作。而且他們能夠操作`counters`和`sums`。Spark 原本支援數值類型的累加器,開發人員可以自行增加可被支援的類型。 25 | 26 | 如果建立一個具名的累加器,它可在 Spark UI 上顯示。這對理解運作階段 ( running stages ) 的過程很有幫助。( 注意:python 中已支援 [AccumulatorPython](http://spark.apache.org/docs/1.0.2/api/python/)) 27 | 28 | 一个累加器可以通过调用`SparkContext.accumulator(v)`方法从一个初始变量v中创建。运行在集群上的任务可以通过`add`方法或者使用`+=`操作来给它加值。然而,它们无法读取这个值。只有驱动程序可以使用`value`方法来读取累加器的值。 29 | 如下的代码,展示了如何利用累加器将一个数组里面的所有元素相加: 30 | 31 | ```scala 32 | scala> val accum = sc.accumulator(0, "My Accumulator") 33 | accum: spark.Accumulator[Int] = 0 34 | scala> sc.parallelize(Array(1, 2, 3, 4)).foreach(x => accum += x) 35 | ... 36 | 10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s 37 | scala> accum.value 38 | res2: Int = 10 39 | ``` 40 | 41 | ```python 42 | >>> accum = sc.accumulator(0) 43 | Accumulator 44 | >>> sc.parallelize([1, 2, 3, 4]).foreach(lambda x: accum.add(x)) 45 | 10/09/29 18:41:08 INFO SparkContext: Tasks finished in 0.317106 s 46 | scala> accum.value 47 | 10 48 | ``` 49 | 50 | 這個例子利用內建的整數類型累加器,開發者還可以利用[AccumulatorParam](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.AccumulatorParam) 建立自己的累加器。 51 | 52 | AccumulatorParam 接口有兩個方法:`zero`方法替你的資料類型提供一個“0 值” ( zero value );`addInPlace`方法計算兩個值的和。例如,我们有一個`Vector`物件代表數學向量,累加器可以透過下列方式進行定義: 53 | 54 | ```scala 55 | object VectorAccumulatorParam extends AccumulatorParam[Vector] { 56 | def zero(initialValue: Vector): Vector = { 57 | Vector.zeros(initialValue.size) 58 | } 59 | def addInPlace(v1: Vector, v2: Vector): Vector = { 60 | v1 += v2 61 | } 62 | } 63 | // Then, create an Accumulator of this type: 64 | val vecAccum = sc.accumulator(new Vector(...))(VectorAccumulatorParam) 65 | ``` 66 | 67 | 在scala 中,Spark 支援一般[Accumulable](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.Accumulable) 接口來累計數值-結果類型和用於累加的元素類型不同( 例如收集的元素建立列表)。Spark 支援`SparkContext.accumulableCollection` 方法累加一般的scala 集合。 68 | -------------------------------------------------------------------------------- /quick-start/README.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | 3 | 本節課程提供一個使用 Spark 的快速介绍,首先我们使用 Spark 的交互式 shell (用 Python 或 Scala) 介绍它的 API。當示範如何在 Java, Scala 和 Python 寫獨立的程式時,可以參考[編程指南](https://spark.apache.org/docs/latest/programming-guide.html)裡完整的範例。 4 | 5 | 依照這個指南,首先從 [Spark 網站](https://spark.apache.org/downloads.html)下載一個 Spark 發行套件。因為我們不會使用 HDFS,你可以下載任何 Hadoop 版本的套件。 6 | 7 | 8 | * [Spark Shell](using-spark-shell.md) 9 | * [獨立應用程序](self-contained-applications.md) 10 | * [開始翻滾吧](where-to-go-from-here.md) 11 | -------------------------------------------------------------------------------- /quick-start/self-contained-applications.md: -------------------------------------------------------------------------------- 1 | # [獨立應用程式](https://spark.apache.org/docs/latest/quick-start.html#self-contained-applications) 2 | 3 | 現在假設我們想要使用 Spark API 寫一個獨立的應用程式。我们將通過使用 Scala(用 SBT),Java(用 Maven) 和 Python 寫一個簡單的應用程式來學習。 4 | 5 | 我们用 Scala 創建一個非常簡單的 Spark 應用程式。如此簡單,他的名字是 `SimpleApp.scala`: 6 | 7 | ```scala 8 | /* SimpleApp.scala */ 9 | import org.apache.spark.SparkContext 10 | import org.apache.spark.SparkContext._ 11 | import org.apache.spark.SparkConf 12 | 13 | object SimpleApp { 14 | def main(args: Array[String]) { 15 | val logFile = "YOUR_SPARK_HOME/README.md" // 應該是你系統上的某個文件 16 | val conf = new SparkConf().setAppName("Simple Application") 17 | val sc = new SparkContext(conf) 18 | val logData = sc.textFile(logFile, 2).cache() 19 | val numAs = logData.filter(line => line.contains("a")).count() 20 | val numBs = logData.filter(line => line.contains("b")).count() 21 | println("Lines with a: %s, Lines with b: %s".format(numAs, numBs)) 22 | } 23 | } 24 | ``` 25 | 26 | 這個程式僅僅是看 Spark README 中計算行裡面包含 'a' 和包含 'b' 的次數。你需要注意將 `YOUR_SPARK_HOME` 替換成你已經安裝 Spark 的路徑。不像之前的 Spark Shell 例子,這裡初始化了自己的 SparkContext,我們把 SparkContext 初始化做為程式的一部分。 27 | 28 | 我們通過 SparkContext 的建構函數傳入 [SparkConf](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.SparkConf) 物件,這個物件包含了一些關於我們程式的訊息。 29 | 30 | 我們的程式依賴於 Spark API,所以我們需要包含一個 sbt 設定文件,`simple.sbt` .這個文件解釋了 Spark 是一個依賴(dependency)。這個文件還需要補充 Spark 依賴於一個 repository: 31 | 32 | ```scala 33 | name := "Simple Project" 34 | 35 | version := "1.0" 36 | 37 | scalaVersion := "2.10.4" 38 | 39 | libraryDependencies += "org.apache.spark" %% "spark-core" % "1.2.0" 40 | ``` 41 | 42 | 要讓 sbt 正确工作,我们需要把 `SimpleApp.scala` 和 `simple.sbt` 按照標準的文件目錄結構配置。做好之後,我們可以把程序的代碼創建成一個 JAR 套件。然后使用 `spark-submit` 來運行我們的程式。 43 | 44 | ``` 45 | # Your directory layout should look like this 46 | $ find . 47 | . 48 | ./simple.sbt 49 | ./src 50 | ./src/main 51 | ./src/main/scala 52 | ./src/main/scala/SimpleApp.scala 53 | 54 | # Package a jar containing your application 55 | $ sbt package 56 | ... 57 | [info] Packaging {..}/{..}/target/scala-2.10/simple-project_2.10-1.0.jar 58 | 59 | # Use spark-submit to run your application 60 | $ YOUR_SPARK_HOME/bin/spark-submit \ 61 | --class "SimpleApp" \ 62 | --master local[4] \ 63 | target/scala-2.10/simple-project_2.10-1.0.jar 64 | ... 65 | Lines with a: 46, Lines with b: 23 66 | ``` 67 | -------------------------------------------------------------------------------- /quick-start/standalone-applications.md: -------------------------------------------------------------------------------- 1 | # 獨立應用程序 2 | -------------------------------------------------------------------------------- /quick-start/using-spark-shell.md: -------------------------------------------------------------------------------- 1 | # [使用 Spark Shell](https://spark.apache.org/docs/latest/quick-start.html#interactive-analysis-with-the-spark-shell) 2 | 3 | ## 基礎 4 | 5 | Spark 的 shell 作為一個强大的交互式數據分析工具,提供了一個簡單的方式來學習 API。它可以使用 Scala(在 Java 虛擬機上運行現有的 Java 庫的一个很好方式) 或 Python。在 Spark 目錄裡使用下面的方式開始運行: 6 | 7 | ```scala 8 | ./bin/spark-shell 9 | ``` 10 | 11 | Spark 最主要的抽象是叫Resilient Distributed Dataset(RDD) 的彈性分布式資料集。RDDs 可以使用 Hadoop InputFormats(例如 HDFS 文件)創建,也可以從其他的 RDDs 轉換。讓我們在 Spark 源代碼目錄從 README 文本文件中創建一個新的 RDD。 12 | 13 | ```scala 14 | scala> val textFile = sc.textFile("README.md") 15 | textFile: spark.RDD[String] = spark.MappedRDD@2ee9b6e3 16 | ``` 17 | 18 | RDD 的 [actions](https://spark.apache.org/docs/latest/programming-guide.html#actions) 從 RDD 中返回值,[transformations](https://spark.apache.org/docs/latest/programming-guide.html#transformations) 可以轉換成一個新 RDD 並返回它的引用。讓我們開始使用幾個操作: 19 | 20 | ```scala 21 | scala> textFile.count() // RDD 的數據行數 22 | res0: Long = 126 23 | 24 | scala> textFile.first() // RDD 的第一行數據 25 | res1: String = # Apache Spark 26 | ``` 27 | 28 | 現在讓我們使用一個 transformation,我們將使用 [filter](https://spark.apache.org/docs/latest/programming-guide.html#transformations) 在這個文件裡返回一個包含子數據集的新 RDD。 29 | 30 | ```scala 31 | scala> val linesWithSpark = textFile.filter(line => line.contains("Spark")) 32 | linesWithSpark: spark.RDD[String] = spark.FilteredRDD@7dd4af09 33 | ``` 34 | 35 | 我們可以把 actions 和 transformations 連接在一起: 36 | 37 | ```scala 38 | scala> textFile.filter(line => line.contains("Spark")).count() // 有多少行包括 "Spark"? 39 | res3: Long = 15 40 | ``` 41 | 42 | ## 更多 RDD 操作 43 | 44 | RDD actions 和 transformations 能被用在更多的複雜計算中。比方說,我們想要找到一行中最多的單詞數量: 45 | 46 | ```scala 47 | scala> textFile.map(line => line.split(" ").size).reduce((a, b) => if (a > b) a else b) 48 | res4: Long = 15 49 | ``` 50 | 51 | 首先將行映射(map)成一個整數產生一個新 RDD。 在這個新的 RDD 上調用 `reduce` 找到行中最大的個數。 `map` 和 `reduce` 的參數是 Scala 的函數串(closures),並且可以使用任何語言特性或者 Scala/Java 函式庫。例如,我们可以很方便地調用其他的函數。 我們使用 `Math.max()` 函數讓代碼更容易理解: 52 | 53 | ```scala 54 | scala> import java.lang.Math 55 | import java.lang.Math 56 | 57 | scala> textFile.map(line => line.split(" ").size).reduce((a, b) => Math.max(a, b)) 58 | res5: Int = 15 59 | ``` 60 | 61 | Hadoop 流行的一個通用的數據流(data flow)模式是 MapReduce。Spark 能很容易地實現 MapReduce: 62 | 63 | ```scala 64 | scala> val wordCounts = textFile.flatMap(line => line.split(" ")).map(word => (word, 1)).reduceByKey((a, b) => a + b) 65 | wordCounts: spark.RDD[(String, Int)] = spark.ShuffledAggregatedRDD@71f027b8 66 | ``` 67 | 68 | 這裡,我們結合 [flatMap](), [map]() 和 [reduceByKey]() 來計算文件裡每個單詞出現的數量,它的結果是包含一組(String, Int) 鍵值對的 RDD。我们可以使用 [collect] 操作在我們的 shell 中收集單詞的數量: 69 | 70 | ```scala 71 | scala> wordCounts.collect() 72 | res6: Array[(String, Int)] = Array((means,1), (under,2), (this,3), (Because,1), (Python,2), (agree,1), (cluster.,1), ...) 73 | ``` 74 | 75 | ## 暫存(Caching) 76 | 77 | Spark 支持把數據集拉到集群內的暫存記憶體中。當要重複取用資料時非常有用.例如當我们在一個小的熱(hot)數據集中查詢,或者運行一個像網頁搜索排序這樣的迭代演算法。作為一個簡單的例子,讓我们把 `linesWithSpark` 數據集標記在暫存中: 78 | 79 | ```scala 80 | scala> linesWithSpark.cache() 81 | res7: spark.RDD[String] = spark.FilteredRDD@17e51082 82 | 83 | scala> linesWithSpark.count() 84 | res8: Long = 15 85 | 86 | scala> linesWithSpark.count() 87 | res9: Long = 15 88 | ``` 89 | 90 | 暫存 100 行的文本文件來研究 Spark 這看起来很傻。真正讓人感興趣的部分是我们可以在非常大型的數據集中使用同樣的函數,甚至在 10 個或者 100 個節點中交叉計算。你同樣可以使用 `bin/spark-shell` 連接到一个 cluster 來繼亂掉[编程指南](https://spark.apache.org/docs/latest/programming-guide.html#initializing-spark)中的方法進行交互操作。 91 | -------------------------------------------------------------------------------- /quick-start/where-to-go-from-here.md: -------------------------------------------------------------------------------- 1 | # [下一步?](https://spark.apache.org/docs/latest/quick-start.html#where-to-go-from-here) 2 | 3 | 恭喜你成功運行你的第一個 Spark 應用程式! 4 | 5 | - 要深入了解 API,可以從[Spark编程指南](https://spark.apache.org/docs/latest/programming-guide.html)開始,或者從其他的元件開始,例如:Spark Streaming。 6 | - 要讓程式運行在集群(cluster)上,前往[部署概論](https://spark.apache.org/docs/latest/cluster-overview.html)。 7 | - 最後,Spark 在 `examples` 文件目錄里包含了 [Scala](https://github.com/apache/spark/tree/master/examples/src/main/scala/org/apache/spark/examples), [Java](https://github.com/apache/spark/tree/master/examples/src/main/java/org/apache/spark/examples) 和 [Python](https://github.com/apache/spark/tree/master/examples/src/main/python) 的幾個簡單的例子,你可以直接運行它们: 8 | 9 | ``` 10 | # For Scala and Java, use run-example: 11 | ./bin/run-example SparkPi 12 | 13 | # For Python examples, use spark-submit directly: 14 | ./bin/spark-submit examples/src/main/python/pi.py 15 | ``` 16 | -------------------------------------------------------------------------------- /spark-sql/README.md: -------------------------------------------------------------------------------- 1 | # [Spark SQL](https://spark.apache.org/docs/latest/sql-programming-guide.html#overview) 2 | 3 | Spark SQL允許Spark執行用SQL, HiveQL或者Scala表示的關係查詢。這個模組的核心是一個新類型的RDD-[SchemaRDD](http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.SchemaRDD)。 4 | SchemaRDDs由[行](http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.package@Row:org.apache.spark.sql.catalyst.expressions.Row.type)物件組成,行物件用有一個模式(scheme) 5 | 來描述行中每一列的資料類型。SchemaRDD與關聯式資料庫中的表(table)很相似。可以通過存在的RDD、一個[Parquet](http://parquet.io/)文件、一個JSON資料庫或者對儲存在[Apache Hive](http://hive.apache.org/)中的資料執行HiveSQL查詢中創建。 6 | 7 | 本章的所有例子都利用了Spark分布式系统中的樣本資料,可以在`spark-shell`中運行它們。 8 | 9 | * [開始](getting-started.md) 10 | * [資料來源](data-sources/README.md) 11 | * [RDDs](data-sources/rdds.md) 12 | * [parquet文件](data-sources/parquet-files.md) 13 | * [JSON資料集](data-sources/jSON-datasets.md) 14 | * [Hive表](data-sources/hive-tables.md) 15 | * [性能優化](performance-tuning.md) 16 | * [其它SQL接口](other-sql-interfaces.md) 17 | * [編寫語言整合(Language-Integrated)的相關查詢](writing-language-integrated-relational-queries.md) 18 | * [Spark SQL資料類型](spark-sql-dataType-reference.md) 19 | -------------------------------------------------------------------------------- /spark-sql/compatibility-with-other-systems/compatibility-with-apache-hive.md: -------------------------------------------------------------------------------- 1 | # 与Apache Hive的兼容性 2 | 3 | Spark SQL设计得和Hive元存储(Metastore)、序列化反序列化(SerDes)、用户自定义函数(UDFs)相兼容。当前的Spark SQL以Hive 0.12.0和0.13.1为基础。 4 | 5 | ## 部署存在的Hive仓库 6 | 7 | Spark SQL Thrift JDBC服务器按照“开箱即用(out of the box)”的方式设计得和存在的Hive相兼容。你不必修改存在的Hive仓库或者改变数据的位置或者分割表。 8 | 9 | ## 支持的Hive特征 10 | 11 | Spark SQL支持绝大部分的Hive特征。 12 | 13 | - Hive查询语句 14 | - SELECT 15 | - GROUP BY 16 | - ORDER BY 17 | - CLUSTER BY 18 | - SORT BY 19 | - 所有的Hive操作 20 | - 关系运算符(=, ⇔, ==, <>, <, >, >=, <=等) 21 | - 算术运算符(+, -, *, /, %等) 22 | - 逻辑运算符(AND, &&, OR, ||) 23 | - 复杂的类型构造函数 24 | - 数学函数(sign, ln, cos等) 25 | - 字符串函数(instr, length, printf等) 26 | - 用户自定义函数 27 | - 用户自定义聚合函数(UDAF) 28 | - 用户自定义序列化格式 29 | - Joins 30 | - JOIN 31 | - {LEFT|RIGHT|FULL} OUTER JOIN 32 | - LEFT SEMI JOIN 33 | - CROSS JOIN 34 | - Unions 35 | - 子查询 36 | - SELECT col FROM ( SELECT a + b AS col from t1) t2 37 | - 采样 38 | - Explain 39 | - Partitioned表 40 | - 视图 41 | - 所有的Hive DDL函数 42 | - CREATE TABLE 43 | - CREATE TABLE AS SELECT 44 | - ALTER TABLE 45 | - 大部分的Hive数据类型 46 | - TINYINT 47 | - SMALLINT 48 | - INT 49 | - BIGINT 50 | - BOOLEAN 51 | - FLOAT 52 | - DOUBLE 53 | - STRING 54 | - BINARY 55 | - TIMESTAMP 56 | - DATE 57 | - ARRAY<> 58 | - MAP<> 59 | - STRUCT<> 60 | 61 | -------------------------------------------------------------------------------- /spark-sql/compatibility-with-other-systems/migration-guide-shark-user.md: -------------------------------------------------------------------------------- 1 | # Shark用户迁移指南 2 | 3 | ## 调度(Scheduling) 4 | 5 | 为JDBC客户端会话设置[Fair Scheduler](https://spark.apache.org/docs/latest/job-scheduling.html#fair-scheduler-pools)池。用户可以设置`spark.sql.thriftserver.scheduler.pool` 6 | 变量。 7 | 8 | ```shell 9 | SET spark.sql.thriftserver.scheduler.pool=accounting; 10 | ``` 11 | ## Reducer数量 12 | 13 | 在Shark中,默认的reducer数目是1,可以通过`mapred.reduce.tasks`属性来控制其多少。Spark SQL反对使用这个属性,支持`spark.sql.shuffle.partitions`属性,它的默认值是200。 14 | 用户可以自定义这个属性。 15 | 16 | ```shell 17 | SET spark.sql.shuffle.partitions=10; 18 | SELECT page, count(*) c 19 | FROM logs_last_month_cached 20 | GROUP BY page ORDER BY c DESC LIMIT 10; 21 | ``` 22 | 23 | 你也可以在`hive-site.xml`中设置这个属性覆盖默认值。现在,`mapred.reduce.tasks`属性仍然可以被识别,它会自动转换为`spark.sql.shuffle.partition`。 24 | 25 | ## Caching 26 | 27 | 表属性`shark.cache`不再存在,名字以`_cached`结尾的表也不再自动缓存。作为替代的方法,我们提供`CACHE TABLE`和`UNCACHE TABLE`语句显示地控制表的缓存。 28 | 29 | ```shell 30 | CACHE TABLE logs_last_month; 31 | UNCACHE TABLE logs_last_month; 32 | ``` 33 | 注意:`CACHE TABLE tbl`是懒加载的,类似于在RDD上使用`.cache`。这个命令仅仅标记tbl来确保计算时分区被缓存,但实际并不缓存它,直到一个查询用到tbl才缓存。 34 | 35 | 要强制表缓存,你可以简单地执行`CACHE TABLE`后,立即count表。 36 | 37 | ```shell 38 | CACHE TABLE logs_last_month; 39 | SELECT COUNT(1) FROM logs_last_month; 40 | ``` 41 | 42 | 有几个缓存相关的特征现在还不支持: 43 | 44 | - 用户定义分区级别的缓存逐出策略 45 | - RDD重加载 46 | - 内存缓存的写入策略(In-memory cache write through policy) -------------------------------------------------------------------------------- /spark-sql/data-sources/README.md: -------------------------------------------------------------------------------- 1 | # [資料來源](https://spark.apache.org/docs/latest/sql-programming-guide.html#data-sources) 2 | 3 | Spark SQL 支持通過 SchemaRDD 接口操作各種資料來源。一個 SchemaRDD 能夠作為一個一般的 RDD 被操作,也可以被註冊為一個臨時的表。註冊一個 SchemaRDD 為一個表就可以允許你在其數據上運行 SQL 查詢。這節描述了將資料讀取為 SchemaRDD 的多種方法。 4 | 5 | * [RDDs](rdds.md) 6 | * [parquet文件](parquet-files.md) 7 | * [JSON資料集](jSON-datasets.md) 8 | * [Hive表](hive-tables.md) 9 | -------------------------------------------------------------------------------- /spark-sql/data-sources/hive-tables.md: -------------------------------------------------------------------------------- 1 | # [Hive](https://spark.apache.org/docs/latest/sql-programming-guide.html#hive-tables) 2 | 3 | Spark SQL也支援從 Apache Hive 中讀取和寫入資料。然而,Hive 有大量的相依套件,所以它不包含在 Spark 工具中。可以透過 `-Phive` 和 `-Phive-thriftserver` 參數建構 Spark,使其 4 | 支持Hive。注意這個重新建構的 jar 檔必須存在於所有的worker節點中,因為它們需要透過 Hive 的序列化和反序列化來存取儲存在 Hive 中的資料。 5 | 6 | 當和 Hive 一起工作時,開發者需要提供 HiveContext。 HiveContext 從 SQLContext 繼承而来,它增加了在 MetaStore 中發現表以及利用 HiveSql 寫查詢的功能。没有 Hive 部署的用戶也 7 | 可以創建 HiveContext。當沒有通過 `hive-site.xml` 配置,上下文將會在當前目錄自動地創建 `metastore_db` 和 `warehouse`。 8 | 9 | ```scala 10 | // sc is an existing SparkContext. 11 | val sqlContext = new org.apache.spark.sql.hive.HiveContext(sc) 12 | 13 | sqlContext.sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING)") 14 | sqlContext.sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src") 15 | 16 | // Queries are expressed in HiveQL 17 | sqlContext.sql("FROM src SELECT key, value").collect().foreach(println) 18 | ``` 19 | -------------------------------------------------------------------------------- /spark-sql/data-sources/jSON-datasets.md: -------------------------------------------------------------------------------- 1 | # [JSON資料集](https://spark.apache.org/docs/latest/sql-programming-guide.html#json-datasets) 2 | 3 | Spark SQL能夠自動推斷 JSON 資料集的模式,讀取為 SchemaRDD。這種轉換可以透過下面兩種方法來實現. 4 | 5 | - jsonFile :從一個包含 JSON 文件的目錄中讀取。文件中的每一行是一個 JSON 物件 6 | - jsonRDD :從存在的 RDD 讀取資料,這些 RDD 的每个元素是一個包含 JSON 物件的字串 7 | 8 | 注意作為*jsonFile*的文件不是一個典型的 JSON 文件,每行必須是獨立的並且包含一個有效的JSON物件。一個多行的JSON物件經常會失敗. 9 | 10 | ```scala 11 | // sc is an existing SparkContext. 12 | val sqlContext = new org.apache.spark.sql.SQLContext(sc) 13 | 14 | // A JSON dataset is pointed to by path. 15 | // The path can be either a single text file or a directory storing text files. 16 | val path = "examples/src/main/resources/people.json" 17 | // Create a SchemaRDD from the file(s) pointed to by path 18 | val people = sqlContext.jsonFile(path) 19 | 20 | // The inferred schema can be visualized using the printSchema() method. 21 | people.printSchema() 22 | // root 23 | // |-- age: integer (nullable = true) 24 | // |-- name: string (nullable = true) 25 | 26 | // Register this SchemaRDD as a table. 27 | people.registerTempTable("people") 28 | 29 | // SQL statements can be run by using the sql methods provided by sqlContext. 30 | val teenagers = sqlContext.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19") 31 | 32 | // Alternatively, a SchemaRDD can be created for a JSON dataset represented by 33 | // an RDD[String] storing one JSON object per string. 34 | val anotherPeopleRDD = sc.parallelize( 35 | """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil) 36 | val anotherPeople = sqlContext.jsonRDD(anotherPeopleRDD) 37 | ``` 38 | 39 | -------------------------------------------------------------------------------- /spark-sql/data-sources/parquet-files.md: -------------------------------------------------------------------------------- 1 | # [Parquet檔案](https://spark.apache.org/docs/latest/sql-programming-guide.html#parquet-files) 2 | 3 | Parquet是一欄位(columnar)格式,可以被許多其它的資料處理系统支援。 Spark SQL 提供支援讀和寫 Parquet 檔案的功能,這些檔案可以自動地保留原始資料的模式。 4 | 5 | ## 讀取資料 6 | 7 | ```scala 8 | // sqlContext from the previous example is used in this example. 9 | // createSchemaRDD is used to implicitly convert an RDD to a SchemaRDD. 10 | import sqlContext.createSchemaRDD 11 | 12 | val people: RDD[Person] = ... // An RDD of case class objects, from the previous example. 13 | 14 | // The RDD is implicitly converted to a SchemaRDD by createSchemaRDD, allowing it to be stored using Parquet. 15 | people.saveAsParquetFile("people.parquet") 16 | 17 | // Read in the parquet file created above. Parquet files are self-describing so the schema is preserved. 18 | // The result of loading a Parquet file is also a SchemaRDD. 19 | val parquetFile = sqlContext.parquetFile("people.parquet") 20 | 21 | //Parquet files can also be registered as tables and then used in SQL statements. 22 | parquetFile.registerTempTable("parquetFile") 23 | val teenagers = sqlContext.sql("SELECT name FROM parquetFile WHERE age >= 13 AND age <= 19") 24 | teenagers.map(t => "Name: " + t(0)).collect().foreach(println) 25 | ``` 26 | 27 | ## 配置 28 | 29 | 可以在 SQLContext 上使用 setConf 方法配置 Parquet .或者在用 SQL 時使用 `SET key=value` 指令來配置 Parquet。 30 | 31 | Property Name | Default | Meaning 32 | --- | --- | --- 33 | spark.sql.parquet.binaryAsString | false | 一些其它的Parquet-producing系统,特别是Impala和其它版本的Spark SQL,當寫出 Parquet 模式的時候,二進位資料和字串之間無法分區分。這個標記告訴Spark SQL 將二進位資料解釋為字串來提供這些系统的相容性。 34 | spark.sql.parquet.cacheMetadata | true | 打開 parquet 中繼資料的暫存,可以提高靜態數據的查詢速度 35 | spark.sql.parquet.compression.codec | gzip | 設置寫 parquet 文件時的壓縮演算法,可以接受的值包括:uncompressed, snappy, gzip, lzo 36 | spark.sql.parquet.filterPushdown | false | 打開 Parquet 過濾器的 pushdown 優化。因為已知的 Paruet 錯誤,這個選項預設是關閉的。如果你的表不包含任何空的字串或者二進位的列,開啟這個選項仍是安全的 37 | spark.sql.hive.convertMetastoreParquet | true | 當設置為 false 時,Spark SQL 將使用 Hive SerDe 代替内建的支援 38 | -------------------------------------------------------------------------------- /spark-sql/data-sources/rdds.md: -------------------------------------------------------------------------------- 1 | # [RDDs](https://spark.apache.org/docs/latest/sql-programming-guide.html#rdds) 2 | 3 | Spark 支持兩種方法將存在的 RDDs 轉換為 SchemaRDDs 。第一種方法使用映射來推斷包含特定對象類型的RDD模式(schema)。在你寫 spark 程序的同時,當你已經知道了模式,這種基於映射的方法可以使代碼更簡潔並且程序工作得更好。 4 | 5 | 創建 SchemaRDDs 的第二種方法是通過一個編程接口來實現,這個接口允许你建構一個模式,然後在存在的 RDDs 上使用它。雖然這種方法更冗長,但是它允許你在運行期之前不知道列以及列的類型的情况下構造 SchemaRDDs。 6 | 7 | ## 利用映射推断(inffering)模式(scheme) 8 | 9 | Spark SQL的Scala接口支持將包含樣本類(case class)的 RDDs 自動轉換為 SchemaRDD。這個樣本類(case class)定義了表的模式。 10 | 11 | 給樣本類的參數名字通過映射來讀取,然後作為列的名字。樣本類可以嵌套或者包含複雜的類型如序列或者數組。這個 RDD 可以隱式轉化為一個 SchemaRDD ,然後註冊為一個表。表可以在後續的 sql 語句中使用。 12 | 13 | ```scala 14 | // sc is an existing SparkContext. 15 | val sqlContext = new org.apache.spark.sql.SQLContext(sc) 16 | // createSchemaRDD is used to implicitly convert an RDD to a SchemaRDD. 17 | import sqlContext.createSchemaRDD 18 | 19 | // Define the schema using a case class. 20 | // Note: Case classes in Scala 2.10 can support only up to 22 fields. To work around this limit, 21 | // you can use custom classes that implement the Product interface. 22 | case class Person(name: String, age: Int) 23 | 24 | // Create an RDD of Person objects and register it as a table. 25 | val people = sc.textFile("examples/src/main/resources/people.txt").map(_.split(",")).map(p => Person(p(0), p(1).trim.toInt)) 26 | people.registerTempTable("people") 27 | 28 | // SQL statements can be run by using the sql methods provided by sqlContext. 29 | val teenagers = sqlContext.sql("SELECT name FROM people WHERE age >= 13 AND age <= 19") 30 | 31 | // The results of SQL queries are SchemaRDDs and support all the normal RDD operations. 32 | // The columns of a row in the result can be accessed by ordinal. 33 | teenagers.map(t => "Name: " + t(0)).collect().foreach(println) 34 | ``` 35 | 36 | ## [编程指定模式](https://spark.apache.org/docs/latest/sql-programming-guide.html#programmatically-specifying-the-schema) 37 | 38 | 當樣本類不能提前確定(例如,記錄的結構是經過編碼的字串,或者一個文本集合將會被解析,不同的字段投射给不同的用戶),一个 SchemaRDD 可以通过三步来创建。 39 | 40 | - 從原來的 RDD 創建一個行的 RDD 41 | - 創建由一个 `StructType` 表示的模式與第一步創建的 RDD 的行結構相匹配 42 | - 在行 RDD 上通過 `applySchema` 方法應用模式 43 | 44 | ```scala 45 | // sc is an existing SparkContext. 46 | val sqlContext = new org.apache.spark.sql.SQLContext(sc) 47 | 48 | // Create an RDD 49 | val people = sc.textFile("examples/src/main/resources/people.txt") 50 | 51 | // The schema is encoded in a string 52 | val schemaString = "name age" 53 | 54 | // Import Spark SQL data types and Row. 55 | import org.apache.spark.sql._ 56 | 57 | // Generate the schema based on the string of schema 58 | val schema = 59 | StructType( 60 | schemaString.split(" ").map(fieldName => StructField(fieldName, StringType, true))) 61 | 62 | // Convert records of the RDD (people) to Rows. 63 | val rowRDD = people.map(_.split(",")).map(p => Row(p(0), p(1).trim)) 64 | 65 | // Apply the schema to the RDD. 66 | val peopleSchemaRDD = sqlContext.applySchema(rowRDD, schema) 67 | 68 | // Register the SchemaRDD as a table. 69 | peopleSchemaRDD.registerTempTable("people") 70 | 71 | // SQL statements can be run by using the sql methods provided by sqlContext. 72 | val results = sqlContext.sql("SELECT name FROM people") 73 | 74 | // The results of SQL queries are SchemaRDDs and support all the normal RDD operations. 75 | // The columns of a row in the result can be accessed by ordinal. 76 | results.map(t => "Name: " + t(0)).collect().foreach(println) 77 | ``` 78 | -------------------------------------------------------------------------------- /spark-sql/getting-started.md: -------------------------------------------------------------------------------- 1 | # [開始](https://spark.apache.org/docs/latest/sql-programming-guide.html#getting-started) 2 | 3 | Spark中所有相關功能的入口點是[SQLContext](http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.SQLContext)物件或者它的子物件,創建一個SQLContext僅僅需要一個SparkContext。 4 | 5 | ```scala 6 | val sc: SparkContext // An existing SparkContext. 7 | val sqlContext = new org.apache.spark.sql.SQLContext(sc) 8 | 9 | // createSchemaRDD is used to implicitly convert an RDD to a SchemaRDD. 10 | import sqlContext.createSchemaRDD 11 | ``` 12 | 除了一個基本的 SQLContext,你也能夠創建一個 HiveContext,它支持基本 SQLContext 所支持功能的一個超集( superset)。它額外的功能包括用更完整的 HiveQL 分析器寫查詢去訪問 HiveUDFs 的能力、 13 | 從 Hive Table 讀取資料的能力。用 HiveContext 你不需要開啟一個已經存在的 Hive, SQLContext 可用的資料來源 HiveContext 也可使用。HiveContext 分開打包是為了避免在 Spark 構建時包含了所有的 Hive 依賴。如果對你的應用程式來說,這些依賴不存在問題, Spark 1.2推薦使用 HiveContext。以後的穩定版本將專注於為 SQLContext 提供與 HiveContext 等價的功能。 14 | 15 | 用來解析查詢語句的特定SQL變種語言可以通過 `spark.sql.dialect` 選項來選擇。這個參數可以通過兩種方式改變,一種方式是通過 `setConf` 方法設定,另一種方式是在SQL命令中通過 `SET key=value` 16 | 來設定。對於 SQLContext ,唯一可用的方言是 “sql” ,它是 Spark SQL 提供的一个個簡單的SQL解析器。在 HiveContext 中,雖然也支持 "sql" ,但預設的語言是 “hiveql”。這是因為 HiveQL解析器更更完整。在很多實例中推薦使用 “hiveql”。 17 | -------------------------------------------------------------------------------- /spark-sql/other-sql-interfaces.md: -------------------------------------------------------------------------------- 1 | # [其它SQL接口](https://spark.apache.org/docs/latest/sql-programming-guide.html#other-sql-interfaces) 2 | 3 | Spark SQL 也支持直接運行 SQL 查詢的接口,不用寫任何代碼。 4 | 5 | ## 運行 Thrift JDBC/ODBC 伺服器 6 | 7 | 這裡實現的 Thrift JDBC/ODBC 伺服器與 Hive 0.12中的[HiveServer2](https://cwiki.apache.org/confluence/display/Hive/Setting+Up+HiveServer2)一致。你可以用在 Spark 8 | 或者 Hive 0.12 附帶的 beeline 腳本測試 JDBC 伺服器。 9 | 10 | 在 Spark 目錄中,運行下面的命令啟動 JDBC/ODBC 伺服器。 11 | 12 | ```shell 13 | ./sbin/start-thriftserver.sh 14 | ``` 15 | 16 | 這個腳本接受任何的 `bin/spark-submit` 命令行參數,加上一個 `--hiveconf` 參數用來指明 Hive 屬性。你可以運行 `./sbin/start-thriftserver.sh --help` 來獲得所有可用選項的完整列表。預設情况下,伺服器監聽 `localhost:10000` 。你可以用環境變數覆蓋這些變數。 17 | 18 | ```shell 19 | export HIVE_SERVER2_THRIFT_PORT= 20 | export HIVE_SERVER2_THRIFT_BIND_HOST= 21 | ./sbin/start-thriftserver.sh \ 22 | --master \ 23 | ... 24 | ``` 25 | 或者透過系統變數覆蓋。 26 | 27 | ```shell 28 | ./sbin/start-thriftserver.sh \ 29 | --hiveconf hive.server2.thrift.port= \ 30 | --hiveconf hive.server2.thrift.bind.host= \ 31 | --master 32 | ... 33 | ``` 34 | 現在你可以用 `beeline` 測試 Thrift JDBC/ODBC 伺服器。 35 | 36 | ```shell 37 | ./bin/beeline 38 | ``` 39 | 40 | 連接到 Thrift JDBC/ODBC 伺服器的方式如下: 41 | 42 | ```shell 43 | beeline> !connect jdbc:hive2://localhost:10000 44 | ``` 45 | 46 | Beeline 將會詢問你的用戶名稱和密碼。在非安全的模式,簡單地輸入你機器的用戶名稱和空密碼就行了。對於安全模式,你可以按照[Beeline文檔](https://cwiki.apache.org/confluence/display/Hive/HiveServer2+Clients)的說明來執行。 47 | 48 | ## 運行 Spark SQL CLI 49 | 50 | Spark SQL CLI 是一個便利的工具,它可以在本地運行 Hive 元儲存(metastore)服務、執行命令行輸入的查詢。注意,Spark SQL CLI不能與 Thrift JDBC 伺服器通信。 51 | 52 | 在 Spark 目錄運行下面的命令可以啟動 Spark SQL CLI。 53 | 54 | ```shell 55 | ./bin/spark-sql 56 | ``` 57 | 58 | -------------------------------------------------------------------------------- /spark-sql/performance-tuning.md: -------------------------------------------------------------------------------- 1 | # [性能優化](https://spark.apache.org/docs/latest/sql-programming-guide.html#performance-tuning) 2 | 3 | 對於某些工作負載,可以在通過在記憶體中緩存數據或者打開一些實驗選項來提高性能。 4 | 5 | ## 在記憶體中緩存數據 6 | 7 | Spark SQL可以通過調用 `sqlContext.cacheTable("tableName")` 方法來緩存使用欄位格式的表。然後, Spark 將會僅僅瀏覽需要的欄位並且自動壓縮數據以減少記憶體的使用以及垃圾回收的 8 | 壓力。你可以通過調用 `sqlContext.uncacheTable("tableName")` 方法在記憶體中刪除表。 9 | 10 | 注意,如果你調用 `schemaRDD.cache()` 而不是 `sqlContext.cacheTable(...)` ,表將不會用欄位格式來緩存。在這種情况下, `sqlContext.cacheTable(...)` 是强烈推薦的用法。 11 | 12 | 可以在 SQLContext 上使用 setConf 方法或者在用SQL時運行 `SET key=value` 命令來配置記憶體緩存。 13 | 14 | Property Name | Default | Meaning 15 | --- | --- | --- 16 | spark.sql.inMemoryColumnarStorage.compressed | true | 當設置為 true 時, Spark SQL 將為基於數據統計信息的每列自動選擇一個壓縮算法。 17 | spark.sql.inMemoryColumnarStorage.batchSize | 10000 | 控制每一批(batches)資料給欄位緩存的大小。更大的資料可以提高記憶體的利用率以及壓縮效率,但有OOMs的風險 18 | 19 | ## 其它的配置選項 20 | 21 | 以下的選項也可以用來條整查詢執行的性能。有可能這些選項會在以後的版本中放棄使用,這是因為更多的最佳化會自動執行。 22 | 23 | Property Name | Default | Meaning 24 | --- | --- | --- 25 | spark.sql.autoBroadcastJoinThreshold | 10485760(10m) | 配置一個表的最大大小(byte)。當執行 join 操作時,這個表將會廣播到所有的 worker 節點。可以將值設置為 -1 來禁用廣播。注意,目前的統計數據只支持 Hive Metastore 表,命令 `ANALYZE TABLE COMPUTE STATISTICS noscan` 已經在這個表中運行。 26 | spark.sql.codegen | false | 當為 true 時,特定查詢中的表達式求值的代碼將會在運行時動態生成。對於一些用有複雜表達式的查詢,此選項可導致顯著速度提升。然而,對於簡單的查詢,這各選項會減慢查詢的執行 27 | spark.sql.shuffle.partitions | 200 | 設定當操作 join 或者 aggregation 而需要 shuffle 資料時分區的数量 28 | -------------------------------------------------------------------------------- /spark-sql/spark-sql-dataType-reference.md: -------------------------------------------------------------------------------- 1 | # [Spark SQL資料類型](https://spark.apache.org/docs/latest/sql-programming-guide.html#spark-sql-datatype-reference) 2 | 3 | - 數字類型 4 | - ByteType:代表一個位元的整數。範圍是-128到127 5 | - ShortType:代表兩個位元的整數。範圍是-32768到32767 6 | - IntegerType:代表4個位元的整數。範圍是-2147483648到2147483647 7 | - LongType:代表8個位元的整數。範圍是-9223372036854775808到9223372036854775807 8 | - FloatType:代表4位元的單精度浮點數 9 | - DoubleType:代表8位元的雙精度浮點數 10 | - DecimalType:代表任意精度的10進位資料。透過内部的java.math.BigDecimal支援。BigDecimal由一個任意精度的不定長度(unscaled value)的整數和一個32位元整數组成 11 | - StringType:代表一個字串 12 | - BinaryType:代表個byte序列值 13 | - BooleanType:代表boolean值 14 | - Datetime類型 15 | - TimestampType:代表包含年,月,日,時,分,秒的值 16 | - DateType:代表包含年,月,日的值 17 | - 複雜類型 18 | - ArrayType(elementType, containsNull):代表由elementType類型元素組成的序列值。`containsNull` 用來指明 `ArrayType` 中的值是否有null值 19 | - MapType(keyType, valueType, valueContainsNull):表示包括一组鍵 - 值組合的值。透過 keyType 表示 key 資料的類型,透過 valueType 表示 value 資料的類型。`valueContainsNull` 用來指明 `MapType` 中的值是否有null值 20 | - StructType(fields):表示一個擁有 `StructFields (fields)` 序列結構的值 21 | - StructField(name, dataType, nullable):代表 `StructType` 中的域(field),field的名字通过 `name` 指定, `dataType` 指定field的資料類型, `nullable` 表示filed的值是否有null值。 22 | 23 | Spark的所有資料類型都定義在 `org.apache.spark.sql` 中,你可以透過 `import org.apache.spark.sql._` 取用它們。 24 | 25 | 資料類型 | Scala中的值類型 | 取用或者創建資料類型的API 26 | --- | --- | --- 27 | ByteType | Byte | ByteType 28 | ShortType | Short | ShortType 29 | IntegerType | Int | IntegerType 30 | LongType | Long | LongType 31 | FloatType | Float | FloatType 32 | DoubleType | Double | DoubleType 33 | DecimalType | scala.math.BigDecimal | DecimalType 34 | StringType | String | StringType 35 | BinaryType | Array[Byte] | BinaryType 36 | BooleanType | Boolean | BooleanType 37 | TimestampType | java.sql.Timestamp | TimestampType 38 | DateType | java.sql.Date | DateType 39 | ArrayType | scala.collection.Seq | ArrayType(elementType, [containsNull]) 注意 containsNull 預設為 true 40 | MapType | scala.collection.Map | MapType(keyType, valueType, [valueContainsNull]) 注意 valueContainsNull 預設為 true 41 | StructType | org.apache.spark.sql.Row | StructType(fields) ,注意 fields 是一個 StructField 序列,不能使用兩個相同名字的 StructField 42 | StructField | The value type in Scala of the data type of this field (For example, Int for a StructField with the data type IntegerType) | StructField(name, dataType, nullable) 43 | -------------------------------------------------------------------------------- /spark-sql/writing-language-integrated-relational-queries.md: -------------------------------------------------------------------------------- 1 | # [編寫語言整合(Language-Integrated)的關聯性查詢](https://spark.apache.org/docs/latest/sql-programming-guide.html#writing-language-integrated-relational-queries) 2 | 3 | **語言整合的關聯性查詢是實驗性的,現在暫時只支援scala。** 4 | 5 | Spark SQL也支持用特定領域的語法編寫查詢語句,請參考使用下列範例的資料: 6 | 7 | ```scala 8 | // sc is an existing SparkContext. 9 | val sqlContext = new org.apache.spark.sql.SQLContext(sc) 10 | // Importing the SQL context gives access to all the public SQL functions and implicit conversions. 11 | import sqlContext._ 12 | val people: RDD[Person] = ... // An RDD of case class objects, from the first example. 13 | 14 | // 下述等同於 'SELECT name FROM people WHERE age >= 10 AND age <= 19' 15 | val teenagers = people.where('age >= 10).where('age <= 19).select('name) 16 | teenagers.map(t => "Name: " + t(0)).collect().foreach(println) 17 | ``` 18 | 19 | DSL使用Scala的符號來表示在潜在表(underlying table)中的列,這些列以前缀(')標示。將這些符號透過隱式轉成由SQL執行引擎計算的表達式。你可以在[ScalaDoc](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.SchemaRDD) 20 | 中了解詳情。 21 | -------------------------------------------------------------------------------- /spark-streaming/README.md: -------------------------------------------------------------------------------- 1 | # Spark Streaming 2 | 3 | Spark streaming是Spark核心API的一個擴充,它對即時資料串流的處理具有可擴充性、高吞吐量、可容錯性等特點。我們可以從kafka、flume、Twitter、 ZeroMQ、Kinesis等來源取得資料,也可以通過由 4 | 高階函式如map、reduce、join、window等組成的複雜演算法計算出資料。最後,處理後的資料可以推送到檔案系統、資料庫、即時儀表板中。事實上,你可以將處理後的資料應用到Spark的[機器學習演算法](https://spark.apache.org/docs/latest/mllib-guide.html)、 5 | [圖形處理演算法](https://spark.apache.org/docs/latest/graphx-programming-guide.html)中去。 6 | 7 | ![Spark Streaming處理流程](../img/streaming-arch.png) 8 | 9 | 在内部,它的工作原理如下圖所示。Spark Streaming接收即時的輸入資料串流,然後將這些資料切分為批次資料供Spark引擎處理,Spark引擎將資料生成最终的结果資料。 10 | 11 | ![Spark Streaming處理原理](../img/streaming-flow.png) 12 | 13 | Spark Streaming支援一個高層的抽象類別類別,叫做離散化串流(`discretized stream`)或者`DStream`,它代表連續的資料串流。DStream既可以利用從Kafka, Flume和Kinesis等來源取得的輸入資料串流創建,也可以在其他DStream的基礎上藉由高階函式獲得。在内部,DStream是由一系列RDDs組成。 14 | 15 | 本指南指導使用者開始利用DStream編寫Spark Streaming程式。使用者能夠利用scala、java或者Python來編寫Spark Streaming程式。 16 | 17 | 注意:Spark 1.2已經為Spark Streaming導入了Python API。它的所有DStream transformations和幾乎所有的輸出操作可以在scala和java介面中使用。然而,它只支援基本的來源如純文字文件或者socket上 18 | 的文字資料。諸如flume、kafka等外部的來源的API會在將來導入。 19 | 20 | * [一個快速的例子](a-quick-example.md) 21 | * [基本概念](basic-concepts/README.md) 22 | * [連接](basic-concepts/linking.md) 23 | * [初始化StreamingContext](basic-concepts/initializing-StreamingContext.md) 24 | * [離散化串流](basic-concepts/discretized-streams.md) 25 | * [輸入DStreams](basic-concepts/input-DStreams.md) 26 | * [DStream中的轉換](basic-concepts/transformations-on-DStreams.md) 27 | * [DStream的輸出操作](basic-concepts/output-operations-on-DStreams.md) 28 | * [暫存或持續化](basic-concepts/caching-persistence.md) 29 | * [Checkpointing](basic-concepts/checkpointing.md) 30 | * [部署應用程式](basic-concepts/deploying-applications.md) 31 | * [監控應用程式](basic-concepts/monitoring-applications.md) 32 | * [性能調教](performance-tuning/README.md) 33 | * [減少執行時間](performance-tuning/reducing-processing-time.md) 34 | * [設定正確的批次大小](performance-tuning/setting-right-batch-size.md) 35 | * [記憶體調教](performance-tuning/memory-tuning.md) 36 | * [容錯語意](fault-tolerance-semantics/README.md) 37 | -------------------------------------------------------------------------------- /spark-streaming/a-quick-example.md: -------------------------------------------------------------------------------- 1 | # 一個快速的例子 2 | 3 | 在我們進入如何編寫Spark Streaming程式的細節之前,讓我們快速地瀏覽一個簡單的例子。在這個例子中,程式從監聽TCP Socket的資料伺服器取得文字資料,然後計算文字中包含的單字數。做法如下: 4 | 5 | 首先,我們導入Spark Streaming的相關類別以及一些從StreamingContext獲得的隱式轉換到我們的環境中,為我們所需的其他類別(如DStream)提供有用的函數。[StreamingContext](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.StreamingContext) 6 | 是Spark所有串流操作的主要入口。然後,我們創建了一個具有兩個執行執行緒以及1秒批次間隔時間(即以秒為單位分割資料串流)的本地StreamingContext。 7 | 8 | ```scala 9 | import org.apache.spark._ 10 | import org.apache.spark.streaming._ 11 | import org.apache.spark.streaming.StreamingContext._ 12 | // Create a local StreamingContext with two working thread and batch interval of 1 second 13 | val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount") 14 | val ssc = new StreamingContext(conf, Seconds(1)) 15 | ``` 16 | 利用這個StreamingContext,我們能夠創建一個DStream,它表示從TCP來源(主機位址localhost,port為9999)取得的資料串流。 17 | 18 | ```scala 19 | // Create a DStream that will connect to hostname:port, like localhost:9999 20 | val lines = ssc.socketTextStream("localhost", 9999) 21 | ``` 22 | 這個`lines`變數是一個DStream,表示即將從資料伺服器獲得的資料串流。這個DStream的每條紀錄都代表一行文字。下一步,我們需要將DStream中的每行文字都切分為單字。 23 | 24 | ```scala 25 | // Split each line into words 26 | val words = lines.flatMap(_.split(" ")) 27 | ``` 28 | `flatMap`是一個一對多的DStream操作,它通過把原DStream的每條紀錄都生成多條新紀錄來創建一個新的DStream。在這個例子中,每行文字都被切分成了多個單字,我們把切分 29 | 的單字串流用`words`這個DStream表示。下一步,我們需要計算單字的個數。 30 | 31 | ```scala 32 | import org.apache.spark.streaming.StreamingContext._ 33 | // Count each word in each batch 34 | val pairs = words.map(word => (word, 1)) 35 | val wordCounts = pairs.reduceByKey(_ + _) 36 | // Print the first ten elements of each RDD generated in this DStream to the console 37 | wordCounts.print() 38 | ``` 39 | `words`這個DStream被mapper(一對一轉換操作)成了一個新的DStream,它由(word,1)對組成。然後,我們就可以用這個新的DStream計算每批次資料的單字頻率。最後,我們用`wordCounts.print()` 40 | 印出每秒計算的單字頻率。 41 | 42 | 需要注意的是,當以上這些程式碼被執行時,Spark Streaming僅僅準備好了它要執行的計算,實際上並没有真正開始執行。在這些轉換操作準備好之後,要真正執行計算,需要調用以下的函數 43 | 44 | ```scala 45 | ssc.start() // Start the computation 46 | ssc.awaitTermination() // Wait for the computation to terminate 47 | ``` 48 | 完整的例子可以在[NetworkWordCount](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/NetworkWordCount.scala)中找到。 49 | 50 | 如果你已經下載和建構了Spark環境,你就能夠用以下的函數執行這個例子。首先,你需要運行Netcat作為資料伺服器 51 | 52 | ```shell 53 | $ nc -lk 9999 54 | ``` 55 | 然後,在不同的terminal,你能夠用如下方式執行例子 56 | ```shell 57 | $ ./bin/run-example streaming.NetworkWordCount localhost 9999 58 | ``` 59 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/README.md: -------------------------------------------------------------------------------- 1 | # 基本概念 2 | 3 | 在了解了簡單的例子的基礎後,接著將介紹編寫Spark Streaming應用程式必需的一些基本概念。 4 | 5 | * [連接](linking.md) 6 | * [初始化StreamingContext](initializing-StreamingContext.md) 7 | * [離散化串流](discretized-streams.md) 8 | * [輸入DStreams](input-DStreams.md) 9 | * [DStream中的轉換](transformations-on-DStreams.md) 10 | * [DStream的輸出操作](output-operations-on-DStreams.md) 11 | * [暫存或持續化](caching-persistence.md) 12 | * [Checkpointing](checkpointing.md) 13 | * [部署應用程式](deploying-applications.md) 14 | * [監控應用程式](monitoring-applications.md) -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/caching-persistence.md: -------------------------------------------------------------------------------- 1 | # 暫存或持續化 2 | 3 | 和RDD相似,DStreams也允許開發者持續化串流資料到記憶體中。在DStream上使用`persist()`函數可以自動地持續化DStream中的RDD到記憶體中。如果DStream中的資料需要計算多次,這是非常有用的。像`reduceByWindow`和`reduceByKeyAndWindow`這種窗口操作、`updateStateByKey`這種基於狀態的操作,持續化是預設的,不需要開發者調用`persist()`函數。 4 | 5 | 例如藉由網路(如kafka,flume等)獲取的輸入資料串流,預設的持續化策略是複製資料到兩個不同的節點以容錯。 6 | 7 | 注意,與RDD不同的是,DStreams預設持續化級别是儲存序列化資料到記憶體中,這將在[性能調教](../performance-tuning/README.md)章節介紹。更多的訊息請看[rdd持續化](../../programming-guide/rdds/rdd-persistences.md) -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/checkpointing.md: -------------------------------------------------------------------------------- 1 | # Checkpointing 2 | 3 | 一個串流應用程式必須全天候運行,所有必須能夠解决應用程式邏輯無關的故障(如系统錯誤,JVM崩溃等)。為了使這成為可能,Spark Streaming需要checkpoint足夠的訊息到容錯儲存系统中, 4 | 以使系统從故障中恢復。 5 | 6 | - Metadata checkpointing:保存流計算的定義訊息到容錯儲存系统如HDFS中。這用來恢復應用程式中運行worker的節點的故障。元資料包括 7 | - Configuration :創建Spark Streaming應用程式的配置訊息 8 | - DStream operations :定義Streaming應用程式的操作集合 9 | - Incomplete batches:操作存在佇列中的未完成的批次 10 | - Data checkpointing :保存生成的RDD到可靠的儲存系统中,這在有狀態transformation(如结合跨多個批次的資料)中是必須的。在這樣一個transformation中,生成的RDDDependencies於之前 11 | 批的RDD,隨着時間的推移,這個依賴鏈的長度會持續增長。在恢復的過程中,為了避免這種無限增長。有狀態的transformation的中間RDD將會定時地儲存到可靠儲存系统中,以截斷這個依賴鏈。 12 | 13 | 元資料checkpoint主要是為了從driver故障中恢復資料。如果transformation操作被用到了,資料checkpoint即使在簡單的操作中都是必須的。 14 | 15 | ## 何時checkpoint 16 | 17 | 應用程式在下面兩種情況下必須開啟checkpoint 18 | 19 | - 使用有狀態的transformation。如果在應用程式中用到了`updateStateByKey`或者`reduceByKeyAndWindow`,checkpoint目錄必需提供用以定期checkpoint RDD。 20 | - 從運行應用程式的driver的故障中恢復過來。使用元資料checkpoint恢復處理訊息。 21 | 22 | 注意,没有前述的有狀態的transformation的簡單串流應用程式在運行時可以不開啟checkpoint。在這種情況下,從driver故障的恢復將是部分恢復(接收到了但是還没有處理的資料將會丢失)。 23 | 這通常是可以接受的,許多運行的Spark Streaming應用程式都是這種方式。 24 | 25 | ## 怎樣配置Checkpointing 26 | 27 | 在容錯、可靠的檔案系統(HDFS、s3等)中設定一個目錄用於保存checkpoint訊息。可以藉由`streamingContext.checkpoint(checkpointDirectory)`函數來做。這運行之前介紹的 28 | 有狀態transformation。另外,如果你想從driver故障中恢復,你應該以下面的方式重寫你的Streaming應用程式。 29 | 30 | - 當應用程式是第一次啟動,新建一個StreamingContext,啟動所有Stream,然後調用`start()`函數 31 | - 當應用程式因為故障重新啟動,它將會從checkpoint目錄checkpoint資料重新創建StreamingContext 32 | 33 | ```scala 34 | // Function to create and setup a new StreamingContext 35 | def functionToCreateContext(): StreamingContext = { 36 | val ssc = new StreamingContext(...) // new context 37 | val lines = ssc.socketTextStream(...) // create DStreams 38 | ... 39 | ssc.checkpoint(checkpointDirectory) // set checkpoint directory 40 | ssc 41 | } 42 | 43 | // Get StreamingContext from checkpoint data or create a new one 44 | val context = StreamingContext.getOrCreate(checkpointDirectory, functionToCreateContext _) 45 | 46 | // Do additional setup on context that needs to be done, 47 | // irrespective of whether it is being started or restarted 48 | context. ... 49 | 50 | // Start the context 51 | context.start() 52 | context.awaitTermination() 53 | ``` 54 | 55 | 如果`checkpointDirectory`存在,StreamingContext將會利用checkpoint資料重新創建。如果這個目錄不存在,將會調用`functionToCreateContext`函數創建一個新的StreamingContext,建立DStreams。 56 | 請看[RecoverableNetworkWordCount](https://github.com/apache/spark/tree/master/examples/src/main/scala/org/apache/spark/examples/streaming/RecoverableNetworkWordCount.scala)例子。 57 | 58 | 除了使用`getOrCreate`,開發者必須保證在故障發生時,driver處理自動重啟。只能藉由部署運行應用程式的基礎設施來達到該目的。在部署章節將有更進一步的討論。 59 | 60 | 注意,RDD的checkpointing有儲存成本。這會導致批次資料(包含的RDD被checkpoint)的處理時間增加。因此,需要小心的設定批次處理的時間間隔。在最小的批次大小(包含1秒的資料)情況下,checkpoint每批次資料會顯著的減少 61 | 操作的吞吐量。相反,checkpointing太少會導致lineage以及任務大小增大,這會產生有害的影響。因為有狀態的transformation需要RDD checkpoint。預設的間隔時間是批次間隔時間的倍數,最少10秒。它可以藉由`dstream.checkpoint` 62 | 來設定。典型的情況下,設定checkpoint間隔是DStream的滑動間隔的5-10大小是一個好的嘗試。 63 | 64 | 65 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/custom-receiver.md: -------------------------------------------------------------------------------- 1 | # 自定義receiver指南 2 | 3 | Spark Streaming可以從包括内置資料來源在内的任意資料來源獲取資料(其他資料來源包括flume,kafka,kinesis,文件,socket等等)。這需要開發者去實作一個定製`receiver`從具體的資料來源接收 4 | 資料。本指南介紹了實作自定義`receiver`的過程,以及怎樣將`receiver`用到Spark Streaming應用程式中。 5 | 6 | ## 實作一個自定義的Receiver 7 | 8 | 這一節開始實作一個[Receiver](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.receiver.Receiver)。一個自定義的receiver必須繼承 9 | 這個抽象類別,實作它的兩個函數`onStart()`(開始接收資料)以及`onStop()`(停止接收資料)。 10 | 11 | 需要注意的是`onStart()`和`onStop()`不能夠無限期的阻塞。通常情況下,`onStart()`啟動執行緒負責資料的接收,`onStop()`確保這個接收過程停止。接收執行緒也能夠調用`receiver`的`isStopped` 12 | 函數去檢查是否已經停止接收資料。 13 | 14 | 一旦接收了資料,這些資料就能夠藉由調用`store(data)`函數存到Spark中,`store(data)`是[Receiver]中的函數。有幾個overload的`store()`函數允許你儲存接收到的資料(record-at-a-time or as whole collection of objects/serialized bytes) 15 | 16 | 在接收執行緒中出現的任何例外都應該被捕捉或者妥善處理從而避免`receiver`在没有提示的情況下失敗。`restart()`函數將會重新啟動`receiver`,它藉由非同步的方式首先調用`onStop()`函數, 17 | 然後在一段延遲之後調用`onStart()`函數。`stop()`將會調用`onStop()`函數终止`receiver`。`reportError()`函數在不停止或者重啟`receiver`的情況下印出錯誤訊息到 18 | 驅動程式(driver)。 19 | 20 | 如下所示,是一個自定義的`receiver`,它藉由socket接收文本資料串流。它用分界符'\n'把文本流分割為行紀錄,然後將它們儲存到Spark中。如果接收執行緒碰到任何連接或者接收錯誤,`receiver`將會 21 | 重新啟動以嘗試再一次連接。 22 | 23 | ```scala 24 | 25 | class CustomReceiver(host: String, port: Int) 26 | extends Receiver[String](StorageLevel.MEMORY_AND_DISK_2) with Logging { 27 | 28 | def onStart() { 29 | // Start the thread that receives data over a connection 30 | new Thread("Socket Receiver") { 31 | override def run() { receive() } 32 | }.start() 33 | } 34 | 35 | def onStop() { 36 | // There is nothing much to do as the thread calling receive() 37 | // is designed to stop by itself isStopped() returns false 38 | } 39 | 40 | //Create a socket connection and receive data until receiver is stopped 41 | private def receive() { 42 | var socket: Socket = null 43 | var userInput: String = null 44 | try { 45 | // Connect to host:port 46 | socket = new Socket(host, port) 47 | // Until stopped or connection broken continue reading 48 | val reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")) 49 | userInput = reader.readLine() 50 | while(!isStopped && userInput != null) { 51 | store(userInput) 52 | userInput = reader.readLine() 53 | } 54 | reader.close() 55 | socket.close() 56 | // Restart in an attempt to connect again when server is active again 57 | restart("Trying to connect again") 58 | } catch { 59 | case e: java.net.ConnectException => 60 | // restart if could not connect to server 61 | restart("Error connecting to " + host + ":" + port, e) 62 | case t: Throwable => 63 | // restart if there is any other error 64 | restart("Error receiving data", t) 65 | } 66 | } 67 | } 68 | 69 | ``` 70 | 71 | ## 在Spark串流應用程式中使用自定義的receiver 72 | 73 | 在Spark串流應用程式中,用`streamingContext.receiverStream()`函數,可以使用自動用`receiver`。程式碼如下所示: 74 | 75 | ```scala 76 | // Assuming ssc is the StreamingContext 77 | val customReceiverStream = ssc.receiverStream(new CustomReceiver(host, port)) 78 | val words = lines.flatMap(_.split(" ")) 79 | ... 80 | ``` 81 | 82 | 完整的程式碼見例子[CustomReceiver.scala](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/CustomReceiver.scala) 83 | 84 | ## Receiver可靠性 85 | 86 | 基於Receiver的穩定性以及容錯語意,Receiver分為兩種類型 87 | 88 | - Reliable Receiver:可靠的來源允許發送的資料被確認。一個可靠的receiver正確的響應一個可靠的來源,資料已經收到並且被正確地複製到了Spark中(指正確完成複製)。實作這個receiver並 89 | 仔細考慮來源確認的語意。 90 | - Unreliable Receiver :這些receivers不支援響應。即使對於一個可靠的來源,開發者可能實作一個非可靠的receiver,這個receiver不會正確響應。 91 | 92 | 為了實作可靠receiver,你必須使用`store(multiple-records)`去保存資料。保存的類型是阻塞訪問,即所有给定的紀錄全部保存到Spark中後才返回。如果receiver的配置儲存級别利用複製 93 | (預設情況是複製),則會在複製结束之後返回。因此,它確保資料被可靠地儲存,receiver恰當的響應给來源。這保證在複製的過程中,没有資料造成的receiver失敗。因為緩衝區資料不會響應,從而 94 | 可以從來源中重新獲取資料。 95 | 96 | 一個不可控的receiver不必實作任何這種邏輯。它簡單地從來源中接收資料,然後用`store(single-record)`一次一個地保存它們。雖然它不能用`store(multiple-records)`獲得可靠的保證, 97 | 它有下面一些優勢: 98 | 99 | - 系统注重分區塊,將資料分為適當大小的區塊。 100 | - 如果指定了速率的限制,系统注重控制接收速率。 101 | - 因為以上兩點,不可靠receiver比可靠receiver更容易實作。 102 | 103 | 下面是兩類receiver的特徵 104 | Receiver Type | Characteristics 105 | --- | --- 106 | Unreliable Receivers | 實作簡單;系统更關心區塊的生成和速率的控制;没有容錯的保證,在receiver失敗時會丢失資料 107 | Reliable Receivers | 高容錯保證,零資料丢失;區塊的生成和速率的控制需要手動實作;實作的複雜性依賴來源的確認機制 108 | 109 | ## 實作和使用自定義的基於actor的receiver 110 | 111 | 自定義的Akka actor也能夠擁有接收資料。[ActorHelper](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.receiver.ActorHelper)trait可以 112 | 應用於任何Akka actor中。它運行接收的資料藉由調用`store()`函數儲存於Spark中。可以配置這個actor的監控(supervisor)策略處理錯誤。 113 | 114 | ```scala 115 | class CustomActor extends Actor with ActorHelper { 116 | def receive = { 117 | case data: String => store(data) 118 | } 119 | } 120 | ``` 121 | 122 | 利用這個actor,一個新的輸入資料串流就能夠被創建。 123 | 124 | ```scala 125 | // Assuming ssc is the StreamingContext 126 | val lines = ssc.actorStream[String](Props(new CustomActor()), "CustomReceiver") 127 | ``` 128 | 129 | 完整的程式碼例子[ActorWordCount.scala](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/ActorWordCount.scala) 130 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/deploying-applications.md: -------------------------------------------------------------------------------- 1 | # 部署應用程式 2 | 3 | ## Requirements 4 | 5 | 運行一個Spark Streaming應用程式,有下面一些步骤 6 | 7 | - 有管理器的集群-這是任何Spark應用程式都需要的需求,詳見[部署指南](../../deploying/README.md) 8 | - 將應用程式包裝為jar檔-你必須編譯你的應用程式為jar檔。如果你用[spark-submit](../../deploying/submitting-applications.md)啟動應用程式,你不需要將Spark和Spark Streaming包裝進這個jar檔。 9 | 如果你的應用程式用到了高級來源(如kafka,flume),你需要將它們連接的外部artifact以及它們的Dependencies打包進需要部署的應用程式jar檔中。例如,一個應用程式用到了`TwitterUtils`,那麼就需要將`spark-streaming-twitter_2.10` 10 | 以及它的所有Dependencies打包到應用程式jar中。 11 | - 為executors配置足夠的記憶體-因為接收的資料必須儲存在記憶體中,executors必須配置足夠的記憶體用來保存接收的資料。注意,如果你正在做10分鐘的窗口操作,系统的記憶體要至少能保存10分鐘的資料。所以,應用程式的記憶體需求Dependencies於使用 12 | 它的操作。 13 | - 配置checkpointing-如果stream應用程式需要checkpointing,然後一個與Hadoop API兼容的容錯儲存目錄必須配置為檢查點的目錄,串流應用程式將checkpoint訊息寫入該目錄用於錯誤恢復。 14 | - 配置應用程式driver的自動重啟-為了自動從driver故障中恢復,運行串流應用程式的部署設施必須能監控driver進程,如果失敗了能夠重啟它。不同的集群管理器,有不同的工具得到該功能 15 | - Spark Standalone:一個Spark應用程式driver可以提交到Spark獨立集群運行,也就是說driver運行在一個worker節點上。進一步來看,獨立的集群管理器能夠被指示用來監控driver,並且在driver失敗(或者是由於非零的退出程式碼如exit(1), 16 | 或者由於運行driver的節點的故障)的情況下重啟driver。 17 | - YARN:YARN為自動重啟應用程式提供了類似的機制。 18 | - Mesos: Mesos可以用[Marathon](https://github.com/mesosphere/marathon)提供該功能 19 | - 配置write ahead logs-在Spark 1.2中,為了獲得极强的容錯保證,我們引入了一個新的實驗性的特性-預寫日誌(write ahead logs)。如果該特性開啟,從receiver獲取的所有資料會將預寫日誌寫入配置的checkpoint目錄。 20 | 這可以防止driver故障丢失資料,從而保證零資料丢失。這個功能可以藉由設定配置參數`spark.streaming.receiver.writeAheadLogs.enable`為true來開啟。然而,這些較强的語意可能以receiver的接收吞吐量為代价。這可以藉由 21 | 平行運行多個receiver增加吞吐量來解决。另外,當預寫日誌開啟時,Spark中的複製資料的功能推薦不用,因為該日誌已經儲存在了一個副本在儲存系统中。可以藉由設定輸入DStream的儲存級别為`StorageLevel.MEMORY_AND_DISK_SER`獲得該功能。 22 | 23 | 24 | ## 升級應用程式程式碼 25 | 26 | 如果運行的Spark Streaming應用程式需要升級,有兩種可能的函數 27 | 28 | - 啟動升級的應用程式,使其與未升級的應用程式平行運行。一旦新的程式(與就程式接收相同的資料)已經準備就緒,舊的應用程式就可以關閉。這種函數支援將資料發送到兩個不同的目的地(新程式一個,舊程式一個) 29 | - 首先,平滑的關閉(`StreamingContext.stop(...)`或`JavaStreamingContext.stop(...)`)现有的應用程式。在關閉之前,要保證已經接收的資料完全處理完。然後,就可以啟動升級的應用程式,升級 30 | 的應用程式會接着舊應用程式的點開始處理。這種函數僅支援具有來源端暫存功能的輸入來源(如flume,kafka),這是因為當舊的應用程式已經關閉,升級的應用程式還没有啟動的時候,資料需要被暫存。 31 | 32 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/discretized-streams.md: -------------------------------------------------------------------------------- 1 | # 離散化串流(DStreams) 2 | 3 | 離散化串流(Discretized Streams)或者DStreams是Spark Streaming提供的基本的抽象類別,它代表一個連續的資料串流。它要麼是從來源中獲取的輸入串流,要麼是輸入串流藉由轉換操作生成的處理後的資料串流。在内部,DStreams由一系列連續的 4 | RDD組成。DStreams中的每個RDD都包含特定時間間隔内的資料,如下圖所示: 5 | 6 | ![DStreams](../../img/streaming-dstream.png) 7 | 8 | 任何對DStreams的操作都轉換成了對DStreams隱含的RDD的操作。在前面的[例子](../a-quick-example.md)中,`flatMap`操作應用於`lines`這個DStreams的每個RDD,生成`words`這個DStreams的 9 | RDD。過程如下圖所示: 10 | 11 | ![DStreams](../../img/streaming-dstream-ops.png) 12 | 13 | 藉由Spark引擎計算這些隱含RDD的轉換操作。DStreams操作隱藏了大部分的細節,並且為了更便捷,為開發者提供了更高層的API。下面幾節將具體討論這些操作的細節。 -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/flume-integration-guide.md: -------------------------------------------------------------------------------- 1 | # flume整合指南 2 | 3 | flume是一個分散式的、穩定的、有效的服務,它能夠高效的收集、聚集以及移動大量的日誌資料。flume的架構如下圖所示。 4 | 5 | ![flume](../../img/flume.png) 6 | 7 | 本節將介紹怎樣配置flume以及Spark Streaming如何從flume中接收資料。主要有兩種函數可用。 8 | ## 函數一:基於推送的flume風格的函數 9 | 10 | flume被設計用來在flume agent間推送資料。在這個函數中,Spark Streaming本質上是建立一個`receiver`,這個`receiver`充當一個Avro代理,用於接收flume推送的資料。下面是配置的過程。 11 | 12 | ### 一般需求 13 | 14 | 在你的集群中,選擇一台滿足下面條件的機器: 15 | 16 | - 當你的flume和Spark Streaming應用程式啟動以後,必須有一個Spark worker運行在這台機器上面 17 | - 可以配置flume推送資料到這台機器的某個端口 18 | 19 | 因為是推送模式,安排好`receiver`並且監聽選中端口的串流應用程式必須是開啟的,以使flume能夠發送資料。 20 | 21 | ### 配置flume 22 | 23 | 藉由下面的配置文件配置flume agent,發送資料到一個Avro sink。 24 | 25 | ``` 26 | agent.sinks = avroSink 27 | agent.sinks.avroSink.type = avro 28 | agent.sinks.avroSink.channel = memoryChannel 29 | agent.sinks.avroSink.hostname = 30 | agent.sinks.avroSink.port = 31 | ``` 32 | 查看[flume文件](https://flume.apache.org/documentation.html)了解更多的訊息。 33 | 34 | ### 配置Spark Streaming應用程式 35 | 36 | - 連接:在你的SBT或者Maven項目定義中,引用下面的组件到串流應用程式中。 37 | ``` 38 | groupId = org.apache.spark 39 | artifactId = spark-streaming-flume_2.10 40 | version = 1.1.1 41 | ``` 42 | - 編程:在應用程式程式碼中,引入`FlumeUtils`創建輸入DStream。 43 | ``` 44 | import org.apache.spark.streaming.flume._ 45 | val flumeStream = FlumeUtils.createStream(streamingContext, [chosen machine's hostname], [chosen port]) 46 | ``` 47 | 查看[API文件](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.flume.FlumeUtils$)和[例子](https://github.com/apache/spark/tree/master/examples/src/main/scala/org/apache/spark/examples/streaming/FlumeEventCount.scala) 48 | 49 | 注意,hostname必須和集群(Mesos,YARN或者Spark Standalone)的resource manager所使用的機器的hostname是同一個,這樣就可以根據名稱分配资料來源,在正確的機器上啟動`receiver`。 50 | 51 | - 部署:將`spark-streaming-flume_2.10`和它的Dependencies(除了`spark-core_2.10`和`spark-streaming_2.10`)打包到應用程式jar檔中。然後用`spark-submit`函數啟動你的應用程式。 52 | 53 | 54 | ## 函數2:利用自定義sink的基於pull的函數 55 | 56 | 作為直接推送資料到Spark Streaming函數的替代函數,這個函數運行一個自定義的flume sink用於滿足下面兩點功能。 57 | 58 | - flume推送資料到sink,該資料被暫存在sink中。 59 | - Spark Streaming利用transactions從sink中拉取資料。只有資料收到了並且被Spark Streaming複製了之後,transactions才算成功。這使得這個函數比之前的函數具有更好的穩定性和容錯性。然而,這個函數需要flume去 60 | 運行一個自定義sink。下面是配置的過程。 61 | 62 | ### 一般需求 63 | 64 | 選擇一台機器在flume agent中運行自定義的sink,配置餘下的flume管道(pipeline)發送資料到agent中。集群中的其它機器應該能夠訪問運行自定義sink的機器。 65 | 66 | ### 配置flume 67 | 68 | 在選定的機器上面配置flume需要以下兩個步骤。 69 | 70 | - Sink Jars:添加下面的jar文件到flume的classpath目錄下面 71 | - 自定義sink jar:藉由下面的方式下載jar(或者[這裡](http://search.maven.org/remotecontent?filepath=org/apache/spark/spark-streaming-flume-sink_2.10/1.1.1/spark-streaming-flume-sink_2.10-1.1.1.jar)) 72 | ``` 73 | groupId = org.apache.spark 74 | artifactId = spark-streaming-flume-sink_2.10 75 | version = 1.1.1 76 | ``` 77 | - scala library jar:下載scala 2.10.4函式庫,你能夠藉由下面的方式下載(或者[這裡](http://search.maven.org/remotecontent?filepath=org/scala-lang/scala-library/2.10.4/scala-library-2.10.4.jar)) 78 | ``` 79 | groupId = org.scala-lang 80 | artifactId = scala-library 81 | version = 2.10.4 82 | ``` 83 | - 配置文件:藉由下面的配置文件配置flume agent用於發送資料到Avro sink。 84 | 85 | ``` 86 | agent.sinks = spark 87 | agent.sinks.spark.type = org.apache.spark.streaming.flume.sink.SparkSink 88 | agent.sinks.spark.hostname = 89 | agent.sinks.spark.port = 90 | agent.sinks.spark.channel = memoryChannel 91 | ``` 92 | 要確保配置的逆流flume管道(upstream Flume pipeline)運行這個sink發送資料到flume代理。 93 | 94 | ### 配置Spark Streaming應用程式 95 | 96 | - 連接:在你的SBT或者Maven項目定義中,引入`spark-streaming-flume_2.10`组件 97 | - 編程:在應用程式程式碼中,引入`FlumeUtils`創建輸入DStream。 98 | 99 | ``` 100 | import org.apache.spark.streaming.flume._ 101 | val flumeStream = FlumeUtils.createPollingStream(streamingContext, [sink machine hostname], [sink port]) 102 | ``` 103 | 104 | 可以查看用例[FlumePollingEventCount](https://github.com/apache/spark/tree/master/examples/src/main/scala/org/apache/spark/examples/streaming/FlumePollingEventCount.scala) 105 | 106 | 注意,每個輸入DStream都可以配置為從多個sink接收資料。 107 | 108 | - 部署:將`spark-streaming-flume_2.10`和它的Dependencies(除了`spark-core_2.10`和`spark-streaming_2.10`)打包到應用程式的jar檔中。然後用`spark-submit`函數啟動你的應用程式。 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/initializing-StreamingContext.md: -------------------------------------------------------------------------------- 1 | # 初始化StreamingContext 2 | 3 | 為了初始化Spark Streaming程式,一個StreamingContext對象必需被創建,它是Spark Streaming所有串流操作的主要入口。一個[StreamingContext](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.StreamingContext) 4 | 對象可以用[SparkConf](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.SparkConf)對象創建。 5 | 6 | ```scala 7 | import org.apache.spark._ 8 | import org.apache.spark.streaming._ 9 | val conf = new SparkConf().setAppName(appName).setMaster(master) 10 | val ssc = new StreamingContext(conf, Seconds(1)) 11 | ``` 12 | 13 | `appName`表示你的應用程式顯示在集群UI上的名字,`master`是一個[Spark、Mesos、YARN](https://spark.apache.org/docs/latest/submitting-applications.html#master-urls)集群URL 14 | 或者一個特殊字串“local[*]”,它表示程式用本地模式運行。當程式運行在集群中時,你並不希望在程式中硬編碼`master`,而是希望用`spark-submit`啟動應用程式,並從`spark-submit`中得到 15 | `master`的值。對於本地測試或者單元測試,你可以傳遞“local”字串在同一個進程内運行Spark Streaming。需要注意的是,它在内部創建了一個SparkContext對象,你可以藉由` ssc.sparkContext` 16 | 訪問這個SparkContext對象。 17 | 18 | 批次時間片需要根據你的程式的潛在需求以及集群的可用資源來設定,你可以在[性能調教](../performance-tuning/README.md)那一節獲取詳細的訊息。 19 | 20 | 可以利用已經存在的`SparkContext`對象創建`StreamingContext`對象。 21 | 22 | ```scala 23 | import org.apache.spark.streaming._ 24 | val sc = ... // existing SparkContext 25 | val ssc = new StreamingContext(sc, Seconds(1)) 26 | ``` 27 | 28 | 當一個StreamingContext(context)定義之後,你必須按照以下幾步進行操作 29 | 30 | - 定義輸入來源; 31 | - 準備好串流計算指令; 32 | - 利用`streamingContext.start()`函數接收和處理資料; 33 | - 處理過程將一直持續,直到`streamingContext.stop()`函數被調用。 34 | 35 | 幾點需要注意的地方: 36 | 37 | - 一旦一個context已經啟動,就不能有新的串流操作建立或者是添加到context中。 38 | - 一旦一個context已經停止,它就不能再重新啟動 39 | - 在JVM中,同一時間只能有一個StreamingContext处於活躍狀態 40 | - 在StreamingContext上調用`stop()`函數,也會關閉SparkContext對象。如果只想僅關閉StreamingContext對象,設定`stop()`的可選參數為false 41 | - 一個SparkContext對象可以重複利用去創建多個StreamingContext對象,前提條件是前面的StreamingContext在後面StreamingContext創建之前關閉(不關閉SparkContext)。 -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/input-DStreams.md: -------------------------------------------------------------------------------- 1 | # 輸入DStreams和receivers 2 | 3 | 輸入DStreams表示從資料來源獲取輸入資料串流的DStreams。在[快速例子](../a-quick-example.md)中,`lines`表示輸入DStream,它代表從netcat服務器獲取的資料串流。每一個輸入串流DStream 4 | 和一個`Receiver`對象相連接,這個`Receiver`從來源中獲取資料,並將資料存入記憶體中用於處理。 5 | 6 | 輸入DStreams表示從資料來源獲取的原始資料串流。Spark Streaming擁有兩類資料來源 7 | - 基本來源(Basic sources):這些來源在StreamingContext API中直接可用。例如檔案系統、socket連接、Akka的actor等。 8 | - 高級來源(Advanced sources):這些來源包括Kafka,Flume,Kinesis,Twitter等等。它們需要藉由额外的類來使用。我們在[連接](linking.md)那一節討論了類Dependencies。 9 | 10 | 需要注意的是,如果你想在一個串流應用中平行地創建多個輸入DStream來接收多個資料串流,你能夠創建多個輸入串流(這將在[性能調教](../performance-tuning/README.md)那一節介紹) 11 | 。它將創建多個Receiver同時接收多個資料串流。但是,`receiver`作為一個長期運行的任務運行在Spark worker或executor中。因此,它占有一個核,這個核是分配给Spark Streaming應用程式的所有 12 | 核中的一個(it occupies one of the cores allocated to the Spark Streaming application)。所以,為Spark Streaming應用程式分配足夠的核(如果是本地運行,那麼是執行緒) 13 | 用以處理接收的資料並且運行`receiver`是非常重要的。 14 | 15 | 幾點需要注意的地方: 16 | - 如果分配给應用程式的核的數量少於或者等於輸入DStreams或者receivers的數量,系统只能夠接收資料而不能處理它們。 17 | - 當運行在本地,如果你的master URL被設定成了“local”,這樣就只有一個核運行任務。這對程式來說是不足的,因為作為`receiver`的輸入DStream將會占用這個核,這樣就没有剩餘的核來處理資料了。 18 | 19 | ## 基本來源 20 | 21 | 我們已經在[快速例子](../a-quick-example.md)中看到,`ssc.socketTextStream(...)`函數用來把從TCPsocket獲取的文本資料創建成DStream。除了socket,StreamingContext API也支援把文件 22 | 以及Akka actors作為輸入來源創建DStream。 23 | 24 | - 檔案串流(File Streams):從任何與HDFS API兼容的檔案系統中讀取資料,一個DStream可以藉由如下方式創建 25 | 26 | ```scala 27 | streamingContext.fileStream[keyClass, valueClass, inputFormatClass](dataDirectory) 28 | ``` 29 | Spark Streaming將會監控`dataDirectory`目錄,並且處理目錄下生成的任何文件(嵌套目錄不被支援)。需要注意一下三點: 30 | 31 | 1 所有文件必須具有相同的資料格式 32 | 2 所有文件必須在`dataDirectory`目錄下創建,文件是自動的移動和重命名到資料目錄下 33 | 3 一旦移動,文件必須被修改。所以如果文件被持續的附加資料,新的資料不會被讀取。 34 | 35 | 對於簡單的文本文件,有一個更簡單的函數`streamingContext.textFileStream(dataDirectory)`可以被調用。檔案串流不需要運行一個receiver,所以不需要分配核。 36 | 37 | 在Spark1.2中,`fileStream`在Python API中不可用,只有`textFileStream`可用。 38 | 39 | - 基於自定義actor的串流:DStream可以調用`streamingContext.actorStream(actorProps, actor-name)`函數從Akka actors獲取的資料串流來創建。具體的訊息見[自定義receiver指南](https://spark.apache.org/docs/latest/streaming-custom-receivers.html#implementing-and-using-a-custom-actor-based-receiver) 40 | `actorStream`在Python API中不可用。 41 | - RDD佇列作為資料串流:為了用測試資料測試Spark Streaming應用程式,人們也可以調用`streamingContext.queueStream(queueOfRDDs)`函數基於RDD佇列創建DStreams。每個push到佇列的RDD都被 42 | 當做DStream的批次資料,像串流一樣處理。 43 | 44 | 關於從socket、文件和actor中獲取串流的更多細節,請看[StreamingContext](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.StreamingContext)和 45 | [JavaStreamingContext](https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/streaming/api/java/JavaStreamingContext.html) 46 | 47 | ## 高級來源 48 | 49 | 這類來源需要非Spark函式庫介面,並且它們中的部分還需要複雜的Dependencies(例如kafka和flume)。為了減少Dependencies的版本冲突问题,從這些來源創建DStream的功能已經被移到了獨立的函式庫中,你能在[連接](linking.md)查看 50 | 細節。例如,如果你想用來自推特的流資料創建DStream,你需要按照如下步骤操作: 51 | 52 | - 連接:添加`spark-streaming-twitter_2.10`到SBT或maven項目的Dependencies中 53 | - 編寫:導入`TwitterUtils`類,用`TwitterUtils.createStream`函數創建DStream,如下所示 54 | ```scala 55 | import org.apache.spark.streaming.twitter._ 56 | TwitterUtils.createStream(ssc) 57 | ``` 58 | - 部署:將編寫的程式以及其所有的Dependencies(包括spark-streaming-twitter_2.10的Dependencies以及它的傳遞Dependencies)包裝為jar檔,然後部署。這在[部署章節](deploying-applications.md)將會作更進一步的介紹。 59 | 60 | 需要注意的是,這些高級的來源在`spark-shell`中不能被使用,因此基於這些來源的應用程式無法在shell中測試。 61 | 62 | 下面將介紹部分的高級來源: 63 | 64 | - Twitter:Spark Streaming利用`Twitter4j 3.0.3`獲取公共的推文流,這些推文藉由[推特流API](https://dev.twitter.com/docs/streaming-apis)獲得。認證訊息可以藉由Twitter4J函式庫支援的 65 | 任何[函數](http://twitter4j.org/en/configuration.html)提供。你既能夠得到公共串流,也能夠得到基於關键字過濾後的串流。你可以查看API文件([scala](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.twitter.TwitterUtils$)和[java](https://spark.apache.org/docs/latest/api/java/index.html?org/apache/spark/streaming/twitter/TwitterUtils.html)) 66 | 和例子([TwitterPopularTags](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/TwitterPopularTags.scala)和[TwitterAlgebirdCMS](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/TwitterAlgebirdCMS.scala)) 67 | - Flume:Spark Streaming 1.2能夠從flume 1.4.0中獲取資料,可以查看[flume整合指南](flume-integration-guide.md)了解詳細訊息 68 | - Kafka:Spark Streaming 1.2能夠從kafka 0.8.0中獲取資料,可以查看[kafka整合指南](kafka-integration-guide.md)了解詳細訊息 69 | - Kinesis:查看[Kinesis整合指南](kinesis-integration.md)了解詳細訊息 70 | 71 | ## 自定義來源 72 | 73 | 在Spark 1.2中,這些來源不被Python API支援。 74 | 輸入DStream也可以藉由自定義來源創建,你需要做的是實作用戶自定義的`receiver`,這個`receiver`可以從自定義來源接收資料以及將資料推到Spark中。藉由[自定義receiver指南](custom-receiver.md)了解詳細訊息 75 | 76 | ## Receiver可靠性 77 | 78 | 基於可靠性有兩類資料來源。來源(如kafka、flume)允許。如果從這些可靠的來源獲取資料的系统能夠正確的響應所接收的資料,它就能夠確保在任何情況下不丢失資料。這樣,就有兩種類型的receiver: 79 | 80 | - Reliable Receiver:一個可靠的receiver正確的響應一個可靠的來源,資料已經收到並且被正確地複製到了Spark中。 81 | - Unreliable Receiver :這些receivers不支援響應。即使對於一個可靠的來源,開發者可能實作一個非可靠的receiver,這個receiver不會正確響應。 82 | 83 | 怎樣編寫可靠的Receiver的細節在[自定義receiver](custom-receiver.md)中有詳細介紹。 84 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/kafka-integration-guide.md: -------------------------------------------------------------------------------- 1 | # kafka整合指南 2 | 3 | [Apache kafka](http://kafka.apache.org/)是一個分散式的發布-订阅訊息系统,它可以分散式的、可分區的、可重複提交的方式讀寫日誌資料。下面我們將具體介紹Spark Streaming怎樣從kafka中 4 | 接收資料。 5 | 6 | - 連接:在你的SBT或者Maven項目定義中,引用下面的组件到串流應用程式中。 7 | 8 | ``` 9 | groupId = org.apache.spark 10 | artifactId = spark-streaming-kafka_2.10 11 | version = 1.1.1 12 | ``` 13 | 14 | - 編程:在應用程式程式碼中,引入`FlumeUtils`創建輸入DStream。 15 | 16 | ```scala 17 | import org.apache.spark.streaming.kafka._ 18 | val kafkaStream = KafkaUtils.createStream( 19 | streamingContext, [zookeeperQuorum], [group id of the consumer], [per-topic number of Kafka partitions to consume]) 20 | ``` 21 | 22 | 有兩點需要注意的地方: 23 | 24 | 1. kafka的topic分區(partition)和由Spark Streaming生成的RDD分區不相關。所以在`KafkaUtils.createStream()`函數中,增加特定topic的分區數只能夠增加單個`receiver`消費這個 25 | topic的執行緒數,不能增加Spark處理資料的並發數。 26 | 27 | 2. 藉由不同的group和topic,可以創建多個輸入DStream,從而利用多個`receiver`並發的接收資料。 28 | 29 | - 部署:將`spark-streaming-kafka_2.10`和它的Dependencies(除了`spark-core_2.10`和`spark-streaming_2.10`)打包到應用程式的jar檔中。然後用`spark-submit`函數啟動你的應用程式。 -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/kinesis-integration.md: -------------------------------------------------------------------------------- 1 | # Kinesis整合指南 2 | 3 | Amazon Kinesis是一個即時處理大規模串流資料的全托管服務。Kinesis receiver應用Kinesis客戶端函式庫(KCL)創建一個輸入DStream。KCL由Amazon 提供,它擁有Amazon 软件許可证(ASL)。KCL构建在 4 | apache 2.0許可的AWS java SDK之上,藉由Workers、檢查點(Checkpoints)和Shard Leases等概念提供了負載均衡、容錯、檢查點機制。下面將詳細介紹怎樣配置Spark Streaming從Kinesis獲取 5 | 資料。 6 | 7 | ## 配置Kinesis 8 | 9 | 一個Kinesis串流可以通用一個擁有一個或者多個shard的有效Kinesis端點(endpoint)來建立,詳情請見[指南](http://docs.aws.amazon.com/kinesis/latest/dev/step-one-create-stream.html) 10 | 11 | ## 配置Spark Streaming應用程式 12 | 13 | 1、連接:在你的SBT或者Maven項目定義中,引用下面的组件到串流應用程式中 14 | 15 | ``` 16 | groupId = org.apache.spark 17 | artifactId = spark-streaming-kinesis-asl_2.10 18 | version = 1.1.1 19 | ``` 20 | 需要注意的是,連接這個artifact,你必須將[ASL](https://aws.amazon.com/asl/)認證程式碼加入到你的應用程式中。 21 | 22 | 2、編程:在你的應用程式程式碼中,藉由引入`KinesisUtils`創建DStream 23 | 24 | ```scala 25 | import org.apache.spark.streaming.Duration 26 | import org.apache.spark.streaming.kinesis._ 27 | import com.amazonaws.services.kinesis.clientlibrary.lib.worker.InitialPositionInStream 28 | val kinesisStream = KinesisUtils.createStream( 29 | streamingContext, [Kinesis stream name], [endpoint URL], [checkpoint interval], [initial position]) 30 | ``` 31 | 32 | 可以查看[API文件](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.streaming.kinesis.KinesisUtils$)和[例子](https://github.com/apache/spark/tree/master/extras/kinesis-asl/src/main/scala/org/apache/spark/examples/streaming/KinesisWordCountASL.scala)。 33 | 34 | - streamingContext:streamingContext包含一個應用程式名稱,這個名稱連接Kinesis應用程式和Kinesis串流。 35 | - Kinesis stream name:這個串流應用程式的Kinesis串流獲取名稱滿足一下幾點: 36 | - 在流StreamingContext中使用的應用程式名稱可以作為Kinesis應用程式名稱 37 | - 對於某個地區的同一帳戶,應用程式名稱必須唯一 38 | - Kinesis的後端藉由一個DynamoDB表(一般情況下在us-east-1 region)自動的連接應用程式名稱和Kinesis串流,這個DynamoDB表由Kinesis串流初始化 39 | - 在某些情況下,改變應用程式名稱或者流名稱可能導致Kinesis錯誤,如果你發现了錯誤,你可能需要手動删除DynamoDB表 40 | - endpoint URL:合法的Kinesis endpoint URL能夠在[這裡](http://docs.aws.amazon.com/general/latest/gr/rande.html#ak_region)找到。 41 | - checkpoint interval:KCL在流中保存檢查點位置的時間間隔,對於初学者,可以將其和串流應用程式的批時間間隔設定得一致。 42 | - initial position:可以是`InitialPositionInStream.TRIM_HORIZON`也可以是`InitialPositionInStream.LATEST`(可以查看Kinesis checkpoint和Amazon Kinesis API文件了解詳細訊息) 43 | 44 | 3、部署:將`spark-streaming-kinesis-asl_2.10`和它的Dependencies(除了`spark-core_2.10`和`spark-streaming_2.10`)打包到應用程式的jar檔中。然後用`spark-submit`函數啟動你的應用程式。 45 | 46 | 在運行過程中需要注意一下幾點: 47 | 48 | - Kinesis的每個分區的資料處理都是有序的,每一条訊息至少出現一次 49 | - 多個應用程式可以從相同的Kinesis串流讀取資料,Kinesis將會保存特定程式的shard和checkpoint到DynamodDB中 50 | - 在某一時間單個的Kinesis串流shard只能被一個輸入DStream處理 51 | - 單個的Kinesis DStream藉由創建多個`KinesisRecordProcessor`執行緒,可以從Kinesis串流的多個shard中讀取資料 52 | - 分開運行在不同的processes或者instances中的多個輸入DStream能夠從Kinesis串流中讀到 53 | - Kinesis輸入DStream的數量不應比Kinesis shard的數量多,這是因為每個輸入DStream都將創建至少一個`KinesisRecordProcessor`執行緒去處理單個的shard 54 | - 藉由添加或者删除DStreams(在單個處理器或者多個processes/instance之間)可以獲得水平擴充,直到擴充到Kinesis shard的數量。 55 | - Kinesis輸入DStream將會平衡所有DStream的負載,甚至是跨processes/instance的DStream 56 | - Kinesis輸入DStream將會平衡由於變化引起的re-shard事件(合並和切分)的負載 57 | - 作為一個最佳實踐,建議避免使用過度的re-shard 58 | - 每一個Kinesis輸入DStream都包含它們自己的checkpoint訊息 59 | - Kinesis串流shard的數量與RDD分區(在Spark輸入DStream處理的過程中產生)的數量之間没有關系。它們是兩種獨立的分區模式 60 | 61 | ![Spark流Kinesis架構](../../img/streaming-kinesis-arch.png) 62 | 63 | ## 運行實例 64 | 65 | - 下載Spark 來源程式碼,然後按照下面的函數build Spark。 66 | ``` 67 | mvn -Pkinesis-asl -DskipTests clean package 68 | ``` 69 | - 在AWS中設定Kinesis串流。注意Kinesis串流的名字以及endpoint URL與流創建的地區相連接 70 | - 在你的AWS证书中設定`AWS_ACCESS_KEY_ID`和`AWS_SECRET_KEY`環境變量 71 | - 在Spark根目錄下面,運行例子 72 | 73 | ``` 74 | bin/run-example streaming.KinesisWordCountASL [Kinesis stream name] [endpoint URL] 75 | ``` 76 | 這個例子將會等待從Kinesis串流中獲取資料 77 | 78 | - 在另外一個终端,為了生成生成隨機的字串資料到Kinesis串流中,運行相關的Kinesis資料生產者 79 | ``` 80 | bin/run-example streaming.KinesisWordCountProducerASL [Kinesis stream name] [endpoint URL] 1000 10 81 | ``` 82 | 這步將會每秒推送1000行,每行带有10個隨機數字的資料到Kinesis串流中,這些資料將會被運行的例子接收和處理 83 | 84 | ## Kinesis Checkpointing 85 | 86 | - 每一個Kinesis輸入DStream定期的儲存流的當前位置到後台的DynamoDB表中。這允許系统從錯誤中恢復,继續執行DStream留下的任務。 87 | - Checkpointing太频繁將會造成AWS檢查點儲存層過載,並且可能導致AWS節流(throttling)。提供的例子藉由隨機回退重试(random-backoff-retry)策略解决這個節流问题 88 | - 當輸入DStream啟動時,如果没有Kinesis checkpoint訊息存在。它將會從最老的可用的紀錄(InitialPositionInStream.TRIM_HORIZON)或者最近的紀錄(InitialPostitionInStream.LATEST)啟動。 89 | - 如果資料添加到流中的時候還没有輸入DStream在運行,InitialPositionInStream.LATEST可能導致丢失紀錄。 90 | - InitialPositionInStream.TRIM_HORIZON可能導致紀錄的重複處理,這個錯誤的影響Dependencies於checkpoint的频率以及處理的幂等性。 91 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/linking.md: -------------------------------------------------------------------------------- 1 | # 連接 2 | 3 | 與Spark類似,Spark Streaming也可以利用maven函式庫。編寫你自己的Spark Streaming程式,你需要引入下面的Dependencies到你的SBT或者Maven項目中 4 | 5 | ```maven 6 | 7 | org.apache.spark 8 | spark-streaming_2.10 9 | 1.2 10 | 11 | ``` 12 | 為了從Kafka, Flume和Kinesis這些不在Spark核心API中提供的來源獲取資料,我們需要添加相關的模區塊`spark-streaming-xyz_2.10`到Dependencies中。例如,一些通用的组件如下表所示: 13 | 14 | Source | Artifact 15 | --- | --- 16 | Kafka | spark-streaming-kafka_2.10 17 | Flume | spark-streaming-flume_2.10 18 | Kinesis | spark-streaming-kinesis-asl_2.10 19 | Twitter | spark-streaming-twitter_2.10 20 | ZeroMQ | spark-streaming-zeromq_2.10 21 | MQTT | spark-streaming-mqtt_2.10 22 | 23 | 為了獲取最新的列表,請訪問[Apache repository](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22org.apache.spark%22%20AND%20v%3A%221.2.0%22) 24 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/monitoring-applications.md: -------------------------------------------------------------------------------- 1 | # 監控應用程式 2 | 3 | 除了Spark的監控功能,Spark Streaming增加了一些专有的功能。應用StreamingContext的時候,[Spark web UI](https://spark.apache.org/docs/latest/monitoring.html#web-interfaces) 4 | 顯示添加的`Streaming`菜單,用以顯示運行的receivers(receivers是否是存活狀態、接收的紀錄數、receiver錯誤等)和完成的批次的統計訊息(批次處理時間、佇列等待等等)。這可以用來監控 5 | 串流應用程式的處理過程。 6 | 7 | 在WEB UI中的`Processing Time`和`Scheduling Delay`兩個度量指標是非常重要的。第一個指標表示批次資料處理的時間,第二個指標表示前面的批次處理完畢之後,當前批在佇列中的等待時間。如果 8 | 批次處理時間比批次間隔時間持續更長或者佇列等待時間持續增加,這就預示系统無法以批次資料產生的速度處理這些資料,整個處理過程滯後了。在這種情況下,考慮減少批次處理時間。 9 | 10 | Spark Streaming程式的處理過程也可以藉由[StreamingListener](https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.scheduler.StreamingListener)介面來監控,這 11 | 個介面允許你獲得receiver狀態和處理時間。注意,這個介面是開發者API,它有可能在未來提供更多的訊息。 -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/output-operations-on-DStreams.md: -------------------------------------------------------------------------------- 1 | # DStreams上的輸出操作 2 | 3 | 輸出操作允許DStream的操作推到如資料函式庫、檔案系統等外部系统中。因為輸出操作實际上是允許外部系统消費轉換後的資料,它們触發的實际操作是DStream轉換。目前,定義了下面幾種輸出操作: 4 | 5 | Output Operation | Meaning 6 | --- | --- 7 | print() | 在DStream的每個批次資料中印出前10条元素,這個操作在開發和調试中都非常有用。在Python API中調用`pprint()`。 8 | saveAsObjectFiles(prefix, [suffix]) | 保存DStream的内容為一個序列化的文件`SequenceFile`。每一個批間隔的文件的文件名基於`prefix`和`suffix`生成。"prefix-TIME_IN_MS[.suffix]",在Python API中不可用。 9 | saveAsTextFiles(prefix, [suffix]) | 保存DStream的内容為一個文本文件。每一個批間隔的文件的文件名基於`prefix`和`suffix`生成。"prefix-TIME_IN_MS[.suffix]" 10 | saveAsHadoopFiles(prefix, [suffix]) | 保存DStream的内容為一個hadoop文件。每一個批間隔的文件的文件名基於`prefix`和`suffix`生成。"prefix-TIME_IN_MS[.suffix]",在Python API中不可用。 11 | foreachRDD(func) | 在從流中生成的每個RDD上應用函數`func`的最通用的輸出操作。這個函數應該推送每個RDD的資料到外部系统,例如保存RDD到文件或者藉由網路寫到資料函式庫中。需要注意的是,`func`函數在驅動程式中執行,並且通常都有RDD action在裡面推動RDD流的計算。 12 | 13 | ## 利用foreachRDD的設計模式 14 | 15 | dstream.foreachRDD是一個强大的原语,發送資料到外部系统中。然而,明白怎樣正確地、有效地用這個原语是非常重要的。下面幾點介紹了如何避免一般錯誤。 16 | - 经常寫資料到外部系统需要建一個連接對象(例如到远程服務器的TCP連接),用它發送資料到远程系统。為了達到這個目的,開發人员可能不经意的在Spark驱動中創建一個連接對象,但是在Spark worker中 17 | 嘗試調用這個連接對象保存紀錄到RDD中,如下: 18 | 19 | ```scala 20 | dstream.foreachRDD(rdd => { 21 | val connection = createNewConnection() // executed at the driver 22 | rdd.foreach(record => { 23 | connection.send(record) // executed at the worker 24 | }) 25 | }) 26 | ``` 27 | 28 | 這是不正確的,因為這需要先序列化連接對象,然後將它從driver發送到worker中。這樣的連接對象在機器之間不能传送。它可能表现為序列化錯誤(連接對象不可序列化)或者初始化錯誤(連接對象應該 29 | 在worker中初始化)等等。正確的解决办法是在worker中創建連接對象。 30 | 31 | - 然而,這會造成另外一個常見的錯誤-為每一個紀錄創建了一個連接對象。例如: 32 | 33 | ``` 34 | dstream.foreachRDD(rdd => { 35 | rdd.foreach(record => { 36 | val connection = createNewConnection() 37 | connection.send(record) 38 | connection.close() 39 | }) 40 | }) 41 | ``` 42 | 43 | 通常,創建一個連接對象有資源和時間的開支。因此,為每個紀錄創建和销毁連接對象會導致非常高的開支,明顯的減少系统的整體吞吐量。一個更好的解决办法是利用`rdd.foreachPartition`函數。 44 | 為RDD的partition創建一個連接對象,用這個兩件對象發送partition中的所有紀錄。 45 | 46 | ``` 47 | dstream.foreachRDD(rdd => { 48 | rdd.foreachPartition(partitionOfRecords => { 49 | val connection = createNewConnection() 50 | partitionOfRecords.foreach(record => connection.send(record)) 51 | connection.close() 52 | }) 53 | }) 54 | ``` 55 | 這就將連接對象的創建開销分摊到了partition的所有紀錄上了。 56 | 57 | - 最後,可以藉由在多個RDD或者批次資料間重用連接對象做更進一步的優化。開發者可以保有一個静态的連接對象池,重複使用池中的對象將多批次的RDD推送到外部系统,以進一步節省開支。 58 | 59 | ``` 60 | dstream.foreachRDD(rdd => { 61 | rdd.foreachPartition(partitionOfRecords => { 62 | // ConnectionPool is a static, lazily initialized pool of connections 63 | val connection = ConnectionPool.getConnection() 64 | partitionOfRecords.foreach(record => connection.send(record)) 65 | ConnectionPool.returnConnection(connection) // return to the pool for future reuse 66 | }) 67 | }) 68 | ``` 69 | 70 | 需要注意的是,池中的連接對象應該根據需要延遲創建,並且在空闲一段時間後自動超時。這樣就獲取了最有效的方式發生資料到外部系统。 71 | 72 | 其它需要注意的地方: 73 | 74 | - 輸出操作藉由懒執行的方式操作DStreams,正如RDD action藉由懒執行的方式操作RDD。具體地看,RDD actions和DStreams輸出操作接收資料的處理。因此,如果你的應用程式没有任何輸出操作或者 75 | 用於輸出操作`dstream.foreachRDD()`,但是没有任何RDD action操作在`dstream.foreachRDD()`裡面,那麼什麼也不會執行。系统僅僅會接收輸入,然後丢棄它們。 76 | - 預設情況下,DStreams輸出操作是分時執行的,它們按照應用程式的定義順序按序執行。 77 | 78 | 79 | -------------------------------------------------------------------------------- /spark-streaming/basic-concepts/transformations-on-DStreams.md: -------------------------------------------------------------------------------- 1 | # DStream中的轉換(transformation) 2 | 3 | 和RDD類似,transformation允許從輸入DStream來的資料被修改。DStreams支援很多在RDD中可用的transformation操作。一些常用的操作如下所示: 4 | 5 | Transformation | Meaning 6 | --- | --- 7 | map(func) | 利用函數`func`處理原DStream的每個元素,返回一個新的DStream 8 | flatMap(func) | 與map相似,但是每個輸入項可用被映射為0個或者多個輸出項 9 | filter(func) | 返回一個新的DStream,它僅僅包含來源DStream中滿足函數func的項 10 | repartition(numPartitions) | 藉由創建更多或者更少的partition改變這個DStream的平行級别(level of parallelism) 11 | union(otherStream) | 返回一個新的DStream,它包含來源DStream和otherStream的联合元素 12 | count() | 藉由計算來源DStream中每個RDD的元素數量,返回一個包含單元素(single-element)RDDs的新DStream 13 | reduce(func) | 利用函數func聚集來源DStream中每個RDD的元素,返回一個包含單元素(single-element)RDDs的新DStream。函數應該是相連接的,以使計算可以平行化 14 | countByValue() | 這個操作應用於元素類型為K的DStream上,返回一個(K,long)對的新DStream,每個键的值是在原DStream的每個RDD中的频率。 15 | reduceByKey(func, [numTasks]) | 當在一個由(K,V)對組成的DStream上調用這個操作,返回一個新的由(K,V)對組成的DStream,每一個key的值均由给定的reduce函數聚集起來。注意:在預設情況下,這個操作利用了Spark預設的並發任務數去分组。你可以用`numTasks`參數設定不同的任務數 16 | join(otherStream, [numTasks]) | 當應用於兩個DStream(一個包含(K,V)對,一個包含(K,W)對),返回一個包含(K, (V, W))對的新DStream 17 | cogroup(otherStream, [numTasks]) | 當應用於兩個DStream(一個包含(K,V)對,一個包含(K,W)對),返回一個包含(K, Seq[V], Seq[W])的元组 18 | transform(func) | 藉由對來源DStream的每個RDD應用RDD-to-RDD函數,創建一個新的DStream。這個可以在DStream中的任何RDD操作中使用 19 | updateStateByKey(func) | 利用给定的函數更新DStream的狀態,返回一個新"state"的DStream。 20 | 21 | 最後兩個transformation操作需要重點介紹一下: 22 | 23 | ## UpdateStateByKey操作 24 | 25 | 26 | updateStateByKey操作允許不斷用新訊息更新它的同時保持任意狀態。你需要藉由兩步來使用它 27 | 28 | - 定義狀態-狀態可以是任何的資料類型 29 | - 定義狀態更新函數-怎樣利用更新前的狀態和從輸入串流裡面獲取的新值更新狀態 30 | 31 | 讓我們舉個例子說明。在例子中,你想保持一個文本資料串流中每個單字的運行次數,運行次數用一個state表示,它的類型是整數 32 | 33 | ```scala 34 | def updateFunction(newValues: Seq[Int], runningCount: Option[Int]): Option[Int] = { 35 | val newCount = ... // add the new values with the previous running count to get the new count 36 | Some(newCount) 37 | } 38 | ``` 39 | 40 | 這個函數被用到了DStream包含的單字上 41 | 42 | ```scala 43 | import org.apache.spark._ 44 | import org.apache.spark.streaming._ 45 | import org.apache.spark.streaming.StreamingContext._ 46 | // Create a local StreamingContext with two working thread and batch interval of 1 second 47 | val conf = new SparkConf().setMaster("local[2]").setAppName("NetworkWordCount") 48 | val ssc = new StreamingContext(conf, Seconds(1)) 49 | // Create a DStream that will connect to hostname:port, like localhost:9999 50 | val lines = ssc.socketTextStream("localhost", 9999) 51 | // Split each line into words 52 | val words = lines.flatMap(_.split(" ")) 53 | // Count each word in each batch 54 | val pairs = words.map(word => (word, 1)) 55 | val runningCounts = pairs.updateStateByKey[Int](updateFunction _) 56 | ``` 57 | 58 | 更新函數將會被每個單字調用,`newValues`擁有一系列的1(從 (词, 1)對而來),runningCount擁有之前的次數。要看完整的程式碼,見[例子](https://github.com/apache/spark/blob/master/examples/src/main/scala/org/apache/spark/examples/streaming/StatefulNetworkWordCount.scala) 59 | 60 | ## Transform操作 61 | 62 | `transform`操作(以及它的變化形式如`transformWith`)允許在DStream運行任何RDD-to-RDD函數。它能夠被用來應用任何没在DStream API中提供的RDD操作(It can be used to apply any RDD operation that is not exposed in the DStream API)。 63 | 例如,連接資料串流中的每個批(batch)和另外一個資料集的功能並没有在DStream API中提供,然而你可以簡單的利用`transform`函數做到。如果你想藉由連接带有預先計算的垃圾邮件訊息的輸入資料串流 64 | 來清理即時資料,然後過了它們,你可以按如下函數來做: 65 | 66 | ```scala 67 | val spamInfoRDD = ssc.sparkContext.newAPIHadoopRDD(...) // RDD containing spam information 68 | 69 | val cleanedDStream = wordCounts.transform(rdd => { 70 | rdd.join(spamInfoRDD).filter(...) // join data stream with spam information to do data cleaning 71 | ... 72 | }) 73 | ``` 74 | 75 | 事實上,你也可以在`transform`函數中用[機器学习](https://spark.apache.org/docs/latest/mllib-guide.html)和[圖計算](https://spark.apache.org/docs/latest/graphx-programming-guide.html)算法 76 | 77 | ## 窗口(window)操作 78 | 79 | Spark Streaming也支援窗口計算,它允許你在一個滑動窗口資料上應用transformation操作。下圖阐明了這個滑動窗口。 80 | 81 | ![滑動窗口](../../img/streaming-dstream-window.png) 82 | 83 | 如上圖顯示,窗口在來源DStream上滑動,合並和操作落入窗内的來源RDDs,產生窗口化的DStream的RDDs。在這個具體的例子中,程式在三個時間單元的資料上進行窗口操作,並且每兩個時間單元滑動一次。 84 | 這說明,任何一個窗口操作都需要指定兩個參數: 85 | 86 | - 窗口長度:窗口的持續時間(圖中為3) 87 | - 滑動時間間隔:窗口操作執行的時間間隔(圖中為2) 88 | 89 | 這兩個參數必須是來源DStream的批時間間隔的倍數(圖中為1)。 90 | 91 | 下面舉例說明窗口操作。例如,你想擴充前面的[例子](../a-quick-example.md)用來計算過去30秒的词频,間隔時間是10秒。為了達到這個目的,我們必須在過去30秒的`pairs` DStream上應用`reduceByKey` 92 | 操作。用函數`reduceByKeyAndWindow`實作。 93 | 94 | ```scala 95 | // Reduce last 30 seconds of data, every 10 seconds 96 | val windowedWordCounts = pairs.reduceByKeyAndWindow((a:Int,b:Int) => (a + b), Seconds(30), Seconds(10)) 97 | ``` 98 | 99 | 一些常用的窗口操作如下所示,這些操作都需要用到上文提到的兩個參數:窗口長度和滑動的時間間隔 100 | 101 | Transformation | Meaning 102 | --- | --- 103 | window(windowLength, slideInterval) | 基於來源DStream產生的窗口化的批次資料計算一個新的DStream 104 | countByWindow(windowLength, slideInterval) | 返回流中元素的一個滑動窗口數 105 | reduceByWindow(func, windowLength, slideInterval) | 返回一個單元素流。利用函數func聚集滑動時間間隔的流的元素創建這個單元素流。函數必須是相連接的以使計算能夠正確的平行計算。 106 | reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) | 應用到一個(K,V)對組成的DStream上,返回一個由(K,V)對組成的新的DStream。每一個key的值均由给定的reduce函數聚集起來。注意:在預設情況下,這個操作利用了Spark預設的並發任務數去分组。你可以用`numTasks`參數設定不同的任務數 107 | reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) | A more efficient version of the above reduceByKeyAndWindow() where the reduce value of each window is calculated incrementally using the reduce values of the previous window. This is done by reducing the new data that enter the sliding window, and "inverse reducing" the old data that leave the window. An example would be that of "adding" and "subtracting" counts of keys as the window slides. However, it is applicable to only "invertible reduce functions", that is, those reduce functions which have a corresponding "inverse reduce" function (taken as parameter invFunc. Like in reduceByKeyAndWindow, the number of reduce tasks is configurable through an optional argument. 108 | countByValueAndWindow(windowLength, slideInterval, [numTasks]) | 應用到一個(K,V)對組成的DStream上,返回一個由(K,V)對組成的新的DStream。每個key的值都是它們在滑動窗口中出現的频率。 109 | -------------------------------------------------------------------------------- /spark-streaming/fault-tolerance-semantics/README.md: -------------------------------------------------------------------------------- 1 | # 容錯語意 2 | 3 | 這一節,我們將討論在節點錯誤事件時Spark Streaming的行為。為了理解這些,讓我們先記住一些Spark RDD的基本容錯語意。 4 | 5 | - 一個RDD是不可變的、確定可重複計算的、分散式資料集。每個RDD記住一個確定性操作的lineage(lineage),這個lineage用在容錯的輸入資料集上來創建該RDD。 6 | - 如果任何一個RDD的分區因為節點故障而丢失,這個分區可以藉由操作lineage從來源容錯的資料集中重新計算得到。 7 | - 假定所有的RDD transformations是確定的,那麼最终轉換的資料是一樣的,不論Spark機器中發生何種錯誤。 8 | 9 | Spark運行在像HDFS或S3等容錯系统的資料上。因此,任何從容錯資料而來的RDD都是容錯的。然而,這不是在Spark Streaming的情況下,因為Spark Streaming的資料大部分情況下是從 10 | 網路中得到的。為了獲得生成的RDD相同的容錯属性,接收的資料需要重複保存在worker node的多個Spark executor上(預設的複製因子是2),這導致了當出現錯誤事件時,有兩類資料需要被恢復 11 | 12 | - Data received and replicated :在單個worker節點的故障中,這個資料會幸存下來,因為有另外一個節點保存有這個資料的副本。 13 | - Data received but buffered for replication:因為没有重複保存,所以為了恢復資料,唯一的办法是從來源中重新讀取資料。 14 | 15 | 有兩種錯誤我們需要關心 16 | 17 | - worker節點故障:任何運行executor的worker節點都有可能出故障,那樣在這個節點中的所有記憶體資料都會丢失。如果有任何receiver運行在錯誤節點,它們的暫存資料將會丢失 18 | - Driver節點故障:如果運行Spark Streaming應用程式的Driver節點出現故障,很明顯SparkContext將會丢失,所有執行在其上的executors也會丢失。 19 | 20 | ## 作為輸入來源的文件語意(Semantics with files as input source) 21 | 22 | 如果所有的輸入資料都存在於一個容錯的檔案系統如HDFS,Spark Streaming總可以從任何錯誤中恢復並且執行所有資料。這给出了一個恰好一次(exactly-once)語意,即無論發生什麼故障, 23 | 所有的資料都將會恰好處理一次。 24 | 25 | ## 基於receiver的輸入來源語意 26 | 27 | 對於基於receiver的輸入來源,容錯的語意既Dependencies於故障的情形也Dependencies於receiver的類型。正如之前討論的,有兩種類型的receiver 28 | 29 | - Reliable Receiver:這些receivers只有在確保資料複製之後才會告知可靠來源。如果這樣一個receiver失敗了,緩衝區(非複製)資料不會被來源所承認。如果receiver重啟,來源會重發數 30 | 据,因此不會丢失資料。 31 | - Unreliable Receiver:當worker或者driver節點故障,這種receiver會丢失資料 32 | 33 | 選擇哪種類型的receiverDependencies於這些語意。如果一個worker節點出現故障,Reliable Receiver不會丢失資料,Unreliable Receiver會丢失接收了但是没有複製的資料。如果driver節點 34 | 出現故障,除了以上情況下的資料丢失,所有過去接收並複製到記憶體中的資料都會丢失,這會影響有狀態transformation的结果。 35 | 36 | 為了避免丢失過去接收的資料,Spark 1.2引入了一個實驗性的特徵`write ahead logs`,它保存接收的資料到容錯儲存系统中。有了`write ahead logs`和Reliable Receiver,我們可以 37 | 做到零資料丢失以及exactly-once語意。 38 | 39 | 下面的表格總结了錯誤語意: 40 | 41 | Deployment Scenario | Worker Failure | Driver Failure 42 | --- | --- | --- 43 | Spark 1.1 或者更早, 没有write ahead log的Spark 1.2 | 在Unreliable Receiver情況下緩衝區資料丢失;在Reliable Receiver和文件的情況下,零資料丢失 | 在Unreliable Receiver情況下緩衝區資料丢失;在所有receiver情況下,過去的資料丢失;在文件的情況下,零資料丢失 44 | 带有write ahead log的Spark 1.2 | 在Reliable Receiver和文件的情況下,零資料丢失 | 在Reliable Receiver和文件的情況下,零資料丢失 45 | 46 | ## 輸出操作的語意 47 | 48 | 根據其確定操作的lineage,所有資料都被建模成了RDD,所有的重新計算都會產生同樣的结果。所有的DStream transformation都有exactly-once語意。那就是說,即使某個worker節點出現 49 | 故障,最终的轉換结果都是一樣。然而,輸出操作(如`foreachRDD`)具有`at-least once`語意,那就是說,在有worker事件故障的情況下,變换後的資料可能被寫入到一個外部實體不止一次。 50 | 利用`saveAs***Files`將資料保存到HDFS中的情況下,以上寫多次是能夠被接受的(因為文件會被相同的資料覆盖)。 -------------------------------------------------------------------------------- /spark-streaming/performance-tuning/README.md: -------------------------------------------------------------------------------- 1 | # 性能調教 2 | 3 | 集群中的Spark Streaming應用程式獲得最好的性能需要一些調整。這章將介紹幾個參數和配置,提高Spark Streaming應用程式的性能。你需要考慮兩件事情: 4 | 5 | - 高效地利用集群資料來源減少批次資料的處理時間 6 | - 設定正確的批次大小(size),使資料的處理速度能夠赶上資料的接收速度 7 | 8 | * [減少批次資料的執行時間](reducing-processing-time.md) 9 | * [設定正確的批次大小](setting-right-batch-size.md) 10 | * [記憶體調教](memory-tuning.md) -------------------------------------------------------------------------------- /spark-streaming/performance-tuning/memory-tuning.md: -------------------------------------------------------------------------------- 1 | # 記憶體調優 2 | 3 | 調整記憶體的使用以及Spark應用程式的垃圾回收行為已經在[Spark優化指南](../../other/tuning-spark.md)中詳細介紹。在這一節,我們重點介紹幾個强烈推薦的自定義選項,它們可以 4 | 減少Spark Streaming應用程式垃圾回收的相關暫停,獲得更穩定的批次處理時間。 5 | 6 | - Default persistence level of DStreams:和RDDs不同的是,預設的持續化級别是序列化資料到記憶體中(DStream是`StorageLevel.MEMORY_ONLY_SER`,RDD是` StorageLevel.MEMORY_ONLY`)。 7 | 即使保存資料為序列化形态會增加序列化/反序列化的開销,但是可以明顯的減少垃圾回收的暫停。 8 | - Clearing persistent RDDs:預設情況下,藉由Spark内置策略(LUR),Spark Streaming生成的持續化RDD將會從記憶體中清理掉。如果spark.cleaner.ttl已經設定了,比這個時間存在更老的持續化 9 | RDD將會被定時的清理掉。正如前面提到的那樣,這個值需要根據Spark Streaming應用程式的操作小心設定。然而,可以設定配置選項`spark.streaming.unpersist`為true來更智能的去持續化(unpersist)RDD。這個 10 | 配置使系统找出那些不需要经常保有的RDD,然後去持續化它們。這可以減少Spark RDD的記憶體使用,也可能改善垃圾回收的行為。 11 | - Concurrent garbage collector:使用並發的標記-清除垃圾回收可以進一步減少垃圾回收的暫停時間。盡管並發的垃圾回收會減少系统的整體吞吐量,但是仍然推薦使用它以獲得更穩定的批次處理時間。 -------------------------------------------------------------------------------- /spark-streaming/performance-tuning/reducing-processing-time.md: -------------------------------------------------------------------------------- 1 | # 減少批次資料的執行時間 2 | 3 | 在Spark中有幾個優化可以減少批次處理的時間。這些可以在[優化指南](../../other/tuning-spark.md)中作了討論。這節重點討論幾個重要的。 4 | 5 | ## 資料接收的平行化程度 6 | 7 | 藉由網路(如kafka,flume,socket等)接收資料需要這些資料反序列化並被保存到Spark中。如果資料接收成為系统的瓶頸,就要考慮平行地接收資料。注意,每個輸入DStream創建一個`receiver`(運行在worker機器上) 8 | 接收單個資料串流。創建多個輸入DStream並配置它們可以從來源中接收不同分區的資料串流,從而實作多資料串流接收。例如,接收兩個topic資料的單個輸入DStream可以被切分為兩個kafka輸入串流,每個接收一個topic。這將 9 | 在兩個worker上運行兩個`receiver`,因此允許資料平行接收,提高整體的吞吐量。多個DStream可以被合並生成單個DStream,這樣運用在單個輸入DStream的transformation操作可以運用在合並的DStream上。 10 | 11 | ```scala 12 | val numStreams = 5 13 | val kafkaStreams = (1 to numStreams).map { i => KafkaUtils.createStream(...) } 14 | val unifiedStream = streamingContext.union(kafkaStreams) 15 | unifiedStream.print() 16 | ``` 17 | 18 | 另外一個需要考慮的參數是`receiver`的阻塞時間。對於大部分的`receiver`,在存入Spark記憶體之前,接收的資料都被合並成了一個大資料區塊。每批次資料中區塊的個數決定了任務的個數。這些任務是用類 19 | 似map的transformation操作接收的資料。阻塞間隔由配置參數`spark.streaming.blockInterval`決定,預設的值是200毫秒。 20 | 21 | 多輸入串流或者多`receiver`的可選的函數是明確地重新分配輸入資料串流(利用`inputStream.repartition()`),在進一步操作之前,藉由集群的機器數分配接收的批次資料。 22 | 23 | ## 資料處理的平行化程度 24 | 25 | 如果運行在計算stage上的並發任務數不足夠大,就不會充分利用集群的資料來源。例如,對於分散式reduce操作如`reduceByKey`和`reduceByKeyAndWindow`,預設的並發任務數藉由配置属性來確定(configuration.html#spark-properties) 26 | `spark.default.parallelism`。你可以藉由參數(`PairDStreamFunctions` (api/scala/index.html#org.apache.spark.streaming.dstream.PairDStreamFunctions))傳遞平行度,或者設定參數 27 | `spark.default.parallelism`修改預設值。 28 | 29 | ## 資料序列化 30 | 31 | 資料序列化的總開销是平常大的,特别是當sub-second級的批次資料被接收時。下面有兩個相關點: 32 | 33 | - Spark中RDD資料的序列化。關於資料序列化請参照[Spark優化指南](../../other/tuning-spark.md)。注意,與Spark不同的是,預設的RDD會被持續化為序列化的位元組陣列,以減少與垃圾回收相關的暫停。 34 | - 輸入資料的序列化。從外部獲取資料存到Spark中,獲取的byte資料需要從byte反序列化,然後再按照Spark的序列化格式重新序列化到Spark中。因此,輸入資料的反序列化花費可能是一個瓶頸。 35 | 36 | ## 任務的啟動開支 37 | 38 | 每秒鐘啟動的任務數是非常大的(50或者更多)。發送任務到slave的花費明顯,這使請求很難獲得亞秒(sub-second)級别的反應。藉由下面的改變可以減小開支 39 | 40 | - 任務序列化。運行kyro序列化任何可以減小任務的大小,從而減小任務發送到slave的時間。 41 | - 執行模式。在Standalone模式下或者粗粒度的Mesos模式下運行Spark可以在比細粒度Mesos模式下運行Spark獲得更短的任務啟動時間。可以在[在Mesos下運行Spark](../../deploying/running-spark-on-mesos.md)中獲取更多訊息。 42 | 43 | These changes may reduce batch processing time by 100s of milliseconds, thus allowing sub-second batch size to be viable. 44 | 45 | -------------------------------------------------------------------------------- /spark-streaming/performance-tuning/setting-right-batch-size.md: -------------------------------------------------------------------------------- 1 | # 設定正確的批次大小 2 | 3 | 為了Spark Streaming應用程式能夠在集群中穩定運行,系统應該能夠以足夠的速度處理接收的資料(即處理速度應該大於或等於接收資料的速度)。這可以藉由串流的網路UI觀察得到。批次處理時間應該小於批次間隔時間。 4 | 5 | 根據串流計算的性質,批次間隔時間可能顯著的影響資料處理速率,這個速率可以藉由應用程式維持。可以考慮`WordCountNetwork`這個例子,對於一個特定的資料處理速率,系统可能可以每2秒印出一次單字計數 6 | (批次間隔時間為2秒),但無法每500毫秒印出一次單字計數。所以,為了在生產環境中維持期望的資料處理速率,就應該設定合適的批次間隔時間(即批次資料的容量)。 7 | 8 | 找出正確的批次大小的一個好的办法是用一個保守的批次間隔時間(5-10,秒)和低資料速率來測試你的應用程式。為了驗證你的系统是否能滿足資料處理速率,你可以藉由檢查點到點的延遲值來判斷(可以在 9 | Spark驅動程式的log4j日誌中查看"Total delay"或者利用StreamingListener介面)。如果延遲維持穩定,那麼系统是穩定的。如果延遲持續增長,那麼系统無法跟上資料處理速率,是不穩定的。 10 | 你能夠嘗試着增加資料處理速率或者減少批次大小來作進一步的測試。注意,因為瞬間的資料處理速度增加導致延遲瞬間的增長可能是正常的,只要延遲能重新回到了低值(小於批次大小)。 11 | 12 | -------------------------------------------------------------------------------- /translation.json: -------------------------------------------------------------------------------- 1 | { 2 | "編程語言": "程式語言", 3 | "代碼": "程式碼", 4 | "屏幕": "螢幕", 5 | "打印": "列印", 6 | "實現": "實作", 7 | "程序": "程式", 8 | "字符串": "字串", 9 | "字符": "字元", 10 | "全局": "全域", 11 | "函數": "函式", 12 | "編程例子": "程式範例", 13 | "例子": "範例", 14 | "實時": "即時", 15 | "運行": "執行", 16 | "聲明": "宣告", 17 | "常量": "常數", 18 | "變量": "變數", 19 | "類型": "型別", 20 | "信息": "資訊", 21 | "反斜槓": "反斜線", 22 | "數組": "陣列", 23 | "枚舉": "列舉", 24 | "訪問": "存取", 25 | "循環": "迴圈", 26 | "迴圈體": "迴圈", 27 | "布爾": "布林", 28 | "支持": "支援", 29 | "重復": "重複", 30 | "標准": "標準", 31 | "調用": "呼叫", 32 | "返回": "回傳", 33 | "復雜": "複雜", 34 | "回调": "回呼", 35 | "對象": "物件", 36 | "對像": "物件", 37 | "析購": "解構", 38 | "默認": "預設", 39 | "結構體": "結構", 40 | "服務器": "伺服器", 41 | "協議": "協定", 42 | "尖括號": "角括號", 43 | "運算符": "運算子", 44 | "操作符": "運算子", 45 | "常規": "常見", 46 | "邏輯與": "邏輯且", 47 | "取余": "取餘", 48 | "求余": "取餘", 49 | "余數": "餘數", 50 | "前置": "前綴", 51 | "中置": "中綴", 52 | "後置": "後綴", 53 | "操作數": "運算元", 54 | "自增": "累加", 55 | "自減": "累減", 56 | "復合": "複合", 57 | "轉義": "跳脫", 58 | "轉移特殊": "跳脫", 59 | "水平制表符": "水平 tab", 60 | "換行符": "換行", 61 | "回車符": "回車", 62 | "標量": "純量", 63 | "字節": "位元組", 64 | "文本文件": "程式本文檔", 65 | "文本": "文字", 66 | "兼容": "相容", 67 | "21位": "21 位元", 68 | "8位程式碼": "8 位元代碼", 69 | "16位程式碼": "16 位元代碼", 70 | "程式碼單元": "代碼單元", 71 | "無符號8位": "無號 8 位元", 72 | "無符號16位": "無號 16 位元", 73 | "存儲": "儲存", 74 | "數據": "資料", 75 | "盡管": "儘管", 76 | "構造語句": "建構語法", 77 | "陣列構造": "陣列建構", 78 | "構造陣列": "建構陣列", 79 | "構造": "建構", 80 | "只讀": "唯讀", 81 | "准備": "準備", 82 | "標識符": "識別符號", 83 | "哈希": "雜湊", 84 | "散列表": "雜湊表", 85 | "復制": "複製", 86 | "下劃線": "底線", 87 | "游戲": "遊戲", 88 | "用戶": "使用者", 89 | "消息": "訊息", 90 | "元音": "母音", 91 | "輔音": "子音", 92 | "程式碼塊": "程式碼區塊", 93 | "准確": "準確", 94 | "井號": "井字號", 95 | "調試器": "除錯器", 96 | "解析器": "直譯器", 97 | "附注": "附註", 98 | "令牌": "語彙單元", 99 | "注釋": "註解", 100 | "源文件": "原始碼", 101 | "子串": "子字串", 102 | "采用": "採用", 103 | "軟件": "軟體", 104 | "內存": "記憶體", 105 | "框架棧": "框架", 106 | "輕松": "輕鬆", 107 | "模塊": "模組", 108 | "過程式編程": "程序式程式設計", 109 | "面向物件編程": "物件導向程式設計", 110 | "程式員": "程式設計師", 111 | "建構器": "建構函式", 112 | "析構器": "解構函式", 113 | "高級運算子": "進階運算子", 114 | "溢出": "溢位", 115 | "定制": "製定", 116 | "按位取反": "位元補數", 117 | "按位與": "位元 AND ", 118 | "按位或": "位元 OR ", 119 | "按位異或": "位元 XOR ", 120 | "取反": "取補數", 121 | "8位無符整型": "8 位元無號整數", 122 | "整型": "整數", 123 | "比特位": "位元", 124 | "無符": "無號", 125 | "集群": "叢集", 126 | "": "", 127 | "": "", 128 | "": "", 129 | "": "", 130 | "": "", 131 | "": "", 132 | "": "", 133 | "": "", 134 | "": "", 135 | "": "", 136 | "": "", 137 | "": "", 138 | "": "", 139 | "": "", 140 | "": "", 141 | "您": "你" 142 | } 143 | --------------------------------------------------------------------------------