缓存小技巧--NullObject模式&&表识cache

本文针对高并发场景下数据库查询压力大的问题,通过三次缓存优化措施显著降低了数据库负载。首次尝试利用缓存减少DB访问,但因缓存命中率低效果不明显;第二次引入NullObject机制提高缓存效率;最终通过结合用户属性进一步优化,有效避免了不必要的数据库查询。

在我们的应用中,有一张表的查询量非常之大,高峰期时6000次/second查询,而且更新很少,于是我们的改造来了:

 

第一次优化:

方法:每次从cache中读取,如果cache中没有命中,则从DB读取,如果有值将此值放入cache中。

效果:上线后效果并不是特别明显,高峰期仍有3000次/second的查询,原因在哪里呢?此表只有700w的数据量,但是对于3亿用户,每个用户都要查询此表,就是说,只有700w/3亿=2.33%可以命中cache,间接导致每次从cache中取数据为空后,仍有97.67%的数据仍然会查询DB。

 

第二次优化:

方法:每次从DB中查询后,如果是空,则在cache中放入一个NullObject,用于区分null的数据。如果结果是NullObject或者此表的对象模型,则直接从cache中返回,不再从db中查询。

效果:这次优化后,相信可以减少对宝贵的db资源大量的浪费。

 

第三次优化:

方法:有一个背景,我们的3亿用户基于读多写的业务已经放入分布式cache,我们可以将业务看做是是用户的一个属性(一个假设的例子:网银的硬key),可以把此业务和客户模型绑定在一起,即在用户的客户模型表中增加一个字段,每次查询客户模型时,即将此字段查询出来;如果此属性有值,外围系统可以在第二步的基础上,继续查询具体表的缓存去完成自己的业务。

效果:客户模型的表识cache+具体业务的cache,即可以避免大部分业务二次查询的问题;又充分利用了cache的功效

