solr简介

@(OTHERS)[solr]

没时间写 PPT了

一、Solr是什么

(一)搜索引擎

google、百度

分成2部分: 爬虫 搜索引擎

主流的框架: elasticSearch solr

为什么不用 ES ???

(二)背景

从Doug Cutting开始说起

Lucene

只是一个jar包,它提供了索引、搜索的API,还包括了拼写检查、关键字高亮、分词等功能模块。比如Eclipse 帮助系统的搜索功能。
our flagship sub-project, provides Java-based indexing and search technology, as well as spellchecking, hit highlighting and advanced analysis/tokenization capabilities

Solr

封装成一个可应用的系统,提供完整的以索引和搜索为核心的功能栈。

Nutch

爬虫 + solr,还有其它东西,我们下面继续。
爬虫抓到的内容越来越多了,一台机器放不下,怎么办?

ndfs(hdfs)

单进程爬虫太慢了,怎么办?

MR

MR + HDFS = ?

HADOOP

(三)Solr简介

Solr is the popular, blazing-fast, open source enterprise search platform built on Apache Lucene.

Solr is * highly reliable, scalable and fault tolerant, providing distributed indexing, replication and load-balanced querying, automated failover and recovery, centralized configuration and more *, Solr powers the search and navigation features of many of the world’s largest internet sites.

最简单的说,solr和hbase功能类似,你先把数据写入solr作保存,然后再从solr中查询出来。只是应用的场景不一样。
示范如何在UI中索引及查询,当然真正的应用是使用API操作的

image

二、Solr在我们业务中的应用场景

1、爬虫+solr
2、网站内的搜索框

(一)我们的应用场景

在hbase记录了以用户id为rowkey,用户属性/标签为qualifer的数据,数据量非常大。
举个例子:

roleId  platform appchannel vip-level

如果现在告诉你一个roleId,让你向它推荐一些产品,你可以通过一个get直接把它的属性读出来再进行分析,这在hbase中是非常高效的操作。
反过来,假如现在有一个新产品上线,你需要向苹果手机、vip级别在10级以上的用户批量投放广告,怎么办?
在RDBMS中,你可以通过select roleId from table where * AND *; 之类的实现。

但在hbase,想要同样的实现方式,你只有一种办法:全表扫描,看哪一行符合要求。
这会导致(1)集群压力很大(2)大量数据被读到JVM堆中,将其它内容冲出缓存,导致其它表的读取命中率下降(3)频繁的GC,GC期间regionserver不可用(4)慢,非常慢。

因此我们需要考虑其它方式:

1、使用filter

使用起来很方便,但是局限性也很大,hbase的filter是直接扫记录的,如果数据范围很大,会导致查询速度很慢。所以如果能先使用行健把记录缩小到一个较小范围,那么就比较适合,否则还是需要全表描述。此外该方法不能解决获取总数的为,只能稍为减少讲读入内存的qualifier数量。

2、使用二级索引

这种方式的适用情形较广。索引的目的地也有多种,一是索引回hbase的另一个表,以原来的value中需要作为查询条件的qualifier作为rowkey,rowkey作为value。这可以解决根据value取rowkey的问题,但还是没办法处理复杂的关联查询。二是索引至solr/elasticSearch等全文搜索引擎。我们下面主要介绍这种方式。
而从hbase0.94开始,coprocessor就变成了创建二级索引的首选方式了。

(二)索引以后的工作

将hbase的key与各个value映射成solr的各个field,然后就可以通过类似:

