暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

OceanBase的物化视图

普通的视图(view)都是在查询的时候再执行视图定义的查询操作,而物化视图是把视图定义的查询结果先存储(物化)下来,等查询的时候只需要读取存储的数据即可。举个简单的例子。

例如 
create table t1 (c1 int primary key, c2 int)
创建视图
create view v1 as select * from t1 order by c2 desc
创建物化视图
create materialized view m1 as select * from t1 order by c2 desc

执行
select * from v1 limit 10
等价于
select * from (select * from t1 order by c1 desc) v1 limit 10

而执行
select * from m1 limit 10
等价于执行查询了普通表m1,不需要进行排序这个耗时的操作。

物化视图除了直接查询m1以外
当用户执行
select * from t1 order by c2 desc
数据库内部会把这个查询改写成
select * from m1

当用户执行更加复杂的操作
select * from t1 where c2 = 5
数据库也会改写成
select * from m1 where c2 = 5
这个改写能利用m1对于c2的排序结果,通过二分查找定位c2 = 5的那些数据,比前一条语句的全表扫描好得多。看到这里眼尖的你会发现这个和
create index i1 on t1(c2)
好像没什么区别。事实上物化视图能做得更多,例如
create materialized view m2 as select c1, count(c2) from t1 group by c1
那我们如果做相应的group by操作就能被消除了。物化视图通常是用在数据仓库(dataware house)中,用于加速复杂的分析型sql使用的。在oracle和teradata中都有相应的实现。

OceanBase实现的物化视图

熟悉OceanBase的人,都知道OceanBase从一开始就有一个为收藏夹业务定制的prejoin功能。这里简单介绍一些收藏夹业务和prejoin功能。收藏夹业务主要有两个表:collect_info和collect_item。collect_info表示用户的收藏关系,collect_item表示商品列表。简化的表定义
create table collect_info (user_id int, item_id int, primary key(user_id, item_id))
create table collect_item(item_id int, item_detail varchar(5000), primary key(item_id))

用户打开收藏夹页面的查询语句为
select user_id, collect_item.item_id, item_detail from collect_info join collect_item on collect_info.item_id = collect_item.item_id where used_id = ?
这个join数据库内部用的是nested-loop join。对于collect_item每次都是随机的行获取操作。例如一个用户收藏了2000个商品,为了获得的这2000个商品的详细信息。那么就需要执行2000次collect_item的随机读取操作。这个在很高的qps情况下,对collect_item的磁盘压力是很大的。OceanBase通过一个巧妙的方法来解决这个问题。简单的说就是先把join的结果存储起来,每次查询就能利用这个join的数据。

图片

如图所示,预先把collect_info和collect_item join好的数据储存在磁盘上。图上蓝色部分。查询的时候只需要读取磁盘上已经join好的数据,然后合并上collect_info join collect_item在内存上更新的部分的数据即可。由于更新的数据通常较少,并且内存的随机读取速度非常快。所以能大大加快join的速度。这样能支撑收藏夹高峰时期的qps。内存中的数据会定期的合并到磁盘中成为新的join快照。

OceanBase的存储结构就是动静态数据分离(如下图)的,非常适合实现上述prejoin功能
图片

prejoin这个功能非常好,很多业务场景都能会遇到这种两个大表join的情况。但是使用起来对用户不是十分友好。例如之前的collect_info表的建表语句是
create table collect_info (user_id int, item_id int, item_detail varchar(5000), primary key(user_id, item_id)) join_info = 'user_id$user_id%collect_item:item_detail$item_detail'
够复杂,具体含义这里就不解释了。

1.0借用物化视图的概念重新实现了prejoin功能,用户可以创建一个这样的物化视图
create materialized view info_item_mv from select user_id, collect_item.item_id, item_detail from collect_info join collect_item on collect_info.item_id = collect_item.item_id
当用户执行
select select user_id, collect_item.item_id, item_detail from collect_info join collect_item on collect_info.item_id = collect_item.item_id where used_id = ?查询的时候,查询就会被改写成
select user_id, collect_item.item_id, item_detai from info_item_mv where user_id = ?
而info_item_mv就是prejoin的一个实现,有很高效的join速度。在不改业务逻辑的情况下,只要创建一个物化视图,就能大大加快join的速度。而且如果不想使用物化视图了,直接drop这个视图就可以了。是不是超级酷,心动了么,来用OceanBase吧。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论