继续带着问题读这段代码
- 怎么决定partition挂在哪个ps上的
createDB
创建db的http请求长这样,我们从这里入手
curl -XPUT -H "content-type:application/json" -d '{
"name": "db_name"
}
' http://master_server/db/_create
函数调用链是这样的:
func (ca *clusterAPI) createDB(c *gin.Context) ->
func (ms *masterService) createDBService(ctx context.Context, db *entity.DB)
来看看createDBService()
函数做了啥
createDBService
func (ms *masterService) createDBService(ctx context.Context, db *entity.DB) (err error) {
// 检查是否合法:1.dbname是否合法 2.不能有id
db.Validate()
// 检查ps,初次创建是没有的
ms.validatePS(ctx, db.Ps)
// 为DB生成一个唯一的ID
db.Id, _ = ms.Master().NewIDGenerate(ctx, entity.DBIdSequence, 1, 5*time.Second)
err = ms.Master().STM(context.Background(), func(stm concurrency.STM) error {
idKey, nameKey, bodyKey := ms.Master().DBKeys(db.Id, db.Name)
if stm.Get(nameKey) != "" {
return fmt.Errorf("dbname %s is exists", db.Name)
}
if stm.Get(idKey) != "" {
return fmt.Errorf("dbID %d is exists", db.Id)
}
value, _ := json.Marshal(db)
stm.Put(nameKey, cast.ToString(db.Id))
stm.Put(idKey, db.Name)
stm.Put(bodyKey, string(value))
return nil
})
return err
}
可以看见步骤是:检查db和ps是否合法,然后为db生成一个ID。之后,为这个db准备了3个key,并在etcd里存上相关的value
- idKey: db/{db.Id}
- nameKey: db/{db.Name}
- bodyKey: db/body/{db.Id} 再以这3个key作为key,
createSpace
Space是表空间,相当于sql里的table,创建表空间的请求如下:具体参数说明文档在这里
curl -XPUT -H "content-type: application/json" -d'
{
"name": "space1",
"partition_num": 1,
"replica_num": 1,
"engine": {
"name": "gamma",
"index_size": 70000,
"id_type": "String",
"retrieval_type": "IVFPQ",
"retrieval_param": {
"metric_type": "InnerProduct",
"ncentroids": 2048,
"nsubvector": 32
}
},
"properties": {
"field1": {
"type": "keyword"
},
"field2": {
"type": "integer"
},
"field3": {
"type": "float",
"index": true
},
"field4": {
"type": "string",
"array": true,
"index": true
},
"field5": {
"type": "integer",
"index": true
},
"field6": {
"type": "vector",
"dimension": 128
},
"field7": {
"type": "vector",
"dimension": 256,
"format": "normalization",
"store_type": "RocksDB",
"store_param": {
"cache_size": 2048,
"compress": {"rate":16}
}
}
}
}
' http://master_server/space/$db_name/_create
入口函数是func (ca *clusterAPI) createSpace(c *gin.Context)
这个函数工作就是根据请求构建了一个space对象。space.Engine
记录了引擎信息,由请求传入,space.Version
被初始化为1。然后调用createSpaceService
函数,这个函数有点长,我们一步一步来看
第一步是解析Properties
,space.Properties
表示这个表的每一项,类似于sql表的每一列
_, err = mapping.SchemaMap(space.Properties) // 解析Properties
解析函数是SchemaMap
, 就是确认是否合法
下一步:确认这个要创建的space是否存在,存在的话就返回
下一步:扫描所有Server
,存在servers
数组里,并为space生成唯一id
servers, err := ms.Master().QueryServers(ctx)
spaceID, err := ms.Master().NewIDGenerate(ctx, entity.SpaceIdSequence, 1, 5*time.Second)
space.Id = spaceID
下一步:这步很关键,可能能解决提出的问题
width := math.MaxUint32 / space.PartitionNum
for i := 0; i < space.PartitionNum; i++ {
partitionID, err := ms.Master().NewIDGenerate(ctx, entity.PartitionIdSequence, 1, 5*time.Second) // 自增
space.Partitions = append(space.Partitions, &entity.Partition{
Id: entity.PartitionID(partitionID),
SpaceId: space.Id,
DBId: space.DBId,
Slot: entity.SlotID(i * width),
})
}
创建space的请求中包含了partition_num
,这一步根据partition_num
创建新的对应数量的partition信息,挂在space
下面,但是这时候还不确定对应的具体机器
下一步 关键,能回答问题就在这里
//peak servers for space
var paddrs [][]string
for i := 0; i < len(space.Partitions); i++ {
if addrs, err := ms.generatePartitionsInfo(
servers,
serverPartitions, //计算好每个server的partition数量
space.ReplicaNum,
space.Partitions[i]
); err != nil {
return err
} else {
paddrs = append(paddrs, addrs)
}
}
哪个partition用哪个机器的问题就在这里,看完generatePartitionsInfo
函数就能回答了
根据space.Partitions
也就是partition的数量,对每个partition调用一次generatePartitionsInfo
函数。参数serverPartitions
记录了每个server下面有多少partition
generatePartitionsInfo
函数先给所有server按照partition数量升序排列,然后根据备份数量Replicas
取最少的几个server,记录下他们的地址RpcAddr()
和ID
加入数组返回
这一步,创建的每个partition
用的机器地址就存在了二维数组paddrs[][]
里,这就回答了上面的问题啦
下一步就是要给这些机器发送创建partition的请求:
client.CreatePartition(addr, space, partition.Id)
最后check一下所有partition是否ok
ok,问题解决
createPartition
这个要看ps里