站点图标 江湖人士

如何优化OutSystems分页性能问题?

如何优化OutSystems分页性能问题?我们在使用OutSystems这种低代码平台开发项目时,少不了web的后台管理功能,而通常的后台管理功能,数据列表分页功能是一大核心功能,因为我们需要在后台完成对各类业务的管理,如订单,交易,商品等,这些数据都是使用OutSystems的Table来实现的,并且需要分页,排序等功能;但江湖人士在实际项目中发现,OutSystems的分页功能是有性能问题的,尤其是随着系统上线使用一段时间后数据量大了后就会明显感到后台慢了。

如何优化OutSystems分页性能问题?

使用低代码平台来开发项目有把双刃剑,对于团队成员来说,如果驾驭不好,会有很多问题的,如开发部署慢,冲突问题,版本管理等问题;如果能很好的配合,架构良好,则会见到使用低代码平台带来的益处,如快速见到上线效果,基本上所见即所得、web和mobile一把梭哈、开发人员无须和数据库打交道等等。

另外使用低代码平台,该有的架构分层还是需要有的,例如在OutSystems官方就有关于架构实践的文章。

Architecture – OutSystems Best Practices

好了,回归本文OutSystems分页查询慢这个性能问题的话题。

项目中使用的数据库是亚马逊云提供的SQL Server数据库,因此可能优化方案只适用于此;如果你的项目中使用的是其它的数据库,如Oracle,MySql以及其它等,本文不一定适用,但分析和解决思路是一致的。

如何优化OutSystems分页性能问题

要明白OutSystems分页有什么性能问题,需要先明白OutSystems是如何实现分页功能的,例如下图所示的一个常见分页功能,使用OutSystems很快就可以完成该功能,在官方网站提供了快速入门教程,感兴趣的可以点击下面的链接查看。

Pagination – OutSystems 11 Documentation

如何优化OutSystems分页性能问题?

接下来就是重点了,看似简单的一个分页功能,对于OutSystems底层生成的是什么样的SQL语句来检索数据的呢?为了知道点击上图的页码,如当你点击17时,会执行什么样的SQL语句,这时我们需要使用SQL Server Profiler这个工具了。

通过SQL Server Profiler工具连接到亚马逊云上的SQL Server数据库。

OutSystems分页查询生成什么样的SQL语句?

为了搞清楚OutSystems分页查询生成的是什么SQL语句,使用SQL Server Profiler,然后添加一些过滤筛选条件即可,不然会抓取到所有的正在执行的SQL语句,不利于分析,具体怎样做的呢?

只需要在新建跟踪属性时(可以滚动窗口到中间部分,将Audit Login 和 Audit Logout Out的事件给取消掉,不然会产生很多跟踪事件),设置好筛选条件即可,点击列筛选器,编辑筛选器,江湖人士这里只设置了DatabaseIDTextData两项(有多个数据库时,填上DatabaseID或者DataBaseName时抓取SQL更精准,不知道DatabaseID的话,只需要执行SQL查询:select DB_ID(‘Jhrs_DB’) 即可获得),如下图所示:

如何优化OutSystems分页性能问题? 10
如何优化OutSystems分页性能问题? 11
如何优化OutSystems分页性能问题? 12

这样设置好了后,进入后台刷新列表页面,或者点击页码翻下页就可以看到执行的SQL语句啦,在本例中,江湖人士是点击第11页,可以看到生成的是select top 110类似这样的语句,如下图所示:

如何优化OutSystems分页性能问题? 13

这样基本上可以确认了OutSystems的分页功能,是查询出所有的数据,然后再到程序里面来取第11页的内容,而不是我们期望的通过SQL直接取第11页的内容,这也说明的为什么随着数据量的增大,有时候SQL数据库服务器慢并且连带着网站管理后台页面也跟着慢的原因。

小编将OutSystems分页查询执行的SQL简化后如下代码所示:

EXEC sp_executesql N'
SELECT TOP (110) 
    [CMP].[EMAIL], 
    [INV].[TYPE], 
    [MEM].[NAME], 
    [USR].[ID], 
    [USR].[EMAIL], 
    [USR].[CREATED], 
    [MODE].[NAME], 
    [INFO].[ID], 
    [INFO].[NAME], 
    [INFO].[ID_NO], 
    [INFO].[PHONE], 
    [INFO].[ADDR], 
    [INFO].[COUNTRY], 
    [INFO].[DOB], 
    [STATUS].[NAME], 
    (CASE WHEN LEN(LTRIM(RTRIM(@selInvs))) = 0 
          THEN 0 
          ELSE (CASE WHEN CHARINDEX(N'','' + CONVERT(VARCHAR(11), [USR].[ID]) + N'','', @selInvs) <> -1 
                     THEN 1 
                     ELSE 0 END) 
     END) AS [SEL] 
