表格存储的表由行组成,每一行包含主键和属性。本节将介绍表格存储数据的操作方法。

表格存储行简介

组成表格存储表的基本单位是行,行由主键和属性组成。其中,主键是必须的,且每一行的主键列的名称和类型相同;属性不是必须的,并且每一行的属性可以不同。更多信息请参见表格存储的数据模型概念

表格存储的数据操作有以下三种类型:

  • 单行操作

    • GetRow:读取单行数据。

    • PutRow:新插入一行。如果该行内容已经存在,则先删除旧行,再写入新行。

    • UpdateRow:更新一行。应用可以增加、删除一行中的属性列,或者更新已经存在的属性列的值。如果该行不存在,则新增一行。

    • DeleteRow:删除一行。

  • 批量操作

    • BatchGetRow:批量读取多行数据。

    • BatchWriteRow:批量插入、更新或者删除多行数据。

  • 范围读取

    • GetRange:读取表中一个范围内的数据。

表格存储单行操作

  • 单行写入操作

    表格存储的单行写操作有三种:PutRow、UpdateRow 和 DeleteRow。下面分别介绍每种操作的行为语义和注意事项:

    • PutRow:新写入一行。如果这一行已经存在,则这一行旧的数据会先被删除,再新写入一行。

    • UpdateRow:更新一行的数据。表格存储会根据请求的内容在这一行中新增列,修改或者删除指定列的值。如果这一行不存在,则会插入新的一行。但是有一种特殊的场景,若 UpdateRow 请求只包含删除指定的列,且该行不存在,则该请求不会插入新行。

    • DeleteRow:删除一行。如果删除的行不存在,则不会发生任何变化。

    应用程序通过设置请求中的 condition 字段来指定写入操作执行时,是否需要对行的存在性进行检查。condition 有三种类型:

    • IGNORE:不做任何存在性检查。

    • EXPECT_EXIST:期望行存在。如果该行存在,则操作成功;如果该行不存在,则操作失败。

    • EXPECT_NOT_EXIST:期望行不存在。如果行不存在,则操作成功;如果该行存在,则操作失败。

    condition 为 EXPECT_NOT_EXIST 的 DeleteRow、UpdateRow 操作是没有意义的,删除一个不存在的行是无意义的,如果需要更新不存在的行可以使用 PutRow 操作。

    如果操作发生错误,如参数检查失败、单行数据量过多、行存在性检查失败等等,会返回错误码给应用程序。如果操作成功,表格存储会将操作消耗的服务能力单元返回给应用程序。

    各操作消耗的写服务能力单元的计算规则如下:

    • PutRow:本次消耗的写 CU 为修改的行主键数据大小与属性列数据大小之和除以 4 KB 向上取整。若指定条件检查不为 IGNORE,还需消耗该行主键数据大小除以 4 KB 向上取整的读 CU。如果操作不满足应用程序指定的行存在性检查条件,则操作失败并消耗 1 单位写服务能力单元和 1 单位读服务能力单元。请参见 PutRow 详解。

    • UpdateRow:本次消耗的写 CU 为修改的行主键数据大小与属性列数据大小之和除以 4 KB 向上取整。UpdateRow 中包含的需要删除的属性列,只有其列名计入该属性列数据大小。若指定条件检查不为 IGNORE,还需消耗该行主键数据大小除以 4 KB 向上取整的读 CU。如果操作不满足应用程序指定的行存在性检查条件,则操作失败并消耗 1 单位写服务能力单元和 1 单位读服务能力单元。请参见 UpdateRow 详解。

    • DeleteRow:被删除的行主键数据大小除以 4 KB 向上取整。若指定条件检查不为 IGNORE,还需消耗该行主键数据大小除以 4 KB 向上取整的读 CU。如果操作不满足应用程序指定的行存在性检查条件,则操作失败并消耗 1 单位写服务能力单元。请参见 DeleteRow 详解。

    写操作会根据指定的 condition 情况消耗一定的读服务能力单元。

    示例

    下面举例说明单行写操作的写服务能力单元和读服务能力单元的计算。

    示例 1,使用 PutRow 进行如下行写入操作:

    //PutRow 操作
    //row_size=len('pk')+len('value1')+len('value2')+8Byte+1300Byte+3000Byte=4322Byte
    {
        primary_keys:{'pk':1},
        attributes:{'value1':String(1300Byte), 'value2':String(3000Byte)}
    }
    
    //原来的行
    //row_size=len('pk')+len('value2')+8Byte+900Byte=916Byte
    //row_primarykey_size=len('pk')+8Byte=10Byte
    {
        primary_keys:{'pk':1},
        attributes:{'value2':String(900Byte)}
    }
    

    读/写服务能力单元的消耗情况如下:

    • 将 condition 设置为 EXPECT_EXIST 时:消耗的写服务能力单元为 4322 Byte 除以 4 KB 向上取整,消耗的读服务能力单元为该行主键数据大小 10 Byte 除以 4 KB 向上取整。该 PutRow 操作消耗 2 个单位的写服务能力单元和 1 个单位的读服务能力单元。

    • 将 condition 设置为 IGNORE 时:消耗的写服务能力单元为 4322 Byte 除以 4 KB 向上取整,消耗 0 个读服务能力单元。该 PutRow 操作消耗 2 个单位的写服务能力单元和 0 个单位的读服务能力单元。

    • 将 condition 设置为 EXPECT_NOT_EXIST 时:指定的行存在性检查条件检查失败,该 PutRow 操作消耗 1 个单位的写服务能力单元和 1 个单位的读服务能力单元。

    示例 2,使用 UpdateRow 新写入一行:

    //UpdateRow 操作
    //删除的属性列列名长度计入 row_size
    //row_size=len('pk')+len('value1')+len('value2')+8Byte+900Byte=922Byte
    {
        primary_keys:{'pk':1},
        attributes:{'value1':String(900Byte), 'value2':Delete}
    }
    
    //原来的行不存在
    //row_size=0
    

    读/写服务能力单元的消耗情况如下:

    • 将 condition 设置为 IGNORE 时:消耗的写服务能力单元为 922 Byte 除以 4 KB 向上取整,消耗 0 个读服务能力单元。该 UpdateRow 操作消耗 1个单位的写服务能力单元和 0 个读服务能力单元。

    • 将 condition 设置为 EXPECT_EXIST 时:指定的行存在性检查条件检查失败,该 PutRow 操作消耗 1 个单位的写服务能力单元和 1 个单位的读服务能力单元。

    示例 3,使用 UpdateRow 对存在的行进行更新操作:

    //UpdateRow 操作
    //row_size=len('pk')+len('value1')+len('value2')+8Byte+1300Byte+3000Byte=4322Byte
    {
        primary_keys:{'pk':1},
        attributes:{'value1':String(1300Byte), 'value2':String(3000Byte)}
    }
    //原来的行
    //row_size=len('pk')+len('value1')+8Byte+900Byte=916Byte
    //row_primarykey_size=len('pk')+8Byte=10Byte
    {
        primary_keys:{'pk':1},
        attributes:{'value1':String(900Byte)}
    }
    

    读/写服务能力单元的消耗情况如下:

    • 将 condition 设置为 EXPECT_EXIST 时:消耗的写服务能力单元为 4322 Byte 除以 4 KB 向上取整,消耗的读服务能力单元为该行主键数据大小 10 Byte 除以 4 KB 向上取整。该 UpdateRow 操作消耗 2 个单位的写服务能力单元和 1 个单位的读服务能力单元。

    • 将 condition 设置为 IGNORE 时:消耗的写服务能力单元为 4322 Byte 除以 4 KB 向上取整,消耗 0 个读服务能力单元,该 UpdateRow 操作消耗 2 个单位的写服务能力单元和 0 个单位的读服务能力单元。

    示例 4,使用 DeleteRow 删除不存在的行:

    //原来的行不存在
    //row_size=0
    
    //DeleteRow 操作
    //row_size=0
    //row_primarykey_size=len('pk')+8Byte=10Byte
    {
        primary_keys:{'pk':1},
    }
    

    修改前后的数据大小均为 0,无论读写操作成功还是失败至少消耗 1 个单位服务能力单元。因此,该 DeleteRow 操作消耗 1 个单位的写服务能力单元。

    读/写服务能力单元的消耗情况如下:

    • 将 condition 设置为 EXPECT_EXIST 时:消耗的写服务能力单元为该行主键数据大小 10 Byte 除以 4 KB 向上取整,消耗的读服务能力单元为该主键数据大小 10 Byte 除以 4 KB 向上取整。该 DeleteRow 操作消耗 1 个单位的写服务能力单元和 1 个单位的读服务能力单元。

    • 将 condition 设置为 IGNORE 时:消耗的写服务能力单元为该行主键数据大小 10 Byte 除以 4 KB 向上取整,消耗 0 个读服务能力单元。该 DeleteRow 操作消耗 1 个单位的写服务能力单元和 0 个单位的读服务能力单元。

    更多信息请参见 API Reference 中的 PutRowUpdateRowDeleteRow 章节。

  • 单行读取操作

    表格存储的单行读操作只有一种:GetRow。

    应用程序提供完整的主键和需要返回的列名。列名可以是主键列或属性列,也可以不指定要返回的列名,此时请求返回整行数据。

    表格存储根据被读取的行主键的数据大小与实际读取的属性列数据大小之和计算读服务能力单元。将行数据大小除以 4 KB 向上取整作为本次读取操作消耗的读服务能力单元。如果操作指定的行不存在,则消耗 1 单位读服务能力单元,单行读取操作不会消耗写服务能力单元。

    示例

    使用 GetRow 读取一行消耗的写服务能力单元的计算:

    //被读取的行
    
    //row_size=len('pk')+len('value1')+len('value2')+8Byte+1200Byte+3100Byte=4322Byte
    {
        primary_keys:{'pk':1},
        attributes:{'value1':String(1200Byte), 'value2':String(3100Byte)}
    }
    
    //GetRow 操作
    //获取的数据 size=len('pk')+len('value1')+8Byte+1200Byte=1216Byte
    {
        primary_keys:{'pk':1},
        columns_to_get:{'value1'}
    }
    

    消耗的读服务能力单元为 1216 Byte 除以 4 KB 向上取整,该 GetRow 操作消耗 1 个单位的读服务能力单元。

    更多信息请参见 API Reference 的 GetRow 章节。

