HTTP/1.1 200 OK
Server: Tengine/2.0.1
Date: Mon, 24 Nov 2014 09:42:13 GMT
Content-Type: application/x-thrift
Transfer-Encoding: chunked
Connection: keep-alive
Keep-Alive: timeout=360
X-Xiaomi-Error-Code: 0
X-Xiaomi-Timestamp: 1416822133
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
[1,"describeTable",2,0,{"1":{"rec":{"1":{"i32":26},"2":{"str":"The table which you are attempting to access does not exist"},"3":{"str":"Table not found [test]"},"4":{"str":"omqt46b7"}}}}]
小米结构化数据存储开发文档
如果您在集成过程中遇到任何问题,都可以添加QQ群:385428920 ,群中会有 工程师解答您的问题。
SDK地址
结构化数据存储(SDS)是一种高效,全面托管的分布式NoSQL数据库服务,为应 用开发者提供快速、安全的数据存储服务。主要功能有:
结构化存储的数据模型是表格模型,属于强schema模型,即一个表由多行相同格式的记录组成。 从逻辑上,每行数据是一组属值的集合,属性支持如下数据类型:
表的数据在物理组织上支持两种索引结构:
另外,在表的定义中,可以将一到多个表属性定义为 **实体组键(Entity Group Key)**,在物理组织上, 实体组键是记录主键和二级索引的前缀。对于实体组键,可以定义是否进行哈希分布,开启此选项后, 会添加取值范围为256的哈希值,从而实现负载均衡的效果,可用于消除请求热点,建议开启。需要注意, 打开此选项后,表中的数据将不再保证全局有序。一般情况下,没有特殊原因,强烈建议设置实体组键并开启哈希 (设计表的Schema以及估算请求量时,应保证每个实体组上的单位时间消耗的读写配额峰值尽可能的小,原则上 不应不超过1000/秒,否则将不保整个表的读写请求配额。实体组的数量的多少不影响性能,所以在满足业务查询 功能前提下,应该尽可能降低每个实体组上的读写配额,避免读写热点,尤其避免同时大量读写一个或少数几个 实体组中的记录)。 当实体组键开启哈希分布时,主键需要包含至少一个属性,如果schema中没有可以作为主键的属性,建议在主键中 使用一个默认的占位属性(例如,如果一个表只有一个userId作为实体组键属性,那么可以添加一个额外的recordId 属性作为主键,取值为常数0,表示某个用户下的第1条记录)。
对于实体组键,主键以及二级索引的属性的编码(KeySpec),可以通过asc选择决定是否是升序还是降序(例如, 整型降序编码时,其排序为…, 2, 1, 0, -1, …与正序相反)。对于扫描(Scan)操作,可以通过通过设置 reverse选项进行正序和逆序扫描,但是正序扫描效率高于逆序扫描,所以定义表结构的时候,需要根据关键路径 上的查询模式来决定编码顺序,尽量保证关键路径是正序扫描。
主记录(主键)行的组织形式为:
其中实体组前缀是可选的,仅当表定义了实体组键时存在。二级索引行的组织形式为:
前面已经提到,投影属性为主记录行的副本,与主数据行总是保证一致,且仅当eager索引才可以定义投影属性。
其中实体组键前缀的组成形式为:
开发者可以根据数据的读取模式,选择是否支持实体组键,以及是否定义二级索引。同时,二级索引需要根据 读写的比例选择类型,例如 Lazy索引 适合写入较多,读取较少且对延迟不敏感的情况。Eager索引 与 投影配合使用,适合读取相对频繁或者对读延迟比较敏感的场合。而只读数据则适合采用Immutable索引。 需要注意的是,目前不支持动态修改表的实体组键(包括哈希分布选项),主键,除索引类型外的其 他二级索引选项),以及属性的数据类型和编码方式,建表时需要慎重选择。表的普通属性可以增删, 但数据访问会有短暂停服(平均在2-3秒),建议在业务低峰期操作。 二级索引的类型可以修改,但仅允许Immutable改为Eager,适合当表中数据由不可更新改为可写的情况。 一种常见的场景是,建表初期导入已有数据,此时数据是只读的,不会存在更新的情况(需要开发者自己保证), 此时索引定义为Immutable,可以节约读配额,数据导入完成后开始线上服务之前,将索引改为Eager模式。
SDS存储是强一致的模型,即后续发生的读总能读到以前写入的数据。数据存储三备份,并且会异步的同步到备集群,备集群目前只提供只读功能,用于离线分析。
4.1 行级别的事务保证
对于同一次put(非batch)的记录数 据,能够保证原子性,例如,两个并发的put分别写入同一行的两个属性p和q: (p1, q1)和(p2, q2), 最终结果不会出现(p1, q2)或者(p2, q1)的情况。
4.2 实体组内的事务保证
属于同一事务组内的记录行,可以支持batch的原子性,可以在建表的时候进行配置是否开启,如果需要定义二级索引,则必须开启batch原子性。同一事务组内可以支持局部二级索引,并且索引跟主记录之间是强一致的。
4.3 自增操作支持
目前支持在整形数据类型上的自增操作
4.4 条件修改
SDS支持条件修改(put和delete),这样可以在应用层实现自己的同步逻辑(比如锁)。
以记事本应用为例,此应用支持通过浏览器和移动客户端两种方式存取数据。下面是基于关系数据库的表设计:
假设应用有四种访问模式:
5.1 表定义
对于此应用,可以转化为SDS的表定义:
5.2 创建client
以PHP为例,可通过以下代码创建client:
可以根据自己的需求,配置各个参数,例如,可以通过ClientFactory的第二个参数配置是否对操作超时 自动进行重试(如果自动重试需要注意操作的幂等性),另外还可以根据安全策略和性能考量选择http或者 https方式访问。此外,还可以自定义底层socket的建立连接和读写的超时时间。对于Java SDK来说,还可以 配置HttpClient是够为线程安全的,以及连接池的参数。
5.3 建表
建表过程可以通过开发者站的页面操作完成,如果需要,可以通过代码方式创建:
5.4 写数据
Put数据时,需要指定需要指定全部的实体组键(如果有定义)和主键的属性值,以及部分属性。特别的,对于lazy索引,所有写入的属性集合必须包含其全部或0个索引属性,即不允许只写入其部分属性。
对于笔记应用,插入和修改操作如下,其中修改操作利用了条件Put来检测并发修改冲突,代码如下:
5.5 根据主键读取
根据主键读取数据,需要指定全部的实体组键(如果有定义)和主键的属性值。其中对于返回值,可以通过attributes属性指定返回部分属性,没有指定时,则表示获取所有的属性:
5.6 扫描操作
除随机读一条数据之外,还可以进行范围扫描查询。可以选择使用主键(indexName不指定表示通过主键查询) 和二级索引查询。扫描可以指定查询范围,不指定时表示全表扫描。 查询范围定义的区间是*[startKey, stopKey),即两者全部属性都指定且相同时 查询的范围为空。其中startKey和stopKey*可以只指定主键或者对应索引的部分属性(必须为前缀),指定 前缀时,查询范围的区间为 [startKey前缀 + 后缀可能的最小值, stopKey前缀 + 后缀可能的最大值 + 1)。例如, startKey = {userId = “user1”}, stopKey = {userId = “user1”} 表示查询user1下的所有笔记。
另外,扫描操作还可以指定过滤条件,需要注意的是,被条件过滤掉的记录也要计算读配额(根据索引类型 记1个或2个读配额)。过滤条件的语法类似SQL的Where语句。需要注意,属性不存在表示值为null,除isnull和notnull操作符之外的所有其他表达式和函数调用结果都为null,如果表达式最终的值为null,则表达式的计算结果为false。
例如对于表中的一条记录记录:
判定结果取值为true的条件:
判定结果取值为false(包含最终表达式取值为null的情况)的条件:
如果定义了哈希分布,跨实体组进行扫描需要先分别确定在每个哈希桶中的范围分布,再分别对每个哈希桶进行局部扫描,最后对所有桶内扫描结果进行归并排序,因此会 引入较大性能开销,建议谨慎使用。 注:这种场景建议用户采用结构化存储MapReduce API(即将上线),MapReduce分布式计算框架将上述步骤分布到多台机器并行执行,从而极大提高扫描性能。
详细的API文档在galaxy-thrift-api子项目中。 目前提供了Java(包括Android), PHP, Python, Go以及 Javascript语言的SDK,其他语言的SDK如有需求可以自己实现。 结构化存储的API是通过Thrift定义,传输协议采用
TJSONProtocol。现在以
describeTableAPI为例,介绍SDK实现:describeTable调用对应的HTTP请求:对应的服务端响应:
6.1 身份认证
身份认信息是通过HTTP头
Authorization来传递。Authorization头由HttpAuthorizationHeader对象通过TJSONProtocol序列化得到:其中
userType,secretKeyId,secretKey为对应的认证信息。对于APP_SECRET方式登录,secretKeyId,secretKey分别为AppKey和AppSecret。 对于非安全传输通道(HTTP),不应当直接传递secretKey,而应采用通过secretKey对请求进行签名 (HMAC: Hash-based message authentication code)的方式进行身份认证。例如,对于示例中的请求:
其MD5值为
43a0cd31f7648b16a45f0371d58f8689。计算签名需要的HTTP头信息如下:其签名
HMAC_SHA1("WOqDHS8AbBHGhaOD2pvmCQ==", "sds.api.xiaomi.com\n1416822141\n43a0cd31f7648b16a45f0371d58f8689")为92543e103926a42afd4c2644f7d793753239333f。 最终得到HttpAuthorizationHeader结构:最终通过
TJSONProtocol序列化得到Authorization头:6.2 常见传输层错误
SDK相关的常见传输层错误对应的HTTP状态码如下:
其中,常见的错误
CLOCK_TOO_SKEWED和套接字超时错误的自动处理可参考现有的SDK实现。目前结构化存储根据用户需求,包含了不同配置的三个集群,同时提供HTTP/HTTPS两种接入方式:
7.1 在线集群
主集群
备集群
7.2 离线集群
8.1 常量与变量
8.2 操作符类表(按照优先级排列)
8.3 函数列表
目前支持的函数如下,如有新的需求,请联系技术支持。