FROM 
    [DB].[dbo].[USR] [USR] 
    INNER JOIN [DB].[dbo].[INFO] [INFO] ON [USR].[ID] = [INFO].[ID]
    INNER JOIN [DB].[dbo].[STATUS] [STATUS] ON [INFO].[STATUS_ID] = [STATUS].[ID]
    LEFT JOIN [DB].[dbo].[INV] [INV] ON [INFO].[INV_TYPE] = [INV].[ID]
    LEFT JOIN [DB].[dbo].[MEM] [MEM] ON [INFO].[MEM_ID] = [MEM].[ID]
    LEFT JOIN [DB].[dbo].[CMP] [CMP] ON [INFO].[CMP_ID] = [CMP].[ID]
    INNER JOIN [DB].[dbo].[MODE] [MODE] ON [INFO].[MODE_ID] = [MODE].[ID]
WHERE 
    [INFO].[TYPE] = 2 
    AND (LEN(LTRIM(RTRIM(@invType))) = 0 OR CHARINDEX(N'','' + CONVERT(VARCHAR(11), [INV].[ID]) + N'','', N'','' + @invType + N'','') <> -1)
    AND (LEN(LTRIM(RTRIM(@memId))) = 0 OR CHARINDEX(N'','' + CONVERT(VARCHAR(11), [MEM].[ID]) + N'','', N'','' + @memId + N'','') <> -1)
    AND (LEN(LTRIM(RTRIM(@modeId))) = 0 OR CHARINDEX(N'','' + CONVERT(VARCHAR(11), [MODE].[ID]) + N'','', N'','' + @modeId + N'','') <> -1)
    AND (LEN(LTRIM(RTRIM(@statusId))) = 0 OR CHARINDEX(N'','' + CONVERT(VARCHAR(11), [STATUS].[ID]) + N'','', N'','' + @statusId + N'','') <> -1)
ORDER BY 
    [INFO].[CREATED] DESC', 
N'@selInvs nvarchar(10), @invType nvarchar(10), @memId nvarchar(10), @modeId nvarchar(10), @statusId nvarchar(10)', 
@selInvs = N'', 
@invType = N'', 
@memId = N'', 
@modeId = N'', 
@statusId = N''

可以看到OutSystems生成的SQL语句是根据查询的Aggregate数据源直接关联,再根据页码进行 top 查询,这样的执行效率当然慢了。

如何优化OutSystems分页查询慢的问题呢?

知道了问题的根源,只需要对应的解决即可,直接将页码,每页的条数,查询条件,排序规则等做为参数传入即可,对于高版本的SQL Server,分页查询的核心逻辑如下:

select * from jhrs_table
where 1=1 -- other filter
order by 1 -- order by filter
offset @pageNumber * @PageSize ROWS
fetch next @PageSize ROWS ONLY

这就是极简的SQL Server数据库分页查询,实际项目中,也是直接通过SQL来优化处理的,见下图所示:

这是底层封装的分页的Action,你需要定义一个封装返回的数据的Structure,用于承载数据。

关于OutSystems分页的一些思考

实际在优化分页的问题时,有感于低代码平台方便的同时,带来的一些性能问题如果不知道如何解决,确实是很麻烦的。在优化这些功能时,本想将分页Action做成公用的,类似于我们使用C# 编写的分页方法一样,返回一个泛型的List对象,或者类似于DataSet,DataTable的对象,结果OutSystems是不支持的,如果后台分页列表页面较多每个都需要优化的话,确实是非常大的工作量。

总之不用官方分页做法,自行处理分页有如下的缺点:

  1. 因为在写分页的Action时,你需要针对每个列表页面建立不同的Structure。
  2. SQL语句会很多,针对分布的SQL语句无法做到共用
  3. 重复代码较多,更改表字段,需要手动处理

虽然有以上的缺点,但是这样优化后,后台的商品列表在没有优化前使用人多时,翻页时几乎打不开,现在秒开,效果是杠杠的。鉴于此,如果读者有更好的方法,欢迎下方留言评论。

退出移动版