├── README.md └── doc ├── creating-search-requests.md ├── import-and-indexing.md └── machine-resources.md /README.md: -------------------------------------------------------------------------------- 1 | # Solrのウェブサービスへの組込みと運用のTips 2 | 3 | ## 目次 4 | - [データのインポートとインデキシング](https://github.com/hatena/solr-tutorial/blob/master/doc/import-and-indexing.md) 5 | - [検索リクエストの作成とレスポンスの形式](https://github.com/hatena/solr-tutorial/blob/master/doc/creating-search-requests.md) 6 | - [マシンリソース・レプリケーション・冗長化構成](https://github.com/hatena/solr-tutorial/blob/master/doc/machine-resources.md) 7 | 8 | ## ライセンス 9 | クリエイティブ・コモンズ・ライセンス
この 作品 は クリエイティブ・コモンズ 表示 - 非営利 - 継承 2.1 日本 ライセンスの下に提供されています。 10 | -------------------------------------------------------------------------------- /doc/creating-search-requests.md: -------------------------------------------------------------------------------- 1 | # 検索リクエストの作成とレスポンスの形式 2 | Solrのインデックスに対する検索リクエストは、Javaで直接呼び出すほか、ネットワーク越しにHTTPリクエストでも行うことが出来ます。ここでは、よく使われるであろう後者の方式のみ解説します。 3 | 4 | $ curl 'http://localhost:8983/solr/hoge/select?q=body:hoge' 5 | 6 | Solrがサポートする検索リクエストのAPIは、大きく分けて2種類、細かく分けると3種類あり、開発者が用途によって使い分けられるようになっています。順を追って説明します。 - [Lucene Query Parser](http://wiki.apache.org/solr/SolrQuerySyntax#lucene) - [DisMax Query Parser](http://wiki.apache.org/solr/DisMaxQParserPlugin) - [Extended DisMax Query Parser](http://wiki.apache.org/solr/ExtendedDisMax) 7 | ## Lucene Query Parser 8 | Solrで利用できるもっとも基本的なクエリパーザーがLucene Query Parserです。SolrはLuceneをベースにしている関係で、Luceneのそれと上位互換性があります。フィールドごとにいわゆるブーリアン検索が出来ます。 9 | ?q=title:(hoge OR fuga) 10 | 11 | 複数フィールドにまたがって検索したい場合は明示的に指定します。 12 | ?q=title:はてな OR body:はてな ### Lucene Query Parserの挙動 デフォルトでは、検索クエリが文書と同じようにトークナイズされるため、片方でもマッチすれば文書自体はマッチしたと見なされます。 13 | 14 | ?q=body:山田太郎 # 山田 だけでも 太郎 だけでもマッチする 15 | 16 | “キーワードがこの順で連続して現れる” 文書だけ検索したい場合はフレーズクエリを使うとよいです。検索ワードを""で囲みます。 17 | 18 | ?q=body:”山田太郎” # 山田太郎 だけにマッチ 19 | 20 | 詳細な仕様は、[Apache Lucene - Query Parser Syntax](http://lucene.apache.org/core/old_versioned_docs/versions/3_5_0/queryparsersyntax.html) を参照して下さい。 21 | 22 | ### Lucene Query Parser のメリット・デメリット 23 | #### メリット 24 | - クエリに文書がマッチする条件を厳密に伝えられる 25 | - ブーリアン検索は一般的なウェブ検索エンジンで使える検索クエリとして慣れ親しんでいる人が多く、理解しやすい #### デメリット 26 | - 検索したいフィールドが複数あり、検索クエリが複数語から成る場合に、検索クエリの組み立てが複雑になる。 具体的には、2語のAND検索を2つフィールドで網羅的に行う場合、以下のようになります。 27 | q=(title:はてな AND title:しなもん) OR (body:はてな AND body:しなもん) OR (title:はてな AND body:しなもん) OR (title:しなもん AND body:はてな) 28 | - 文書のスコアリングも基本的に組み込みのTF・IDFのみ 29 | 30 | ##### 解決策 - schema.xml で CopyFieldを使い、検索したいTextFieldを一つにまとめてインデックスする 31 | この方法でも可能ですが、DisMax Query Parserにおいてフィールドごとにマッチしたときの重みをカスタマイズする方法(後述)は使えなくなります。 - DisMax Query Parser や Extended DisMax Parser などの拡張クエリを使う 検索対象文書が多く、文書のスコアリング(検索結果のランキング)が重要な場合、こちらがおすすめです。 32 | 33 | ## DisMax Query Parser 34 | 35 | Lucene Query Parserでカバーしきれないユースケースを満たすためにSolrで独自に開発されたクエリパーザーです。** &defType=dismax ** というパラメータを指定することでデフォルトのLucene Query Parserに代わってDisMax Query Parserを利用するモードになります。 36 | 37 | 1. 各フィールドをそれぞれ(Disjunction)検索して 38 | 2. TF・IDFスコアがもっとも高い(Max)フィールドを 39 | 3. その文書のスコアとして表現する 40 | 41 | のが、基本的な動作です。 42 | ### DisMax Query Parserの使いどころ 43 | #### 多数のテキストフィールドを横断的に検索する 44 | 45 | q=はてな しなもん&defType=dismax&qf=title+body 46 | 47 | BooleanQueryの例よりシンプルに書けるのが分かると思います。 48 | 49 | **qf** パラメータを指定すると、他のテキストフィールドよりも重視したいなフィールドがある場合、個別に重みを指定することが出来ます。以下はタイトルでのマッチを本文よりも重視する(タイトルの方にweightを設定する)例です。 50 | 51 | q=はてな しなもん&defType=dismax&qf=title^2+body 52 | 53 | #### テキストフィールド以外のフィールドを文書のスコアリングに使う 54 | 55 | **boost** パラメータを使うことで、ソーシャルボタンのShare数や、ブログエントリの日付の新しさ、テキストの内容以外のメタデータを文書のスコアリングに使えるようになります。ここで使える関数などは[FunctionQuery - Solr Wiki](http://wiki.apache.org/solr/FunctionQuery)を参照して下さい。 56 | 57 | q=はてな しなもん&defType=dismax&qf=title^2 body&boost=log(share_count) 58 | 59 | #### 単語の出現位置を考慮してスコアリングする 60 | 複数のキーワードで検索した場合、とくにキーワード感の近さなどはスコアリングに考慮されないのですが、これだと長い文書のまったく関係ないコンテキストでたまたまマッチしてしまったりして都合が悪いです。この問題を解決するために、複数のキーワードで検索したときに検索対象の文書中のマッチしたキーワード間の距離に関して 61 | 62 | - どのぐらい近ければ"文書が検索クエリにマッチした"と見なすか 63 | - どのぐらい近ければ"キーワード同士が近くにある"と見なすか 64 | 65 | を考慮するオプションがあります。 66 | 67 | **qs** パラメータを指定して、複数のキーワードで検索したとき、それらの距離が600 positions 以下の場合のみ検索クエリにマッチしたことにする。 68 | 69 | q=はてな しなもん&defType=edismax&qf=title^2 body&qs=600 70 | 71 | **ps** パラメータで複数のキーワードで検索したとき、各フィールドでそれらの距離が20 positions 以下の場合に検索クエリにとくに関連する文書と見なして、各フィールドの追加の重みを**pf** パラメータで設定する。 72 | 73 | q=はてな しなもん&defType=edismax&qf=title^2 body&pf=title^4 body^2&ps=20 74 | 75 | ## Extended DisMax Query Parser 76 | 77 | DisMax Query Parser よりもさらに詳細に文書のマッチやスコアリングをコントロールできる [Extended DisMax Query Parser]() があります。**defType=edismax** と指定することで、文書のマッチとスコアリングに関するいくつかの追加パラメータが指定可能になりますが、詳細は割愛します。 [ExtendedDisMax - Solr Wiki](http://wiki.apache.org/solr/ExtendedDisMax) を参照して下さい。 78 | 79 | ## レスポンスの形式 80 | レスポンス形式はデフォルトのXMLのほか、JSON, Ruby, CSVなどが選べます。**wt** パラメータで指定します。 81 | 82 | $ curl 'http://localhost:8983/solr/hoge/select?q=body:hoge&wt=json' 83 | 84 | また、必要に応じて自分でレスポンス形式を定義することも出来ます。詳しくは[QueryResponseWriter - Solr Wiki](http://wiki.apache.org/solr/QueryResponseWriter)を参照して下さい。 -------------------------------------------------------------------------------- /doc/import-and-indexing.md: -------------------------------------------------------------------------------- 1 | # データのインポートとインデキシング 2 | [目次に戻る](../README.md) 3 | 4 | Solrに文書をインポートする方法は、[複数提供](http://wiki.apache.org/solr/#Search_and_Indexing)されています。基本的な方法としては、[XML](http://wiki.apache.org/solr/UpdateXmlMessages)、[JSON](http://wiki.apache.org/solr/UpdateJSON)、[CSV](http://wiki.apache.org/solr/UpdateCSV)などの形式で決められたHTTPエンドポイントにHTTP POSTすることで登録できます。また、外部のデータベースなどから直接文書データを取り込むことが出来る [DataImportHandler](http://wiki.apache.org/solr/DataImportHandler) というツールが標準で提供されています。 5 | 6 | ここでは実際に使われるケースが多いであろう「DataImportHandlerでJDBCを使ってMySQLからSolrにデータをインデックスする」ときの基本的な手順と、その際のコツについて解説します。 7 | 8 | ## 必要な設定ファイル 9 | 10 | $CORE_HOME は solr.core.instanceDir として指定されたディレクトリを示したものです。solr.xml 内で指定するか、あるいはcoreを作成するときに明示的に指定することができます。 11 | 12 | ### ファイルの構成 13 | 14 | $CORE_HOME/ 15 | conf/ 16 | solrconfig.xml 17 | data-config.xml 18 | schema.xml 19 | lib/ 20 | (3rd party製のトークナイザやJDBCドライバなどはここに配置するとSolrに認識される) 21 | 22 | ### solrconfig.xml 23 | 24 | キャッシュの量やリクエストハンドラ、レプリケーションの設定など、 Solrの全体的な動作を記述するのが solrconfig.xml です。基本的な使い方をしている間は、あまり設定することはないはずです。 25 | 26 | [SolrConfigXml - Solr Wiki](http://wiki.apache.org/solr/SolrConfigXml) 27 | 28 | ### data-config.xml 29 | 30 | DataImportHandlerを使ってインポートする方法を記述する設定ファイルです。例を使って説明します。 31 | 32 | テーブルスキーマで db01 というホストの blog_db というデータベースに、以下のスキーマでデータがあるとします。 33 | 34 | CREATE TABLE blog_entry ( 35 | entry_id INT unsigned NOT NULL AUTO_INCREMENT, 36 | title varbinary(255) NOT NULL DEFAULT '', 37 | body blob NOT NULL DEFAULT '', 38 | category_id tinyint unsigned, 39 | created timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 40 | modified timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 41 | PRIMARY KEY (entry_id), 42 | KEY (created), 43 | KEY (modified) 44 | ) ENGINE=InnoDB DEFAULT CHARSET=binary; 45 | 46 | CREATE TABLE category ( 47 | category_id TINYINT UNSIGNED AUTO_INCREMENT, 48 | category_name varbinary(255) NOT NULL DEFAULT '' 49 | ) ENGINE=InnoDB DEFAULT CHARSET=binary; 50 | 51 | これをSolrにインポートするための data-config.xml は以下のようなものです。 52 | 53 | 54 | 65 | 66 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 以下、それぞれの箇所を解説します。 112 | 113 | #### MySQLのデータベースからインポートするときの設定 114 | DataImportHandlerはJDBC経由では1行ずつfetchされたものをインデックスすることを想定しています。MySQLのJDBCでこの挙動をbatchSize="-1"と指定する必要があります。 115 | 116 | batchSize="-1" 117 | 118 | これは分かりづらいため[FAQでも一番最初に表示されています](http://wiki.apache.org/solr/DataImportHandlerFaq)。 119 | 120 | 121 | #### インポートクエリ 122 | **query** 123 | 124 | 初回の全件インポート用のクエリを記述します。 125 | 126 | **deltaQuery** 127 | 128 | 差分インポートをするにあたって、対象となるレコードの主キーを取得するためのクエリを記述します。ここでは、デフォルトで **${dataimporter.last_index_time}** という、**ローカルのタイムゾーンでの前回のインポート開始時刻**を変数として参照できます。必要に応じて、[独自の変数を定義することも出来ます](http://wiki.apache.org/solr/DataImportHandlerFaq#Is_it_possible_to_use_core_properties_inside_data-config_xml.3F)。 129 | 130 | **deltaImportQuery** 131 | 132 | deltaQuery で取得した主キーを元に、更新・追加されたentityをインポートするためのクエリを記述します。ここでは、 **${dataimporter.delta.(deltaQueryで取得したカラム)}** の変数が使えます。上の例では **${dataimporter.delta.entry_id}** を使っています。 133 | 134 | #### UTF-8文字列のカラムを正しくインデックスするための設定 135 | テーブルのカラムに格納されたUTF-8文字列を正しく扱うために必要な設定です。これを設定しないと、マルチバイト文字を正しく認識されず、文字化けすることがあります。 136 | 137 | useUnicode="true" 138 | characterEncoding="utf8" 139 | useOldUTF8Behavior="true” 140 | 141 | #### 発行するSQLを参照系のみに限定する 142 | 143 | インポート設定のミスによって誤ってテーブルの内容を書き換えてしまうことを防止します。 144 | 145 | readOnly="true" 146 | 147 | #### フィールドの値の加工 148 | Transformerという仕組みによって、インポートした後のフィールドの値をインデックスする前に加工することが出来ます。 149 | 150 | 152 | transformer="ClobTransformer,DateFormatTransformer"> 153 | 154 | 155 | 156 | ClobTransformerは field 要素 で clob="true" と設定したフィールドを、byte型の配列から文字列に変換します。これはvarbinary型あるいはblob型のカラムがbyte型の配列としてDataImportHandlerに渡ってくるのを、後の行程で文字列として正しく処理するために必要な設定です。分かりづらいためか[FAQにも記述されています](http://wiki.apache.org/solr/DataImportHandlerFaq#Blob_values_in_my_table_are_added_to_the_Solr_document_as_object_strings_like_B.401f23c5)。 157 | 158 | DateFormatTransformerは日付と時刻を含むフィールドを変換します。[Solrが受け付ける日付型のタイムゾーンはUTCのみ](http://lucene.apache.org/solr/api/org/apache/solr/schema/DateField.html)なので、フィールドの値をこの形式に適切に変換するために使います。 159 | 160 | ##### JavaScriptによるフィールドの加工 161 | 162 | SolrをJava 6以上の環境で実行している場合、[既存のTransformer](http://wiki.apache.org/solr/DataImportHandler#Transformer)を利用する他に、[ScriptTransformer](http://wiki.apache.org/solr/DataImportHandler#ScriptTransformer)として data-config.xml 内で任意の処理を行うのtransformerをJavaScriptで定義できます。以下はgzipされた状態で格納されているフィールド(gzipped_body)があったとして、それを伸長し、UTF-8文字列として別なフィールド(body)に格納する例です。 163 | 164 | 165 | 167 | 179 | 180 | 183 | 184 | 185 | 186 | 187 | 上記の例を見ると分かるとおり、row.get('column_name') で加工前のフィールドの値を取得して加工した後で row.set('column_name') で 新たなフィールドを定義したり、既存のフィールドの値を上書きしたりできます。ScriptTransformerではJavaScriptの文法が使用でき、Javaの標準ライブラリをシームレスに利用できますが、Javaのパッケージや配列の扱いにの一部独特の癖があります。ここで利用可能な特有のイディオムについては[Scripting Java](http://www.mozilla.org/rhino/ScriptingJava.html)を参照して下さい。なお、独自のTransformerは[Javaで記述することも出来ます](http://wiki.apache.org/solr/DIHCustomTransformer)。 188 | 189 | ### schema.xml 190 | 191 | data-config.xml で インポートしたデータをどうインデックスするかを決定するのが schema.xml です。 192 | 193 | [SchemaXml - Solr Wiki](http://wiki.apache.org/solr/SchemaXml) 194 | 195 | いくつかのポイントについて解説します。 196 | 197 | トップレベル要素にスキーマの名前を定義できます。スキーマの名前はSolrの管理画面で表示され、操作対象を区別するために重要な情報となるので、容易に識別可能な名前にした方がよいです。ここでは、[coreレベルで利用可能な変数](http://wiki.apache.org/solr/CoreAdmin#Configuration)ため、coreの名前と同一にすることも出来ます。 198 | 199 | 200 | 201 | 202 | 最初から定義されているフィールドのほかに、独自のフィールドも定義できます。以下は、日本語文字列からなるフィールドの値を形態素解析した上でインデックスし、検索クエリに関しても同様なことを行った上で検索するフィールドを定義する例です。 203 | 204 | 205 | … 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | … 214 | 215 | 216 | org.atilika.kuromoji.solr.KuromojiTokenizerFactory というライブラリは [kuromoji - japanese morphological analyzer](http://www.atilika.org/) からダウンロードし、$CORE_HOME/lib 以下に配置すると利用可能になります。なお、[Solr 3.6 以上では Kuromoji Tokenizer が日本語の標準トークナイザーとして採用された](http://www.rondhuit.com/solr%E3%81%AE%E6%97%A5%E6%9C%AC%E8%AA%9E%E5%AF%BE%E5%BF%9C.html)ので、このような作業は必要ありません。 217 | 218 | 219 | DataImportHandlerなどを経由して外部から取得したフィールドの値とtypes要素以下のフィールドの型の定義をもって、どのようにインデックスするかを決定するのが、fields要素内の設定です。 220 | 221 | 222 | … 223 | 224 | 225 | … 226 | 227 | 228 | #### name 229 | DataImportHandlerのqueryやdeltaImportQuery属性で指定したカラム名を指定することで、値をSolrのフィールドと対応させるための属性です。 230 | #### indexed 231 | フィールドの値をインデックスすることで、検索、ソート、フィルタリングを可能にするかどうかを設定します。デフォルトの値はfalseです。 232 | #### stored 233 | フィールドを解析しインデックスする前のフィールドの値を、Solrに保持するかを設定します。trueにすることで、検索時にヒットした部分を抽出し、さらに検索クエリに含まれる単語をマークアップする[highlighting機能](http://wiki.apache.org/solr/HighlightingParameters)が使えるようになりますが、インデックスサイズは増加します。デフォルトの値はfalseです。 234 | #### termVectors 235 | フィールドの特徴ベクトルを生成するかを指定します。trueにすることで、似ている文書をインデックスから発見する[MoreLikeThis機能](http://wiki.apache.org/solr/MoreLikeThis)の精度が向上しますが、インデックスサイズは増加します。デフォルトの値はfalseです。 236 | #### required 237 | フィールドの値を必須としたいときにtrueに設定します。必ず存在しているべきフィールドに指定することで、もしインポート時にフィールドの値が存在していない場合にエラーにできます。デフォルトの値はfalseです。 238 | #### multiValued 239 | 1つの文書に同じフィールドが複数個ひもづく場合、multiValuedを指定します。デフォルトの値はfalseです。 240 | 241 | どの機能を使うためにどのオプションを有効にする必要があるかの対応表は、 [FieldOptionsByUseCase - Solr Wiki](http://wiki.apache.org/solr/FieldOptionsByUseCase) にあります。 242 | 243 | ### solr.xml 244 | 245 | Solrが起動したときに最初にロードするcoreの名前と対応する設定ファイルの位置などを設定するのが solr.xml です。 246 | -------------------------------------------------------------------------------- /doc/machine-resources.md: -------------------------------------------------------------------------------- 1 | # マシンリソース・レプリケーション・冗長化構成 2 | ## 必要なマシンリソースの見積もり 3 | ### ディスク容量 4 | TextFieldが主体のデータで、かつschema.xml において stored="true" (元のデータをSolrのインデックス内に保持する)と指定している場合、Solrを稼働させるホストではおおむね**元データの6倍** のディスク容量を見積もっておくと安全です。 5 | 6 | これは、 schema.xml における TextField型のフィールドのインデキシングの設定で一般的な設定を行った場合 7 | 、インデックスは元データの約2倍の容量を消費することと、optimize時に元のインデックスと同じサイズの追加のディスク容量が一時的に必要になるためです。 8 | 9 | 元データのサイズ: N 10 | 11 | stored=“true“設定による元データの保持に必要な容量: N 12 | インデックス自体に必要な容量: 2N 13 | optimizeで一時的に必要になる容量: (N+2N)*2=6N 14 | 15 | デフォルトの stored="false" だと約4倍で済みますが、インデックスにデータの追加・更新がある場合も考えて余裕をもって見積もるのが安全です。 16 | 17 | ### ネットワーク 18 | レプリケーション構成をとった場合、masterがoptimizeを行ったあと最初のレプリケーションは、インデックス全体のネットワーク越しのコピーとなります。インデックスサイズが大きい場合、かなりのネットワーク帯域を消費します。optimize後のレプリケーションを短時間で終わらせるためにも、Solrのmasterとslaveはネットワーク的に出来るだけ近くに配置した方がよいです。 19 | 20 | ## ベンチマークソフトによる性能の予測 21 | 22 | Solrのベンチマークソフトとして[SolrMeter](http://code.google.com/p/solrmeter/)というJava Swing製のツールが利用できます。詳細は割愛します。 23 | 24 | ### 注意すべき点 25 | 26 | SolrMeterを使ってベンチマークをとる場合、ベンチマーク用の検索クエリのバリエーションは、本番環境を想定して十分大きくする必要があります。そうしないと検索結果のデータがディスクキャッシュやSolr自体のLRUキャッシュに載ってしまい、ベンチマークの精度が出ません。 27 | 28 | ## レプリケーション 29 | データインポート時のレスポンス悪化によるユーザー体験への影響を最小限にするため、可能であれば master/slave構成(レプリケーション構成)をとり、ユーザーの検索リクエストへのレスポンスはおもにslaveが担当することが望ましいです。はてなでは[SolrReplication - Solr Wiki](http://wiki.apache.org/solr/SolrReplication) の [enable/disable master/slave in a node](http://wiki.apache.org/solr/SolrReplication#enable.2BAC8-disable_master.2BAC8-slave_in_a_node)の例をほぼそのまま利用し、起動時のパラメータを以下のようにすることでmaster/slaveの役割動的に設定するようにしています。 30 | 31 | ### solrconfig.xml 内の/replicationリクエストハンドラ設定箇所 32 | 33 | 34 | 35 | ${enable.master:false} 36 | commit 37 | startup 38 | schema.xml,dataimport.properties 39 | 40 | 41 | ${enable.slave:false} 42 | http://${master.host:localhost}:8983/solr/${solr.core.name}/replication 43 | 00:00:60 44 | 45 | 46 | 47 | solrconfig.xmlでこう設定しておくと、以下のように HTTPリクエスト時に **property.** から始まるパラメータを渡すか、[solrcore.properties という設定ファイルを配置する](http://wiki.apache.org/solr/SolrConfigXml#System_property_substitution)ことで、coreの作成時に solrconfig.xml に変数を渡すことが出来ます。 48 | 49 | curl "http://solrhost02:8983/solr/admin/cores? 50 | action=CREATE&name=blog_entry&property.enable.slave=true&property.master.host=solrhost01" 51 | 52 | このようにすることで、ホストの役割をリモートから動的に決定できます。これは、以降で説明する、Solrの検索サービスを冗長化するときに重要となります。 53 | 54 | ## Solrの冗長化構成 55 | バックアップ用ホストを用意してSolrのサービスを冗長化する場合、 56 | 57 | 1. 普段はホットスタンバイとしてmasterのインデックスを同期しておく 58 | 2. 障害が起きた場合、そのホストの役割に応じて、masterにもslaveにもなれる 59 | 60 | という仕様を満たす必要があります。前述の通り、Solrにはcoreを作成するときに、パラメータ応じてmaster/slaveの役割を切り替える仕組みがあるので、これを利用します。 61 | 62 | Solr自体はデーモン化の仕組みも、フェイルオーバーの仕組みを提供していないため、はてなでは daemontools によるデーモン化と keepalived によるフェイルオーバーを組み合わせて運用しています。 63 | 64 | ### keepalived用failoverスクリプト 65 | バックアップ用ホストにkeepalivedをインストールした上で、このようなfailoverスクリプトを配置することで、master,slaveのどちらがダウンした場合もバックアップ用ホストが適切な役割で昇格する仕組みになっています。 66 | 67 | #!/bin/bash 68 | 69 | # Usage 70 | # -t SERVICE -c CORE ... promote the solr to MASTER of / 71 | # -t SERVICE -c CORE -s MASTER_HOST ... promote the solr to SLAVE of with / 72 | 73 | BASEDIR="/home/httpd/apps/Hatena-Solr-Admin/releases/solr" 74 | 75 | usage() 76 | { 77 | cat <