Skip to content

kv&排行榜介绍

注意:

  • !!!3.0中kv和排行榜仅支持Data.Map 接口,不再支持2.0的CloudSever接口调用,千万不要混合使用,会造成数据丢失。
  • !!!3.0中kv和排行榜仅支持Data.Map 接口,不再支持2.0的CloudSever接口调用,千万不要混合使用,会造成数据丢失。
  • !!!3.0中kv和排行榜仅支持Data.Map 接口,不再支持2.0的CloudSever接口调用,千万不要混合使用,会造成数据丢失。

排行榜、K/V数据介绍

排行榜和K/V数据存储功能将为开发者开发具有网络化、功能更加丰富的地图提供更有力的保障。开发者不仅可以对玩家个人数据,还可以对地图维度的全局数据(如战力排行榜,竞速时间排行榜等)进行管理、保存、使用。

K/V数据

为一般普通的数据存储,其如同一张表,而表里面可以存任何类型的数据,并且可以存储多个不同关键字(Key)的表。

存储中的每个值(value)都由一个独特的键(key)索引,可以往里面添加任意值。一个key只能对应一个value,value类型可以是数值,可以是字符串,也可以是json字符串等。例如玩家相关数据可如下所示存取:

键 (KEY)值 (VALUE)
level50
attr{ "flowers": 100, "level": 6, "vip": 3,"played_count": 13, "label": "超神"}
coin78000

通过相同的数据集名称和键(key),就可以针对数据进行存/取操作。

排行榜数据

为有序数据集合,其数据存储的类型为数值类型,系统会按照数值自动进行排序,故我们把它简称为排行榜数据,其可以存储多个维度的数据进行自动排序。

  1. 排行榜可以实现地图排名前1000位玩家的数据,实现全图排行榜功能
  2. 排行榜默认排序方式为(升序)从小到大,使用get数据时,正数代表升序,负数代表降序
  3. 排行榜只能存正整数,存入的小数会进行取整数部分

排行榜基本格式如下:

键(KEY)(玩家迷你号)值 (VALUE)
100005
100008510
1000035412

数据保存的有效期

  1. 地图审核通过且状态属于有效中,其所存储的数据即处于有效中。
  2. 地图取消上传,数据将会继续保留30天,如果30天内未重新上传,数据将会彻底清除,无法恢复。
  3. 地图开发者主动调取删除接口,或者地图删除,则数据将会彻底清除,无法恢复。

注意:在联机或单机情况下,该功能只能实现当前地图,当前房间数据的临时存储,房间关闭后数据就会丢失。

       验证数据存储的正确性,必须在**云服环境下(确保在云服房间内)**才可正确的获取到所存储的数据,在单机或者联机下将无法获取到云服下的全部数据。

如何开启K/V存储和排行榜

开发者可以在开发者工具->触发器->变量库->全局变量中,通过选择排行榜或者表来进行存储两种类型的数据。如图所示:

针对排行榜及表进行存取操作,开发者可在Lua中选择需要执行的操作动作,具体的操作动作,如下图所示:

序号函数名函数描述
1SetValueAndCallBack(...)回调设置kv、排行榜数据
2SetValueAndBlock(...)阻塞设置kv、排行榜数据
3RemoveValueAndCallBack(...)回调删除指定key的数据
4RemoveValueAndBlock(...)阻塞删除指定key的数据
5UpdateValueAndCallback(...)更新指定key的数据
6GetValueAndCallBack(...)回调获取kv、排行榜数据
7GetValueAndBlock(...)阻塞获取kv、排行榜数据
8GetIndexValueAndCallback(...)获取排行榜指定排名索引的值
9GetIndexValueAndBlock(...)获取排行榜指定排名索引的值
10GetNumValuesAndCallback(...)获取排行榜指定前num个值
11GetRangeValuesAndCallback(...)获取排行榜值为min~max区间的所有值
12SetRankValueAndBlock(...)阻塞设置排行榜的值
13ClearData(...)清空kv表/排行榜

存储限制

系统为保障存/取数据过程顺利,会对数据存储设定一些限制,以便给与玩家最好的体验。如果超过设定的限制,玩家数据将会存在丢失的风险。

每个游戏内的请求服务器的上限,系统会根据同时在线的玩家数量来动态调配,允许一定数量的数据存/取请求(更多的玩家意味有更高的配额更多的数据),具体的如下表:

