索引表设计
在电商项目中, 物理库存系统是个极其重要的系统, 订单支付后, 就会开始来占用物理库存一般情况下, 库存系统都是要分库的, 因为主要的操作是写操作, 例如占用 / 释放 / 取消等写操作使用分库可以降低数据库写的压力尽管写操作为主, 但是读操作也是有的比如说, 库存占用的时候, 得先查询是否有库存, 而这个查询操作并不都会带上分库因子 (用于路由到具体的某个数据库), 而是一些比较宽松的查询条件, 这些查询条件对应的数据可能分布在不同的数据库上这个时候为了查询的方便, 会构建一个索引表这个索引表是存在另外的单独的一个数据库中, 不会再分库了的
索引表的设计也分不同情况, 大体可以分三种
1 查询字段 + 数据库主键
把查询字段放到索引表, 还需要把对应的数据库主键 ID 也放置进去当查询请求到来时, 根据查询条件找出对应的数据主键, 再根据数据主键路由到对应的存有完整业务数据的数据分库上这种方案呢索引表占用空间小, 可以支撑很久但是要找出业务数据, 还是需要路由到分库上另外, 如果要把索引表的数据存储到 ES 搜索引擎上的话, 这种方式就不行了因为索引表中没有外部系统要的业务数据所以当时的库存系统没有使用这种索引表设计方案
2 查询字段 + 数据库主键 + 需要展示的业务字段
这种方案呢当请求到来的时候, 直接查询索引表即可无需根据主键路由到分库了同时如果要结合 ES 的话可以直接把索引表的数据弄到 ES 上即可后续直接让 ES 暴露查询接口即可目前我在唯品会参与的物理库存项目中, 是使用这种方式的但是这个方案也有个缺点就是索引表的体积比较大, 后续数据量一大的话, 也是个问题能不能优化一下呢?
3 索引表拆分
上面说到的第二种方案, 索引表的膨胀可能很快, 可以考虑将索引表拆分比如说: 索引表只是保存查询条件和主键, 而需要展示给外部系统的数据, 另外存储在单独的表上比如叫 index_detail 表这个表拥有索引表的主键这样的话, 当查询请求到来的时候, 先从索引表查询到主键, 再根据主键从 index_detail 表中查询出数据当然这样做的话 ES 的数据来源就变成多张表了, 不过这是可以接受的
如何把业务数据写入到索引表
使用 MQ
一般来说, 构建索引数据是使用单独一个应用来做的比如叫 data-index 域这个域会从消息队列中读取消息, 用于构建索引数据当业务数据发生变化后, 生产者发送 MQ 消息到队列上
这里的消息设计也分两种情况一种是消息只是带有数据主键和操作类型 (ADD/Update/DELETE), 消费者拿到主键后再去 DB 获取完整的数据并插入到索引表中另一种方案呢, 是消息包含了大部分需要的字段, 消费者拿到消息后直接把数据插入到索引表中这两种消息设计, 我在实际项目中都有用过
直接操作 DB
这种方案呢就比较粗暴, 直接配置一个索引表库的数据源, 当业务数据发生变化时, 使用 Mybatis 或者 JDBC 把数据更新到索引表中一般不建议这么做, 一来构建索引数据的逻辑跟数据的 CRUD 操作融合在一起了二来, 操作其他数据库的数据, 要么通过接口的方式, 要么由单独的域来做建议还是使用 MQ 的方式来构建索引数据
如何把索引表数据弄到 ES 上
监听数据库表的数据变化
像在唯品会这边, 自研了一个叫 VDP 的组件, 使用 storm job 去监听索引表数据的变化, 一旦有变化, 就把数据同步到队列中, ES 直接从队列中获取数据, 并存储到 ES 上
这种方案的好处是, 我们无需写任何代码, 数据自动可以同步到 ES 上
使用 MQ
如果公司内部没有开发 VDP 这样的组件, 可以通过发送 MQ 消息的方式来将索引表的数据同步数据到 ES 上
让 ES 暴露 CUD 接口
另外一种方案是, 让 ES 暴露 CUD 接口, 用于同步索引表数据但是这样就跟 ES 耦合在一块了不太推荐这么做
进一步的思考
1ES 不支持 Group By 这样的操作, 所以在构建索引表的时候, 可以事先计算好 Group By 的一些统计数据, 并存储到索引表中;
2 一些后台应用中, 如果数据库表的数量已经很大, 好几个亿了, 并且查询的 SQL 还非常变态, 用数据库已经无法支持了, 那么可以使用 ES, 查询速度快, 也支持一些统计操作;
3 使用 ES 输出数据时, 也有个坑经常会拿到脏数据的例如当数据发送变化后, 构建索引数据并把索引数据同步到 ES 上是需要时间的, 但是我们后台通常有将数据下架的操作, 下架的操作操作完后, 再次点击查询按钮, 可能还是看到脏数据, 因为数据同步到 ES 上没那么快现在我还没想到很好的办法来解决这个问题欢迎网友提些建议
来源: http://blog.jobbole.com/113726/