商协会系统里有一类查询很折腾人:谁介绍谁入会、谁和谁是上下游供需、某个会员的介绍链能拉开几层。这类关系天然是图,用关系型数据库做,要么靠多张联表,要么递归查询,字段一变就要改表,成本不低。会员之间靠介绍入会、靠上下游做生意,这套关系网恰恰是商协会很有价值的数据资产,却也容易被表格埋掉。
我们这边在用的方案是 Tablestore 宽表。宽表的好处是列可以稀疏、可以随业务加,不用预先定死结构。我们把"会员"和"关系"两类数据放在一张表里,用不同类型的行来区分。
建表时只需要定义主键,属性列按需写入:
CreateTableRequest request = new CreateTableRequest();
request.setTableName("member_graph");
TableMeta tableMeta = new TableMeta();
tableMeta.addPrimaryKeyColumn("pk", PrimaryKeyType.STRING);
tableMeta.addPrimaryKeyColumn("sk", PrimaryKeyType.STRING);
request.setTableMeta(tableMeta);
写入一个会员节点和一条介绍关系,属性列自由扩展:
RowPutChange memberRow = new RowPutChange("member_graph");
memberRow.setPrimaryKey(buildPk("member", "m_1001"));
memberRow.addColumn("name", ColumnValue.fromString("某制造企业"));
memberRow.addColumn("industry", ColumnValue.fromString("配套加工"));
RowPutChange relRow = new RowPutChange("member_graph");
relRow.setPrimaryKey(buildPk("rel", "r_1001_1002"));
relRow.addColumn("from", ColumnValue.fromString("m_1001"));
relRow.addColumn("to", ColumnValue.fromString("m_1002"));
relRow.addColumn("type", ColumnValue.fromString("introduce"));
光有宽表还不够,查询"某会员介绍的所有人""某行业上下游"要快,得靠多元索引。建多元索引也很轻,指定要索引的列即可,后续加列不影响已有索引结构。我们给 industry、type 这些列建了多元索引,检索时直接走索引而不是全表扫。想查"由 1001 介绍入会、且行业为配套加工"的会员,一条索引查询就能返回,不用再回原表做多层 join。
为什么不用关系型?关系场景里属性和关系都容易变:今天加"是否供需对接",明天加"对接品类"。宽表加列零成本,关系型要 DDL 改表、还得迁数据。对商协会这种组织来说,schema 演进频繁,宽表明显更省心。当然,如果关系很规整、几乎不变,关系型也够用;只有当 schema 总在变、查询爱跨层,宽表的价值才真正显出来。
我们这边在用的未来漫城,把会员关系图谱放在 Tablestore 上,秘书处查"这个人背后站着多少介绍关系"从原来的慢查询变成了秒回。小步快跑地把关系数据沉淀下来,比一上来就搭重型图数据库更贴合这类组织的实际节奏。