请求类型函数每分钟请求上限备注
设置SetValueAndCallBack(...)
SetValueAndBlock(...)
RemoveValueAndCallBack(...)
RemoveValueAndBlock(...)
UpdateValueAndCallback(...)
30 + numPlayers × 10每分钟该函数允许被请求的次数,超过上限将会被限制请求
获取GetValueAndCallBack(...)
GetValueAndBlock(...)
30 + numPlayers × 10所有列出的函数共享此限制 每分钟这些函数被请求的次数之和不能超过上限,否则请求将会被限制
有序数据集(排行榜)GetIndexValueAndBlock(...)
GetNumValuesAndCallback(...)
GetRangeValuesAndCallback(...)
ClearData(...)
5 + numPlayers × 2所有列出的函数共享此限制 每分钟这些函数被请求的次数之和不能超过上限,否则请求将会被限制

数据长度限制

除了请求频率,数据存储同时也会限制每个条目可使用的数据量。其中键(key)、名称和数据都需要在一定的字符长度内,存储的数据量也有限制。具体的限制,如下表:

组成最大字符数备注
l键(key)50键(key)建议控制在50字符以内
名称50名称建议控制在50字符以内
数据2,000,000数据建议控制在2,000,000字符以内

由于键、名称是字符串,因此可以使用 string.len() 检查它们的长度。数据也保存为数据存储中的字符串,不管其初始类型如何,我们都可以使用 Lua 数据转换为序列化 JSON 表的 JSONEncode() 函数检查数据的大小。

全局KV数据并发读写

**全局KV数据并发读写,主要保证了多个玩家同时对一个KEY进行读写操作时的唯一性,确保读写成功,并且获取最新值。
****应用场景 包括但不仅限于宗门系统,拍卖行系统,抢购系统等,开发者可根据自己的业务需要,选择使用。**

普通的[SetValueAndBlock] 和[SetValueAndCallBack] 接口已经满足KV数据的绝大多数存取需求,但是当需要实现全服共同宗门系统(全局属性,例如总人数和贡献值等)、全服共同拍卖行(售卖物品的剩余数量)等大型跨游戏房间功能的时候,容易出现数据的彼此覆盖,没法保证全局唯一性。

为保证全局唯一性,我们特意设计了全新的UpdateValueAndCallback(...)接口。使用该接口,开发者仅需自己处理一个回调函数,在函数中处理自己的数据的逻辑。接口底层会自动重试,直至数据修改成功

注意:此接口只在云服有效,不要单机或联机测试这个接口!

注意:**此**接口只在云服有效,不要单机或联机测试这个接口!!

注意:**此**接口只在云服有效,不要单机或联机测试这个接口!!!

若遇到冲突、失败的情况,接口会自动回传最新的数据值,并调用开发者的回调函数,按照开发者自己设定的逻辑进行更新数据,确保了数据不出错,不会被其他玩家覆盖。

callback函数示例说明

UpdateValueAndCallback(...)

  • 参数及类型:
    • varId:stringkv表变量ID
    • playerId:number玩家uin
    • key:string键值(数值会转换成字符串)
    • value:string,number,boolean具体值
  • 返回值及类型:
    • ret:bool是否成功 (全局云KV变量可用)
  • 该方法的主要作用: 安全更新表中某个key下的值,并发存取数据,确保Key下的值唯一性
  • 具体使用案例如下:
lua
-- ret只有0和2两种值
-- callback至少会调用两次,首次调用ret必然为0,用于设置数值
-- 第2次或第2次以上调用callback时,如果设置失败会再次调用callback,此时ret为0;如果设置成功则ret为ErrorCode.OK,不会再次设置
    local function GlobalKVCallback(code,key,value)

        Chat:SendChat(table.concat({"UpdateValueAndCallback code = ",tostring(code), "key = ",tostring(key), "value = ",tostring(value)}, " "))

        value = json.decode(value) or value

        print("code", code , "key", key, "value", value)

        if code == ErrorCode.KV_UPDATE_SET then  -- 需要返回更新设置的最终值

            value = value or {} 

            value.updatevalue = 999

            return json.encode(value) -- 需要返回更新设置的最终值 并且序列化结构

        elseif code == ErrorCode.KV_UPDATE_GET then

            print("获取到的当前最新的值:", value)

        elseif code == ErrorCode.OK then

            print("更新完成")

        else 

            print("更新失败")

        end

    end

    local result = Data.Map:UpdateValueAndCallback(GlobalKV, nil, "GlobalKV_Key2", GlobalKVCallback)

    if result then

        print("调用成功")

    else

        print("调用失败")

    end

普通KV存储 VS 全局KV数据并发读写