多行操作

表格存储提供了 BatchWriteRow 和 BatchGetRow 两种多行操作。

  • BatchWriteRow 用于插入、修改、删除一个表或者多个表中的多行记录。BatchWriteRow 操作由多个 PutRow、UpdateRow、DeleteRow 子操作组成。BatchWriteRow 的各个子操作独立执行,表格存储会将各个子操作的执行结果分别返回给应用程序,可能存在部分请求成功、部分请求失败的现象。即使整个请求没有返回错误,应用程序也必须要检查每个子操作返回的结果,从而拿到正确的状态。BatchWriteRow 的各个子操作单独计算写服务能力单元。

  • BatchGetRow 用于读取一个表或者多个表中的多行记录。BatchGetRow 各个子操作独立执行,表格存储会将各个子操作的执行结果分别返回给应用程序,可能存在部分请求成功、部分请求失败的现象。即使整个请求没有返回错误,应用程序也必须要检查每个子操作返回的结果,从而拿到正确的状态。BatchGetRow 的各个子操作单独计算读服务能力单元。

更多信息请参见 API Reference 中的 BatchWriteRow BatchGetRow 章节。

范围读取操作

表格存储提供了范围读取操作 GetRange,该操作将指定主键范围内的数据返回给应用程序。

表格存储表中的行按主键进行从小到大排序,GetRange 的读取范围是一个左闭右开的区间。操作会返回主键属于该区间的行数据,区间的起始点是有效的主键或者是由 INF_MIN 和 INF_MAX 类型组成的虚拟点,虚拟点的列数必须与主键相同。其中,INF_MIN 表示无限小,任何类型的值都比它大;INF_MAX 表示无限大,任何类型的值都比它小。