query.set("q", “platform:apple AND vip:10")

去查询得到所需要的用户了。
如果在solr中没有保存所有信息(比如hbase有一些字段不会作为查询条件的),则需要回hbase反查所有信息,完整示意图如下:

image

为什么要到hbase反查?

index 与 store

一个doc回到solr时,可以指定某个field是否index,以及是否store。也就是说solr中可能没有完整的信息的。当然也可以完全索引与存储,但这会使用大量的空间。另外如果还有一些二进制的内容,也不可能完全索引与存储。

hbase(main):003:0> scan 'webpage'  
ROW                   COLUMN+CELL                                                 
 com.163.www:http/    column=f:fi, timestamp=1419085934952, value=\x00'\x8D\x00   
 com.163.www:http/    column=f:ts, timestamp=1419085934952, value=\x00\x00\x01Jh  
                      \x1C\xBC7                                                   
 com.163.www:http/    column=mk:_injmrk_, timestamp=1419085934952, value=y        
 com.163.www:http/    column=mk:dist, timestamp=1419085934952, value=0            
 com.163.www:http/    column=mtdt:_csh_, timestamp=1419085934952, value=?\x80\x0  
                      0\x00                                                       
 com.163.www:http/    column=s:s, timestamp=1419085934952, value=?\x80\x00\x00    
com.163.www:http/      column=f:cnt, 
\x80\x00\x00                          timestamp=1404983104070, value=\x0D\x0A<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:
                             o="urn:schemas-microsoft-com:office:office" xmlns="http://www.w3.org/TR/REC-html40">\x0D\x0
                             A\x0D\x0A<head>\x0D\x0A<meta http-equiv="Content-Type" content="text/html; charset=windows-
                             1252">\x0D\x0A<meta http-equiv="Content-Language" content="es">\x0D\x0A\x0D\x0A<title>CAMBI
                             O DE CHEQUES | cambio de cheque | cheque | cheques | compra de cheques de terceros | cambio
                              cheques | cheques no a la orden </title>\x0D\x0A<link rel="shortcut icon" ref="favivon.ico
                             " />\x0D\x0A<meta name="GENERATOR" content="Microsoft FrontPage 5.0">\x0D\x0A<meta name="Pr
                             ogId" content="FrontPage.Editor.Document">\x0D\x0A<meta name=keywords content="cambio de ch
                             eques, compro cheques, canje de cheques, Compro cheques de terceros, cambio cheques">\x0D\x
                             0A<meta name="description" http-equiv="description" content="Admival.com.ar cambia por efec
                             tivo sus cheques de terceros, posdatados, al d\xECa, de Sociedades, personales, cheques no
                             a la orden, cheques de indemnizaci\xF3n por despido, etc. " /> \x0D\x0A<meta name="robots"
                             content="index,follow">\

(三)实现方式

1、倒排索引

为hbase表建倒排索引,重新索引回hbase中,以标签作rowkey,以rowkey作值。这很适合于根据value查值,但不能根据多个value查值。

2、solr-index

使用solr-index等开源工具将数据索引至solr。
CDH有一个hbase-solr的模块,它是基于开源项目hbase-indexer的。问题是hbase-indexer基于0.94与0.98的,不清楚cdh是否有改进,没文档说明。但一般而言,它与CDH5.6同时发布,应该是不存在兼容性问题的。
此外,这需要起一个server,引入了多一层的故障点。
已经几年没更新了。

3、coprocessor

0.9.4以后最灵活的方式。
缺点是开发coprocessor要非常小心,否则会影响集群稳定,因此目前新的coprocessor一般由SA开发维护。但SA可能不熟悉业务。

4、使用示例(操作)
create 'ljhtest8', 'cf'
disable 'ljhtest8'
alter 'ljhtest8', 'Coprocessor'=>'hdfs://testcluster/hbase/userlib/hbase-solr-indexer-0.2.3.jar|com.lujinhong.hbase.solr.indexer.SolrIndexerObserver|1001|arg1=1'
enable 'ljhtest8'
put 'ljhtest8','lulu','cf:f1','ljhtest2'

此时索引内容为:

  {
    "id": "lulu",
    "qualifier_col": "f1:ljhtest2;;",
    "_version_": 1542711403501060000
  }

继续操作hbase:

get 'ljhtest8','@@@GETTIME@@@'
put 'ljhtest8','lulu','cf:f2','ljhtest23'
put 'ljhtest8','lulu','cf:f3','ljhtest23'

此时索引内容为:

{
        "id": "lulu",
        "qualifier_col": "f1:ljhtest2;;f2:ljhtest23;;f3:ljhtest23;;",
        "_version_": 1542711353190383600
  }

delete操作也类似。

三、SolrCloud架构

solr有2种部署模式:独立与cloud(集群)。前者只需要一个java命令启动服务即可,它基于jetty来提供服务,适用于功能测试及小数据规模的情况。
建议开发人员可以建一个自己的服务方便测试代码。

下面主要介绍cloud模式。

(一)特性

SolrCloud是基于Solr和Zookeeper的分布式搜索方案,是正在Solr4.0以后的核心组件之一,它的主要思想是使用Zookeeper作为集群的配置信息中心。它有几个特色功能,集中式的配置信息、自动容错 、近实时搜索 、查询时自动负载均衡。

1、集中式的配置信息使用ZK进行集中配置

启动时可以指定把Solr的相关配置文件上传Zookeeper,多机器共用。这些ZK中的配置不会再拿到本地缓存,Solr直接读取ZK中的配置信息。配置文件的变动,所有机器都可以感知到。另外,Solr的一些任务也是通过ZK作为媒介发布的。目的是为了容错。接收到任务,但在执行任务时崩溃的机器,在重启后,或者集群选出候选者时,可以再次执行这个未完成的任务。

2、自动容错

SolrCloud对索引分片,并对每个分片创建多个Replication。每个Replication都可以对外提供服务。一个Replication挂掉不会影响索引服务。更强大的是,它还能自动的在其它机器上帮你把失败机器上的索引Replication重建并投入使用。

3、近实时搜索

立即推送式的replication(也支持慢推送)。可以在秒内检索到新加入索引。

4、查询时自动负载均衡

SolrCloud索引的多个Replication可以分布在多台机器上,均衡查询压力。如果查询压力大,可以通过扩展机器,增加Replication来减缓。

5、自动分发的索引和索引分片

发送文档到任何节点,它都会转发到正确节点。

6、事务日志

事务日志确保更新无丢失,即使文档没有索引到磁盘。

7、索引存储在HDFS上

索引的大小通常在G和几十G,上百G的很少,这样的功能或许很难实用。但是,如果你有上亿数据来建索引的话,也是可以考虑一下的。我觉得这个功能最大的好处或许就是和下面这个“通过MR批量创建索引”联合实用。

8、通过MR批量创建索引

有了这个功能,你还担心创建索引慢吗?

(二)核心概念

1、Collection

在SolrCloud集群中逻辑意义上的完整的索引。它常常被划分为一个或多个Shard,它们使用相同的Config Set。如果Shard数超过一个,它就是分布式索引,SolrCloud让你通过Collection名称引用它,而不需要关心分布式检索时需要使用的和Shard相关参数。

2、Config Set

Solr Core提供服务必须的一组配置文件。每个config set有一个名字。最小需要包括solrconfig.xml (SolrConfigXml)和schema.xml (SchemaXml),除此之外,依据这两个文件的配置内容,可能还需要包含其它文件。它存储在Zookeeper中。Config sets可以重新上传或者使用upconfig命令更新,使用Solr的启动参数bootstrap_confdir指定可以初始化或更新它。

3、Core

也就是Solr Core,一个Solr中包含一个或者多个Solr Core,每个Solr Core可以独立提供索引和查询功能,每个Solr Core对应一个索引或者Collection的Shard,Solr Core的提出是为了增加管理灵活性和共用资源。在SolrCloud中有个不同点是它使用的配置是在Zookeeper中的,传统的Solr core的配置文件是在磁盘上的配置目录中。

4、Leader

赢得选举的Shard replicas。每个Shard有多个Replicas,这几个Replicas需要选举来确定一个Leader。选举可以发生在任何时间,但是通常他们仅在某个Solr实例发生故障时才会触发。当索引documents时,SolrCloud会传递它们到此Shard对应的leader,leader再分发它们到全部Shard的replicas。

5、Replica

Shard的一个拷贝。每个Replica存在于Solr的一个Core中。一个命名为“test”的collection以numShards=1创建,并且指定replicationFactor设置为2,这会产生2个replicas,也就是对应会有2个Core,每个在不同的机器或者Solr实例。一个会被命名为test_shard1_replica1,另一个命名为test_shard1_replica2。它们中的一个会被选举为Leader。

6、Shard

Collection的逻辑分片。每个Shard被化成一个或者多个replicas,通过选举确定哪个是Leader。
shard是对完整文档集索引 分片(块)处理的体现,在solr的代码里每个shard都有一个solrCore对其维护管理 。 所以core是从solr代码的层面上讲的,而shard是从索引数据的分割角度来讲的。目前solr只允许一个core管理维护一个shard。

7、Zookeeper

Zookeeper提供分布式锁功能,对SolrCloud是必须的。它处理Leader选举。Solr可以以内嵌的Zookeeper运行,但是建议用独立的,并且最好有3个以上的主机。

image

(三)核心流程

1、索引

image

2、索引

image

3、分裂

image

四、Solr开发最佳实践

(1)所有对象均是线程安全
(2)SolrClient对象代价较高,要实现单例,其它很简单,随便用。

(一)索引

public class IndexDemo {

private static final String DEFAULT_COLLECTION = "collection_1";
// private static final String SOLR_ZK = "10.120.69.101:2181/solr";
private static final String SOLR_ZK = "10.120.69.101:2181/solr55";
private static final int DOC_NUM = 10000;

public static void main(String[] args) throws SolrServerException, IOException {

    try (CloudSolrClient client = new CloudSolrClient(SOLR_ZK);) {// cloud模式
        //
        // SolrInputDocument doc = new SolrInputDocument();// 实例化索引Doc
        //
        // doc.addField("id", "ljh3");// 添加主键
        // //doc.addField("score", "100.0");
        // doc.addField("qualifier_s", "best2");
        client.setDefaultCollection(DEFAULT_COLLECTION);
        // client.add(doc);
        // // client.add("collection1", doc);
        // client.commit();

        // 1、每次提交一个文档。注意,不只是commit()效率不高,client.add()的效率也是非常低的,因此需要将所有文档先add进一个collection,然后client.add(collection)
        // 100条就要5秒多,根本不可接受
        Long current = System.currentTimeMillis();
        for (int i = 0; i < DOC_NUM; i++) {
            SolrInputDocument doc2 = new SolrInputDocument();
            // printTimeDuration(current);
            doc2.addField("id", "way1" + i);
            // printTimeDuration(current);
            // doc2.addField("score_s",Collections.singletonMap("set","score"));
            Set set = new HashSet();
            // printTimeDuration(current);
            for (String s : "abc,edf,kkk,lll".split(",")) {
                set.add(s);
            }
            // printTimeDuration(current);
            Map map = new HashMap();
            map.put("set", set);
            doc2.addField("key_ss", map);
            // printTimeDuration(current);
            client.add(doc2);
            // printTimeDuration(current);
            // client.commit();
        }
        client.commit();
        printTimeDuration(current);

        // 2、方式二:将所有文档先add进一个collection,然后client.add(collection)
        current = System.currentTimeMillis();
        //List<SolrInputDocument> docList = new LinkedList<SolrInputDocument>();
        ConcurrentLinkedQueue<SolrInputDocument> docList = new ConcurrentLinkedQueue<SolrInputDocument>();

        for (int i = 0; i < DOC_NUM; i++) {
            SolrInputDocument doc2 = new SolrInputDocument();
            doc2.addField("id", "way2" + i);
            Set set = new HashSet();
            for (String s : "abc,edf,kkk,lll".split(",")) {
                set.add(s);
            }
            Map map = new HashMap();
            map.put("set", set);
            doc2.addField("key_ss", map);
            docList.add(doc2);
        }
        client.add(docList);
        client.commit();
        printTimeDuration(current);

        // 3、方式三:和方式二类似,只是add时指定一个commitWith的参数
        // client.add(docList,2000);
    }

}

private static void printTimeDuration(Long current) {

    System.out.println("Time use: " + (System.currentTimeMillis() - current));
}

}

(二)搜索

查询时提供的参数可以分成三部分:
search strings—that is, terms to search for in the index
parameters for fine-tuning the query by increasing the importance of particular strings or fields, byapplying Boolean logic among the search terms, or by excluding content from the search results
parameters for controlling the presentation of the query response, such as specifying the order in which results are to be presented or limiting the response to particular fields of the search application’s schema.

public class QueryDemo {

    private static final String DEFAULT_COLLECTION = "hbase";
    private static final String SOLR_ZK = "10.120.69.101:2181/solr5";
//  private static final String SOLR_ZK = "10.120.69.101:2181/solr";

    public static void main(String[] args) throws SolrServerException, IOException {
        try (CloudSolrClient client = new CloudSolrClient(SOLR_ZK);) {// cloud模式
            //根据id查doc
            SolrDocument doc = client.getById(DEFAULT_COLLECTION, "index_demo");
            if(null != doc){

            for (String field : doc.getFieldNames()) {
                System.out.println(field + " : " + doc.getFieldValues(field));
            }
        }

            //根据field查doc
            SolrQuery query = new SolrQuery();
            //query.setQuery(mQueryString);
            query.setRequestHandler("/query");
            query.set("q", "qualifier_col:valuedemo");//返回index_demo
//          query.set("q", "qualifier_col:valuedemo OR id:index_demo2");//返回index_demo
//          query.set("q", "qualifier_col:valuedemo AND id:index_demo2");//返回空
//          query.set("q", "qualifier_col:valuede*");//返回index_demo
            QueryResponse response = client.query(DEFAULT_COLLECTION, query);
            for(SolrDocument doc2 :response.getResults()){
                System.out.println(doc2.getFieldValue("id"));
            }
        }

    }

}

五、Solr的部署与运维简介

(一)版本及准备工作

1、下载并解压tomcat, solr至目录/home/hadoop/tomcat, /home/hadoop/solr
其中版本为:

solr-5.2.1 
tomcat-7.0.70

注意solr5.5有各种兼容性问题,怀疑要JDK8或者tomcat8.

(二)准备solr相关webapp内容,并复制至tomcat

1、把solr.war解压到/home/hadoop/solr/solr 目录下。

~/solr/solr$ cp ../server/webapps/solr.war .
~/solr/solr$ jar -xf solr.war

2、将/home/hadoop/solr/server/lib/ext/*中的jar包复制到/home/hadoop/solr/solr/WEB-INF/lib/,tomcat加载solr时要用到这些jar包。

~/solr$ cp server/lib/ext/* solr/WEB-INF/lib/

3、将/home/hadoop/solr/solr整个目录复制到tomcat/webapps中

~/solr$ cp -r solr ../tomcat/webapps/

(三)准备配置文件并上传至zk

1、复制配置文件到/home/hadoop/solr/conf

~/solr$ mkdir conf
~/solr$ cp -r  server/solr/configsets/basic_configs/conf/* conf
#~/solr$ cp -r example/files/conf/* conf

源码中的一个bug

问题现象:在core中创建索引的时候,出现以下问题:
Unable to invoke function processAdd in script: update-script.js

解决方法:https://issues.apache.org/jira/browse/SOLR-9005

将updat-scripts.js中的

 var token_stream =
   analyzer.tokenStream("content", doc.getFieldValue("content"));

改为:

  var content = doc.getFieldValue("content");
  if (!content) {
      return; //No content found, so we are done here
  }
var token_stream =
        analyzer.tokenStream("content", content);

2、将配置文件上传至zk

 java -classpath .:/home/hadoop/tomcat/webapps/solr/WEB-INF/lib/* org.apache.solr.cloud.ZkCLI -cmd upconfig -zkhost 10.120.69.101:2181,10.120.69.100:2181,10.120.69.102:2181/solr5 -confdir /home/hadoop/solr/conf/ -confname solrconfig

然后再到zk中检查配置是否成功上传:

[zk: 10.120.69.101(CONNECTED) 1] get /solr5/configs/solrconfig/
currency.xml          protwords.txt         _rest_managed.json    solrconfig.xml        lang                  stopwords.txt         synonyms.txt          update-script.js
velocity              elevate.xml           params.json           email_url_types.txt   schema.xml            managed-schema

(四)准备solr的索引数据目录及配置

1、创建用于保存索引文件的目录

mkdir /disk1/solr/data/solr-core

注意这个目录的权限。

2、将solr.xml复制到上述目录

~/solr$ cp server/solr/solr.xml /disk1/solr/data/solr-core/

3、 修改solr.xml,修改hostPort和Tomcat端口一致

<int name="hostPort">8080</int>

(五)配置tomcat并启动

1、在tomca/conf目录下创建Catalina/localhost目录

~/tomcat/conf$ mkdir -p Catalina/localhost

2、在上述目录中创建solr.xml,此为Solr/home的配置文件。

<?xml version="1.0" encoding="UTF-8"?>
 <Context docBase="/home/hadoop/tomcat/webapps/solr" debug="0" crossContext="true"> 
 <Environment name="solr/home" type="java.lang.String" value="/disk1/solr/data/solr-core/" override="true"/> 
</Context>

3、修改bin/catalina.sh中的JAVA_OPTS变量,加上zk的配置:

JAVA_OPTS="$JAVA_OPTS -DzkHost=10.120.69.101:2181,10.120.69.100:2181,10.120.69.102:2181/solr5"

4、启动tomcat

bin/startup.sh

注意先为几个脚本加上权限:

chmod +x startup.sh shutdown.sh  catalina.sh

5、验证UI

http://10.120.69.104:8080/solr/

截图

(六)添加其它节点

1、将/home/hadoop/solr、/home/hadoop/tomcat与/disk1/solr/data/ 三个目录复制到其它机器相同的目录中。

2、启动tomcat

(七)创建core

curl ‘http://10.120.69.104:8080/solr/admin/collections?action=CREATE&name=collection_1&numShards=3&replicationFactor=1&collection.configName=solrconfig

curl ‘http://10.120.69.104:8080/solr/admin/collections?action=CREATE&name=collection_2&numShards=2&replicationFactor=1&collection.configName=solrconfig

创建的2个core分别如下图:

(八)使用API索引与搜索

1、索引

public class IndexDemo {

    private static final String DEFAULT_COLLECTION = "collection4";
    //private static final String SOLR_ZK = "10.120.69.101:2181/solr";
    private static final String SOLR_ZK = "10.120.69.101:2181/solr5";

    public static void main(String[] args) throws SolrServerException, IOException {

        SolrInputDocument doc = new SolrInputDocument();// 实例化索引Doc
        doc.addField("id", "index_demo");// 添加主键
        //doc.addField("qualifier_col", "valuedemo");// 添加主键
        CloudSolrClient client = new CloudSolrClient(SOLR_ZK);// cloud模式
        client.setDefaultCollection(DEFAULT_COLLECTION);
        client.add(doc);
      //client.add("collection1", doc);
        client.commit();
        client.close();

    }

}

2、索引

public class QueryDemo {

    private static final String DEFAULT_COLLECTION = "collection4";
    private static final String SOLR_ZK = "10.120.69.101:2181/solr5";
//  private static final String SOLR_ZK = "10.120.69.101:2181/solr";

    public static void main(String[] args) throws SolrServerException, IOException {
        try (CloudSolrClient client = new CloudSolrClient(SOLR_ZK);) {// cloud模式
            //根据id查doc
            SolrDocument doc = client.getById(DEFAULT_COLLECTION, "index_demo");
            if(null != doc){

            for (String field : doc.getFieldNames()) {
                System.out.println(field + " : " + doc.getFieldValues(field));
            }
        }

            //根据field查doc
            SolrQuery query = new SolrQuery();
            //query.setQuery(mQueryString);
            query.setRequestHandler("/query");
    //          query.set("q", "qualifier_col:valuedemo");//返回index_demo
    //          query.set("q", "qualifier_col:valuedemo OR id:index_demo2");//返回index_demo
    //          query.set("q", "qualifier_col:valuedemo AND id:index_demo2");//返回空
    //          query.set("q", "qualifier_col:valuede*");//返回index_demo
            QueryResponse response = client.query(DEFAULT_COLLECTION, query);
            for(SolrDocument doc2 :response.getResults()){
                System.out.println(doc2.getFieldValue("id"));
            }
        }

    }

}

(九)zk中的内容

[zk: 10.120.69.101(CONNECTED) 16] get /solr5/
configs             clusterstate.json   aliases.json        live_nodes          overseer            overseer_elect      collections

通过命令上传的配置文件都在configs目录中,collections中是各个collection的信息。
其它目录的详细信息以后再分析。

(十)创建新的core

1、准备配置文件

cp /home/hadoop/solr/conf /home/hadoop/solr/myconf

然后根据需要修改,最常见的修改是在schema.xml中添加字段。

必备的配置文件有schema.xml和solrconfig.xml。其余还有一些可选的如stopwords.txt, synonyms.txt,protwords.txt, currency.xml等。

2、上传至zk

java -classpath .:/home/hadoop/tomcat/webapps/solr/WEB-INF/lib/* org.apache.solr.cloud.ZkCLI -cmd upconfig -zkhost 10.120.69.101:2181,10.120.69.100:2181,10.120.69.102:2181/solr5 -confdir /home/hadoop/solr/myconf/ -confname myconf

3、创建core

curl 'http://10.120.69.104:8080/solr/admin/collections?action=CREATE&name=collection2&numShards=2&replicationFactor=1&collection.configName=myconf2'
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