普通KV存储set操作时,执行速度更快一些。直接保存key对应的数据,只会有写入时的每分钟请求次数限制。但是在多服同时对同一个KEY进行写数据的时候容易造成数据不一致。

全局KV数据并发读写(UpdateValueAndCallback(...))执行更慢,因为修改之前会读取最新的值,然后尝试写入。因此该接口同时受到读取和写入的每分钟请求次数限制。另外,它第一次操作某个key时,因为数据不存在会返回空值,此时依然是在callback里面处理,并提交首次有效数据给底层。

注意事项

1.UpdateValueAndCallback(...)接口无需事先设置key对应的数据,因为数据都在callback里面提交,从无到有的第一次数据也是这里提交。这样才能避免同时对同一个KEY的数据操作时,彼此冲突覆盖问题;

2.非必要慎用UpdateValueAndCallback(...)接口,性能降低。只有业务场景,必须要对同一个key的数据进行写操作时保证唯一性、正确性的场景和功能时,才推荐使用该接口;

3.使用UpdateValueAndCallback(...)修改过的数据,请勿使用普通SetValueAndBlock接口去设置新值,因为会直接覆盖掉原来数据,没法保证唯一、正确性,而且有可能导致其它问题;

4.对同一个key,**禁止混用****普通SetValueAndBlock和UpdateValueAndCallback(...)去存数据**;

5.此接口只在云服有效,不要单机或联机测试这个接口

6.每个key只能设置一个回调,设置后不能随意修改;

7.Update接口操作的数据,其set操作只能在callback里面,并采用返回最新value进行修改,千万别基于自己调用get拿到的数据进行操作,容易导致数据覆盖,出错。

最佳实践

**Q:****执行set操作,出现请求失败**

**A:**1、避免出现服务器CD限制,相同key的set操作需要间隔>=6s,避免同一秒多次set相同key的情况,导致数据没有保存成功。

2、避免无变化的数据,仍不停去执行set操作。给需要保存的数据设置needSave之类的布尔值标签,数据变化了设置为true,定时保存中根据needSave为true时候发送set请求保存,否则没必要发送到服务器。

3、避免绑定玩家行走、碰撞等高频行为而触发实时读写操作,否则一个玩家有可能一秒内触发数百个数据读写请求。

4、避免触发每分钟请求数QPM限制。

5、妥善处理set或get返回失败的情况,可以创建请求失败列,合理安排时机重试几次,一旦成功就从失败列表移除。

**Q:****一个游戏可以设置多个排行榜**

**A:**1、一个游戏可以包括多个维度的排行榜,如:得分排行榜,击杀Boss次数排行榜,速度排行榜,时长排行榜,技能点数排行榜等。

2、如果游戏里有多个排行榜,每个榜的刷新时间要错开。

**Q:****排行榜最多可以设置多少位排名?**

**A:**1、单个排行榜最多可存储1万名。

2、建议排行榜展示名次最好不要超过TOP 30,最多展示前100名。展示的名次越多,参与排行的人数越多,越影响性能,导致响应变慢;设计的时候需要做合理的取舍。

**Q:****如何合理的进行set,get操作?**

**A:**1、避免零值(或初始值)参与排行榜,会出现所有玩家都进行set操作了一次。

2、避免低于排行榜最后一名的数据也set操作。假设你的排行榜展示前50名,如果第50名已经有100分了,那么对于分数低于100分的玩家就无需添加到排行榜里面去。

3、参与排行的数值,比如经验,等级,杀怪数量等,这几个数值可以序列化成json字符串保存在一个kv中,避免多次拉取数据的情况。

4、假设某游戏有经验,等级,杀怪数3个排行榜,那么可以一个玩家一个json保存在kv表里面,key为玩家uin,value为json:

lua
{"exp": 888999,"lvl": 7,"kmonster": 39}

5、区分游戏商店购买、任务、成就等模块的配置和需要持久化的状态数据。

**Q:****配置文件建议不设置到数据储存中**

A: 通用配置可以放脚本或另外一个全局表里,没有必要存储在kv表里。

lua
"FindMaHongJun_5":{"questName":"FindMaHongJun_5","questProg":0,"questProgAll":1,"questSta":"no"}

这种可以简化,假设TaskID:10086表示该任务,那么

lua
"10086":[0,1,0]

数组第1个元素表示questProg,第2个元素表示questProgAll,第三个元素表示questSta。

排行榜案例

参考2.0文档 https://dev-wiki.mini1.cn/wiki/673b36153ffc6baf0859d33a