GetRange 操作需要指定请求列名,请求列名中可以包含多个列名。如果某一行的主键属于读取的范围,但是不包含指定返回的列,那么请求返回结果中不包含该行数据。不指定请求列名,则返回完整的行。

GetRange 操作需要指定读取方向,读取方向可以为正序或逆序。假设同一表中有两个主键 A 和 B,A < B。如正序读取 [A, B),则按从 A 至 B 的顺序返回主键大于等于 A、小于 B 的行。逆序读取 [B,A),则按从 B 至 A 的顺序返回大于 A、小于等于 B 的数据。

GetRange 操作可以指定最大返回行数。表格存储按照正序或者逆序最多返回指定的行数之后即结束该操作的执行,即使该区间内仍有未返回的数据。

GetRange 操作可能在以下几种情况下停止执行并返回数据给应用程序:

  • 返回的行数据大小之和达到 4 MB。

  • 返回的行数等于 5000。

  • 返回的行数等于最大返回行数。

  • 当前剩余的预留读吞吐量已被全部使用,余量不足以读取下一条数据。同时 GetRange 请求的返回结果中还包含下一条未读数据的主键,应用程序可以使用该返回值作为下一次 GetRange 操作的起始点继续读取。如果下一条未读数据的主键为空,表示读取区间内的数据全部返回。

