一 Salesforce 之组织,四载间,案牍自五万增至五百万。昔者 Apex 之码,处理案牍,二百毫秒可毕。今则同码,三十秒犹未竟,时复超时。众皆咎“平台渐缓”。然实因 SOQL 之选择性不足。查询不再触及索引,平台乃遍索全表。
触大物之Apex码,择取之精当,乃其最要之效能所忧。早察则易解,迟觉则费甚。Sapota Salesforce之众,视LDV(巨量数据)之审为常事,盖其败状屡现也。
何谓择取之精当
若Salesforce能藉索引速寻匹配之记录,则SOQL查询为精选。查询为非精选者,乃Salesforce须遍查全表之时也。
Salesforce之索引:
- 凡标准对象之主键(Id,标准对象之名称)。
- 凡外键字段(参照,主从)。
- 凡标为外部ID之字段。
- 任何标明为唯一之域.
- 特定域之定制索引(须通过支持申请;非自助服务).
欲使索引得用,WHERE子句须过滤索引之域,且过滤须足够精微。其精微之阈限:
- 标准索引之域:查询返回之记录少于全部之三十百分,或少于百万(取其低者)。
- 定制索引字段:查询结果不足总记录之十,或不足三十三万三千三百三十三。
越此阈限,Salesforce 必回溯全表扫描,无论该字段是否技术性索引。
无选择性查询之貌
高量失效之模式:
// 状若"Open"者,或合四十之百。// 非择于五万行案表。SELECT Id, Subject FROM Case WHERE Status = 'Open'
// LIKE with leading wildcard cannot use the index.SELECT Id FROM Account WHERE Name LIKE '%Acme%'// 否者于索引之域,则索引废。SELECT Id FROM Lead WHERE IsConverted != TRUE// OR across non-indexed fields produces a scan.SELECT Id FROM OpportunityWHERE StageName = 'Closed Won' OR Probability > 90// 无筛选之极(诚然,此存于实码)。SELECT Id, Amount FROM Opportunity
此等诸事,于千记录之开发者组织,皆可运行无碍。然数据日增,遂成事端。
选用重写之式
同此查询,重构之:
// 添加索引字段,以狭其域。// CreatedDate已索引;与Status相配。SELECT Id, Subject FROM Case WHERE Status = 'Open' AND CreatedDate = LAST_N_DAYS:30
// Use full-text search (SOSL) for substring matching,// or store a normalized prefix in an indexed field.SELECT Id FROM Account WHERE Name LIKE 'Acme%'// 改写以避免在索引字段上使用 NOT。SELECT Id FROM Lead WHERE IsConverted = FALSE// Split the OR into separate queries against indexed fields.List won = [SELECT Id FROM OpportunityWHERE StageName = 'Closed Won'];List highProb = [SELECT Id FROM OpportunityWHERE Probability > 90AND StageName != 'Closed Lost'];// 大型对象务必包含日期或所有者筛选。SELECT Id, Amount FROM Opportunity WHERE CloseDate = THIS_QUARTER
模式:以索引筛选为首要标准,缩小结果集。在子集中使用附加筛选以进一步细化。
如何知晓查询是否具有选择性
二诊断之器:
开发者控制台之查询计划工具。键入查询,点“查询计划”。得成本估算,并注查询是否精选。成本低于1.0者,查询多精选;高于1.0者,平台将逾阈扫描。
以sObjectDescribe及索引论之。若论自设之域,当检视其customIndexed属性,或览域之设屏。索引之域,显"已索引"之识。
于生产之际,其症在时序。凡SOQL询查,若对一物表耗时逾数秒,率多非择性。其解,首在辨识询查当用之索引域。
对于极高量级之物(常逾五百万行)且频有报查之需者,Salesforce复设二优化之法:
纤表乃支持所请之特征。Salesforce创一仅含汝所指定之列之对象之只读副本。凡触及仅此诸列之报告与查询,皆运行于纤表,绕过全记录表。于可预知之查询模式,性能大增。
其难处在于:瘦表仅适用于特定场景(如仅读报表、特定SOQL模式)。须撰定制Apex查询以击瘦表。需与Salesforce支持方交涉,方得设置之。
外标识与唯一索引。将自定义字段标为外标识则可索引之。标其唯一亦能索引之。二者皆于设置中自行服务。宜于Apex中过滤或联结之字段,主动使用此等。数据既增,于自定义字段加外标识,需一次性索引构建(Salesforce管理此)。
何时当用Database.query抑或静态SOQL
靜態SOQL(方括號內)於編譯時檢查,具優良之工具支援。動態SOQL(Database.query(...))乃於運行時構建之查詢所必需。
就LDV之憂,二者所產生之查詢計劃無異。選擇性者,乃屬於查詢字符串之屬性,非關乎其構建之法。已知查詢之形狀,當優選靜態SOQL;唯有形狀確實變遷,方可使用動態SOQL。
寻常LDV之弊,动态SOQL是也:以用户输入建查询,致过滤非选择性。用户所键之索,击无索引之域,查询遍扫全表,页至时过。常验用户驱动之询,用索引之域,施选择性过滤。
批量Apex,用于诚然宏大之务
若一 SOQL 查询实需处理百万之记录,纵有万般选择,亦难容于一交易之中。其宜之器,乃 Batch Apex 也.
Batch Apex 之制,默认将查询分批,每批二百记录(可调至二千),各批自成一交易,各具管辖之限。其框架自能理顺迭次.
其式如下:
公有类CaseAgingBatch实现Database.Batchable{公有Database.QueryLocator开始(Database.BatchableContext bc){//选择性以CreatedDate为界以限定结果。返回Database.getQueryLocator('SELECT Id, Status FROM Case ' + 'WHERE CreatedDate}<自九十日以来:且状若启,)
public void execute(Database.BatchableContext bc,List scope) {// Process up to 200 cases per batch.}公无终事于数据库之批处理境。
虽批量Apex亦需择取之始询。框架不能化非择取之询为速询,唯能分块其果。
LDV之善,其状何如?
Salesforce之组织,于巨量数据,康健若此:
- 凡Apex之SOQL询于高量之物(Case、Task、Opportunity、定制之物逾百万行),必用索引之场于WHERE之句。
- 以日期为界之滤器(创建日期=最近30日)乃批量处理之常法。
- 外部ID字段,于Apex联接或查询之各定制字段皆须配置。
- 瘦表所求,用于高量报告之用。
- 批量Apex用于触碰逾五万条记录之操作。
- 查询计划之器,于代码检视时运行于新SOQL。
萨波塔之Salesforce团队持平台开发者一级之资历,行LDV审计为既定之业务模式。审计得列非选择性查询之清单,期于组织数据增长之预期影响,及各重构路径。蓄数据之前投资选择性,远廉于蓄数据之后。
审 Apex SOQL 之选择性或行 LDV 感知重构乎?Sapota 之 Salesforce 团队于生产项目中掌性能审计、索引策略及 Batch Apex 设计之事。相接>
睹吾全貌平台之务吾所论之栈也。