<think>我们已经在之前的对话中讨论了分页数据的模拟实现,现在用户的问题是关于微信小程序中实现tabs切换并缓存数据的方法。 根据用户的问题,我们需要实现两个主要功能: 1. Tabs切换:通常使用小程序内置组件或自定义组件实现标签页切换。 2. 缓存数据:在切换tab时,每个tab对应的数据应该被缓存,避免重复加载。 我们可以参考引用中的一些技术点: 引用[1]:展示了数据绑定和TabBar的基本结构,以及页面的生命周期函数。 引用[2]:展示了使用缓存(wx.setStorageSync)的方法。 引用[3]:展示了多层列表渲染和条件渲染。 实现方案: 1. 使用小程序的`wx:if`或`hidden`来控制不同tab内容的显示与隐藏。 2. 使用小程序的存储API(如wx.setStorageSync和wx.getStorageSync)来缓存数据,或者利用页面栈中的data来保存数据(非持久化)。 3. 也可以使用小程序的页面参数传递,但这里更关注缓存。 但是,用户希望的是在tabs切换时缓存数据,避免每次切换都重新加载,提升用户体验。 我们可以考虑两种缓存策略: 策略一:在页面data中保存每个tab的数据(非持久化,页面关闭后消失) 策略二:使用小程序本地存储(持久化,但需注意大小限制) 由于是同一个页面内的tab切换,通常使用策略一即可,因为用户离开页面再回来时,可能需要重新加载(或者根据需求使用策略二)。 具体步骤: 1. 在页面data中定义: - tabs:标签数组 - activeTab:当前激活的tab索引 - 为每个tab准备一个数据容器(比如tab1Data, tab2Data, ...)或者用一个对象来存储,键为tab的标识 2. 切换tab时: - 改变activeTab - 如果当前tab的数据还没有加载,则加载数据并保存到对应的数据容器中;如果已经加载过,则直接显示。 3. 使用wx:if或hidden控制显示对应的tab内容。 4. 为了减少重复加载,可以在第一次加载某个tab的数据后,将其缓存起来。 代码示例: 假设有两个tab:tab1和tab2。 ```javascript Page({ data: { tabs: ['tab1', 'tab2'], activeIndex: 0, // 当前激活的tab索引 tab1Data: null, // tab1的数据 tab2Data: null // tab2的数据 }, // 切换tab事件 switchTab(e) { const index = e.currentTarget.dataset.index; if (index === this.data.activeIndex) return; this.setData({ activeIndex: index }); // 检查该tab的数据是否已加载,没有则加载 this.loadTabData(index); }, // 加载tab数据 loadTabData(index) { const tabKey = `tab${index+1}Data`; if (this.data[tabKey]) { // 已经有缓存,不再加载 return; } // 模拟加载数据 this.loadDataForTab(index); }, // 模拟加载数据函数 loadDataForTab(index) { // 这里模拟网络请求 setTimeout(() => { // 假设根据index获取不同的数据 const data = index === 0 ? { list: ['A', 'B', 'C'] } : { list: [1, 2, 3] }; const tabKey = `tab${index+1}Data`; this.setData({ [tabKey]: data }); }, 500); }, onLoad() { // 初始化加载第一个tab的数据 this.loadDataForTab(0); } }) ``` 在WXML中: ```html <view class="tabs"> <view wx:for="{{tabs}}" wx:key="index" data-index="{{index}}" bindtap="switchTab"> <text class="{{index === activeIndex ? 'active' : ''}}">{{item}}</text> </view> </view> <!-- tab1的内容 --> <view wx:if="{{activeIndex === 0}}"> <view wx:if="{{tab1Data}}"> <view wx:for="{{tab1Data.list}}" wx:key="index">{{item}}</view> </view> <view wx:else>加载中...</view> </view> <!-- tab2的内容 --> <view wx:if="{{activeIndex === 1}}"> <view wx:if="{{tab2Data}}"> <view wx:for="{{tab2Data.list}}" wx:key="index">{{item}}</view> </view> <view wx:else>加载中...</view> </view> ``` 这个例子中,每个tab的数据在第一次加载后就会保存在data中,切换tab时不会重复加载。 但是,如果用户希望即使离开页面再回来也能保持数据,那么可以使用小程序的缓存: 在加载数据后,将数据存入缓存: ```javascript loadDataForTab(index) { const tabKey = `tab${index+1}Data`; // 先尝试从缓存读取 const cacheData = wx.getStorageSync(tabKey); if (cacheData) { this.setData({ [tabKey]: cacheData }); return; } // 模拟网络请求 setTimeout(() => { const data = index === 0 ? { list: ['A', 'B', 'C'] } : { list: [1, 2, 3] }; this.setData({ [tabKey]: data }); // 存入缓存 wx.setStorageSync(tabKey, data); }, 500); } ``` 注意:缓存是持久化的,所以如果数据可能变化,需要合理的更新策略(比如设置过期时间,或者在合适的时机清除缓存)。 另外,如果tab数量较多,可以考虑用一个对象来统一管理,例如: ```javascript data: { tabs: ['tab1', 'tab2'], activeIndex: 0, tabData: { 0: null, 1: null } } ``` 这样在设置数据时: ```javascript this.setData({ [`tabData.${index}`]: data }); ``` 这样更灵活。 综上所述,根据需求选择缓存策略,核心是在data中保存已加载的tab数据,避免重复请求。 相关问题: 1. 如何在小程序中使用本地缓存存储复杂数据结构? 2. 小程序tab切换时如何避免频繁请求? 3. 如何实现小程序tab页面的懒加载? 4. 小程序中如何清除特定缓存数据? 5. 小程序中如何更新已缓存的tab数据? </think> 以下是微信小程序中实现 Tabs 切换并缓存数据的完整解决方案,包含核心实现代码和优化建议: ### 一、核心实现方案 使用小程序内置的 `wx:if` + 数据缓存机制实现高效切换 ```javascript // pages/index/index.js Page({ data: { tabs: [ { id: 1, name: '推荐' }, { id: 2, name: '热门' }, { id: 3, name: '最新' } ], activeTab: 1, // 为每个tab创建独立的数据缓存 cachedData: { 1: [], // 推荐数据 2: [], // 热门数据 3: [] // 最新数据 }, currentData: [] // 当前显示的数据 }, onLoad() { // 初始化加载第一个tab的数据 this.loadTabData(1); }, // 切换Tab事件 switchTab(e) { const tabId = e.currentTarget.dataset.id; if (this.data.activeTab === tabId) return; this.setData({ activeTab: tabId }); // 检查是否有缓存数据 if (this.data.cachedData[tabId].length > 0) { this.setData({ currentData: this.data.cachedData[tabId] }); } else { this.loadTabData(tabId); } }, // 加载Tab数据(模拟API请求) loadTabData(tabId) { // 显示加载状态 wx.showLoading({ title: '加载中' }); // 模拟网络请求延迟 setTimeout(() => { // 模拟不同tab的数据 const mockData = { 1: [{id:1, title:"推荐内容1"}, {id:2, title:"推荐内容2"}], 2: [{id:1, title:"热门内容1"}, {id:2, title:"热门内容2"}], 3: [{id:1, title:"最新内容1"}, {id:2, title:"最新内容2"}] }; // 更新缓存和当前数据 const newCachedData = { ...this.data.cachedData }; newCachedData[tabId] = mockData[tabId]; this.setData({ currentData: mockData[tabId], cachedData: newCachedData }); wx.hideLoading(); }, 500); }, // 页面卸载时持久化缓存(可选) onUnload() { wx.setStorageSync('tabCache', this.data.cachedData); } }) ``` ```html <!-- pages/index/index.wxml --> <view class="tabs"> <block wx:for="{{tabs}}" wx:key="id"> <view class="tab-item {{activeTab === item.id ? 'active' : ''}}" bindtap="switchTab" data-id="{{item.id}}"> {{item.name}} </view> </block> </view> <view class="content"> <!-- 使用wx:if确保切换时保留DOM状态 --> <block wx:for="{{currentData}}" wx:key="id"> <view class="item">{{item.title}}</view> </block> </view> ``` ```css /* pages/index/index.wxss */ .tabs { display: flex; height: 80rpx; border-bottom: 1rpx solid #eee; } .tab-item { flex: 1; text-align: center; line-height: 80rpx; } .tab-item.active { color: #07c160; border-bottom: 4rpx solid #07c160; } .item { padding: 20rpx; border-bottom: 1rpx solid #f0f0f0; } ``` ### 二、缓存机制说明 1. **内存缓存**: - 使用 `cachedData` 对象存储各Tab数据 - 切换时优先使用缓存数据 - 避免重复请求相同数据 2. **持久化存储**(可选): ```javascript // 在onLoad中读取缓存 onLoad() { const cache = wx.getStorageSync('tabCache'); if (cache) { this.setData({ cachedData: cache }); } this.loadTabData(1); } // 在onUnload中保存缓存 onUnload() { wx.setStorageSync('tabCache', this.data.cachedData); } ``` ### 三、优化技巧 1. **数据更新策略**: ```javascript // 添加刷新时间戳判断数据新鲜度 loadTabData(tabId) { const lastUpdate = this.data.cachedData[tabId]?._timestamp || 0; const now = Date.now(); // 5分钟内使用缓存 if (now - lastUpdate < 300000 && this.data.cachedData[tabId].length > 0) { this.setData({ currentData: this.data.cachedData[tabId] }); return; } // ...请求新数据 } ``` 2. **大容量缓存优化**: ```javascript // 使用LRU缓存策略 const MAX_CACHE_SIZE = 10; if (Object.keys(this.data.cachedData).length > MAX_CACHE_SIZE) { // 删除最久未使用缓存 } ``` 3. **请求防抖处理**: ```javascript let timer = null; switchTab(e) { if (timer) clearTimeout(timer); timer = setTimeout(() => { // 实际切换逻辑 }, 300); } ``` ### 四、注意事项 1. 使用 `wx:if` 而非 `hidden`,确保切换时保留DOM状态 2. 复杂数据使用 `JSON.parse(JSON.stringify())` 深拷贝避免引用问题 3. 缓存数据量较大时使用压缩: ```javascript const compressed = wx.compressData(JSON.stringify(data)); wx.setStorageSync('cache', compressed); ``` ### 五、适用场景 1. 内容类应用的分类展示 2. 电商平台商品分类切换 3. 数据仪表盘的多维度查看 4. 需要保持滚动位置的列表
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值