表格存储计算读取,区间起始点到下一条未读数据的起始点的,所有行主键数据大小与实际读取的属性列数据大小之和。将数据大小之和除以 4 KB 向上取整计算消耗的读服务能力单元。例如,若读取范围中包含 10 行,每行主键数据大小与实际读取到的属性列数据之和占用数据大小为 330 Byte,则消耗的读服务能力单元为 1(数据总和 3.3 KB,除以 4 KB 向上取整为 1)。

示例

下面举例说明 GetRange 操作的行为。假设表的内容如下,PK1、PK2 是表的主键列,类型分别为 String 和 Integer;Attr1、Attr2 是表的属性列。

PK1 PK2 Attr1 Attr2
'A' 2 'Hell' 'Bell'
'A' 5 'Hello' 不存在
'A' 6 不存在 'Blood'
'B' 10 'Apple' 不存在
'C' 1 不存在 不存在
'C' 9 'Alpha' 不存在

示例 1,读取某一范围内的数据:

//请求
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", STRING, "A"), ("PK2", INTEGER, 2)
exclusive_end_primary_key: ("PK1", STRING, "C"), ("PK2", INTEGER, 1)

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 2)
    	attribute_columns:("Attr1", STRING, "Hell"), ("Attr2", STRING, "Bell")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 5)
    	attribute_columns:("Attr1", STRING, "Hello")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 6)
    	attribute_columns:("Attr2", STRING, "Blood")
      },
      {
    	primary_key_columns:("PK1", STRING, "B"), ("PK2", INTEGER, 10)
    	attribute_columns:("Attr1", STRING, "Apple")
      }
    }

示例 2,利用 INF_MIN 和 INF_MAX 读取全表数据:

//请求
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", INF_MIN)
exclusive_end_primary_key: ("PK1", INF_MAX)

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 2)
    	attribute_columns:("Attr1", STRING, "Hell"), ("Attr2", STRING, "Bell")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 5)
    	attribute_columns:("Attr1", STRING, "Hello")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 6)
    	attribute_columns:("Attr2", STRING, "Blood")
      },
      {
    	primary_key_columns:("PK1", STRING, "B"), ("PK2", INTEGER, 10)
    	attribute_columns:("Attr1", STRING, "Apple")
      }
      {
    	primary_key_columns:("PK1", STRING, "C"), ("PK2", INTEGER, 1)
      }
      {
    	primary_key_columns:("PK1", STRING, "C"), ("PK2", INTEGER, 9)
    	attribute_columns:("Attr1", STRING, "Alpha")
      }
    }

示例 3,在某些主键列上使用 INF_MIN 和 INF_MAX:

//请求
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", STRING, "A"), ("PK2", INF_MIN)
exclusive_end_primary_key: ("PK1", STRING, "A"), ("PK2", INF_MAX)

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 2)
    	attribute_columns:("Attr1", STRING, "Hell"), ("Attr2", STRING, "Bell")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 5)
    	attribute_columns:("Attr1", STRING, "Hello")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 6)
    	attribute_columns:("Attr2", STRING, "Blood")
      }
    }

示例 4,逆序读取:

