如何以优化方式检查BiDiMapper是否包含指定ID或值?如何节省Gas费?
优化方案:将线性遍历改为常数时间查询
当前实现通过遍历所有元素检查存在性,时间复杂度为O(n),Gas开销会随元素数量线性增长,在元素较多时极易触发Gas超限。以下是针对Substrate pallet场景的优化方案:
方案1:使用StorageMap替代集合存储
将原有的集合存储改为StorageMap,直接以Item作为键存储对应数据(比如你的Token),利用Substrate存储的键值对特性实现O(1)时间复杂度的存在性检查。
修改存储定义
use frame_support::storage::StorageMap; use sp_runtime::traits::Blake2_128Concat; #[storage] pub(super) type ItemTokenMap<T: Config> = StorageMap<_, Blake2_128Concat, Item<T::Api>, Token>;
优化后的has_item实现
fn has_item(&self, item: &Item<Self::Api>) -> bool { ItemTokenMap::<Self>::contains_key(item) }
方案2:额外维护哈希集合辅助查询
如果需要保留原有集合存储结构(比如需要遍历所有元素的场景),可以额外维护一个HashSet类型的存储,在添加/删除元素时同步更新该集合,查询时直接调用contains方法。
修改存储定义
use frame_support::storage::StorageValue; use std::collections::HashSet; #[storage] pub(super) type ItemsTokens<T: Config> = StorageValue<_, Vec<(Item<T::Api>, Token)>>; #[storage] pub(super) type ItemsSet<T: Config> = StorageValue<_, HashSet<Item<T::Api>>>;
同步更新逻辑(示例:添加元素时)
fn add_item(&self, item: Item<Self::Api>, token: Token) { // 更新原有集合 let mut items = ItemsTokens::<Self>::get(self); items.push((item.clone(), token)); ItemsTokens::<Self>::put(items); // 同步更新哈希集合 let mut set = ItemsSet::<Self>::get(self); set.insert(item); ItemsSet::<Self>::put(set); }
优化后的has_item实现
fn has_item(&self, item: &Item<Self::Api>) -> bool { ItemsSet::<Self>::get(self).contains(item) }
核心优化逻辑
区块链中遍历操作的Gas开销与元素数量正相关,而键值对查询(StorageMap::contains_key)或哈希集合查询(HashSet::contains)的Gas开销为常数级,不会随元素数量增加而上升,能大幅降低Gas消耗,同时避免元素过多时触发区块Gas上限。
注意:无论采用哪种方案,都需要保证添加、删除元素时同步更新对应存储结构,避免数据不一致。
内容的提问来源于stack exchange,提问作者Fargerik
相关产品推荐
相关产品推荐