//请求
table_name: "table_name"
direction: BACKWARD
inclusive_start_primary_key: ("PK1", STRING, "C"), ("PK2", INTEGER, 1)
exclusive_end_primary_key: ("PK1", STRING, "A"), ("PK2", INTEGER, 5)

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "C"), ("PK2", INTEGER, 1)
      },
      {
    	primary_key_columns:("PK1", STRING, "B"), ("PK2", INTEGER, 10)
    	attribute_columns:("Attr1", STRING, "Apple")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 6)
    	attribute_columns:("Attr2", STRING, "Blood")
      }
    }

示例 5,指定列名不包含 PK:

//请求
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", STRING, "C"), ("PK2", INF_MIN)
exclusive_end_primary_key: ("PK1", STRING, "C"), ("PK2", INF_MAX)
columns_to_get: "Attr1"

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
		attribute_columns: {"Attr1", STRING, "Alpha"}
      }
    }

示例 6,指定列名中包含 PK:

//请求
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", STRING, "C"), ("PK2", INF_MIN)
exclusive_end_primary_key: ("PK1", STRING, "C"), ("PK2", INF_MAX)
columns_to_get: "Attr1", "PK1"

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "C")
      }
      {
    	primary_key_columns:("PK1", STRING, "C")
    	attribute_columns:("Attr1", STRING, "Alpha")
      }
    }

示例 7,使用 limit 和断点:

//请求 1
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", STRING, "A"), ("PK2", INF_MIN)
exclusive_end_primary_key: ("PK1", STRING, "A"), ("PK2", INF_MAX)
limit: 2

//响应 1
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 2)
    	attribute_columns:("Attr1", STRING, "Hell"), ("Attr2", STRING, "Bell")
      },
      {
    	primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 5)
    	attribute_columns:("Attr1", STRING, "Hello")
      }
    }
next_start_primary_key:("PK1", STRING, "A"), ("PK2", INTEGER, 6)

//请求 2
table_name: "table_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", STRING, "A"), ("PK2", INTEGER, 6)
exclusive_end_primary_key: ("PK1", STRING, "A"), ("PK2", INF_MAX)
limit: 2

//响应 2
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", STRING, "A"), ("PK2", INTEGER, 6)
    	attribute_columns:("Attr2", STRING, "Blood")
      }
    }

示例 8,使用 GetRange 操作消耗的读服务能力单元计算:

在以下表中执行 GetRange 操作,其中 PK 1 是表的主键列,Attr1、Attr2 是表的属性列。

PK1 Attr1 Attr2
1 不存在 String(1000Byte)
2 8 String(1000Byte)
3 String(1000Byte) 不存在
4 String(1000Byte) String(1000Byte)
//请求
table_name: "table2_name"
direction: FORWARD
inclusive_start_primary_key: ("PK1", INTEGER, 1)
exclusive_end_primary_key: ("PK1", INTEGER, 4)
columns_to_get: "PK1", "Attr1"

//响应
cosumed_read_capacity_unit: 1
rows: {
      {
	    primary_key_columns:("PK1", INTEGER, 1)
      },
      {
    	primary_key_columns:("PK1", INTEGER, 2),
    	attribute_columns:("Attr1", INTEGER, 8)
      },
      {
    	primary_key_columns:("PK1", INTEGER, 3),
    	attribute_columns:("Attr1", STRING, String(1000Byte))
      },
    }

此次 GetRange 请求中:

  • 获取的第一行数据大小为:len ('PK1') + 8 Byte = 11 Byte

  • 第二行数据大小为:len ('PK1') + 8 Byte + len ('Attr1') + 8 Byte = 24 Byte

  • 第三行数据大小为:len ('PK1')+ 8 Byte + len ('Attr1') + 1000 Byte = 1016 Byte

消耗的读服务能力单元为获取的三行数据之和 11 Byte + 24 Byte + 1016 Byte = 1051 Byte 除以 4 KB 向上取整,该 GetRange 操作消耗 1 个单位的读服务能力单元。

更多详细信息请参见 API Reference 中的 GetRange 章节。

最佳实践

表格存储数据操作的最佳实践

使用表格存储 SDK 进行数据操作

使用 TableStore Java SDK 进行数据操作

使用 TableStore Python SDK 进行数据操作