performance

122 阅读7分钟

纵向货架的优化

  • 针对纵向货架指标进行优化,主要集中在Android侧,目前新策略秒开Android已提升3.4分。主要优化:新策略下钟点房模块延迟加载,避免抢占首屏全日房加载资源。

  • 剩余可优化点

    • 钟点房首屏延迟加载全日房模块。
    • 详情页接口添加returnFormat,避免Android侧序列化耗时长的问题。
    • 钟点房场景与全日房接口拆分,提升首屏接口速度。
    • 纵向快筛栏layout后置逻辑优化。
  • 中间页接口缓存:需要注意,价格在接口返回之后展示,否则价格跳变体验不好

缓存优化

image.png

  • 缓存: 缓存就是一个临时储存数据的地方。当用户查询数据时,首先会在缓存中查找,如果找到了就直接使用;如果找不到,就再到数据的原始位置去寻找。所以,缓存本质上是一种用空间换时间的技术,通过数据在空间上的重复,来提升数据的访问速度。 并不是所有的缓存都只是为了提升速度,因为在分布式系统中,缓存机制实际上是系统级性能设计的一个重要权衡手段。 比如当某个数据库的负载比较高,接近系统瓶颈时,我们就可以使用缓存技术,把负荷分担到其他数据库中,那么这里使用缓存的目的,主要就是负载均衡,而不是提升访问速度。
  • 几种数据类型
    • 不变性数据:这类数据就是可以优先考虑使用缓存技术的一种数据类型,在实际的业务场景中也非常多。比如,Web服务中的静态网页、静态资源,或者数据库表中列数据与key的映射关系、业务的启动配置,等等,这些都可以认为是不变性数据;
    • 弱一致性数据,它代表数据会经常发生变化,但是业务对数据的一致性要求不高,也就是说,不同用户在同一时间点上看到不完全一致的数据,都是可以接受的。由于这类数据对一致性的要求比较低,所以在设计缓存机制时,你只需要实现最终一致性就可以了。这类数据在实际业务中也比较多,比如业务的历史分析数据、一些搜索查找返回数据等,即使最近的一些数据没有记录进去,关系也不大。这类数据缓存失效机制是基于时间失效
    • 强一致性数据:第三种缓存数据类型是强一致性数据,它是指代码数据会经常发生变化,而且业务对数据库的一致性要求非常高,也就是说当数据发生变更后,其他用户在系统中的任何地方,都应该看到的是更新后的数据。那么,针对这种类型的数据,我一般是不推荐你去使用缓存机制,因为这类数据在使用缓存时会比较复杂,而且很容易会引入新的问题。比如说,用户可以直接提交和修改的各种数据内容,如果没有同步修改缓存中的数据,就会引发数据不一致性的问题,导致比较严重的业务故障。不过在一些特殊的业务场景中,比如,在针对个别的数据访问频率非常高的情况下,我们还是需要通过设计缓存机制,来进一步提升性能。因此针对这类强一致性数据,在设计缓存机制时,你需要特别注意两点:
      • 这种数据的缓存一定要采用修改同步的实现方式。也就是说,所有的数据修改都必须确保可以同步修改缓存与数据库中的数据。
      • 准确识别特定业务流程中,可以使用缓存获取数据的时间有多长。因为有些缓存数据(比如一次REST请求中,多个流程都需要使用的数据)只可以在单次业务流程中使用,不能跨业务流程使用。

缓存

//过滤字段,即key
const filter = `${checkInDate}_${checkOutDate}_${roomCount}_${numberOfAdults}_${childAges}`;
//加载缓存
 dayRoomCacheData = getDayRoomListCache(poiId, filter);
 //方法
 export const getDayRoomListCache = (poiId, filter) => {
    return getDayRoomListCacheWitchKey(
        poiId,
        filter,
        OLD_FIRST_KEY,
        OLD_SECOND_KEY_PREFIX
    );
};
//具体缓存
/**
 * 读缓存获取全日房数据
 * @param poiId
 * @param filter 筛选条件
 * @returns
 */
const getDayRoomListCacheWitchKey = (
    poiId,
    filter,
    firstKey,
    secondKeyPrefix
) => {
    const value = syncGetStorage(firstKey);
    if (value && value.data) {
        try {
            const dayRoomList = JSON.parse(value.data);
            if (isValid(dayRoomList)) {
                const invalidList = []; //过期缓存
                let hasCache = false;
                const currentTimestamp = getCurrentTimeSync();
                for (const item of dayRoomList) {
                    if (item.uniqueKey === poiId) {
                        if (item.filter === filter) {
                            if (currentTimestamp < item.expirationTime) {
                                hasCache = true;
                            } else {
                                //缓存过期
                                toastCacheStatus(
                                    '缓存过期' +
                                        (currentTimestamp -
                                            item.expirationTime) +
                                        'ms'
                                );
                            }
                        } else {
                         //缓存不匹配
                            toastCacheStatus(
                                '缓存筛选条件不匹配\nfilter:' +
                                    filter +
                                    '\ncacheFilter:' +
                                    item.filter
                            );
                        }
                    }
                    if (currentTimestamp >= item.expirationTime) {
                        invalidList.push(item);
                    }
                }
                if (isValid(invalidList)) {
                    clearInvalidRoomList(invalidList, secondKeyPrefix);
                }
                if (hasCache) {
                    const dayRoomCache = syncGetStorage(
                        secondKeyPrefix + poiId
                    );
                    if (dayRoomCache && dayRoomCache.data) {
                        //命中缓存
                        const dayRoomData = JSON.parse(dayRoomCache.data);
                        toastCacheStatus('命中缓存');
                        return dayRoomData;
                    } else {
                        toastCacheStatus('第二部分缓存数据丢失');
                    }
                }
                //debug调试用
                setCachePoiIds(dayRoomList);
            }
        } catch (err) {}
    } else {
        toastCacheStatus('没有缓存');
    }
    //没有缓存
    return null;
};

/**
 * 遍历清除过期的第二部分缓存(新老货架共用)
 */
const clearInvalidRoomList = (invalidList, secondKeyPrefix) => {
    return new Promise(resolve => {
        try {
            for (const item of invalidList) {
                KNB.clearStorage({
                    key: secondKeyPrefix + item.uniqueKey, // 数据键,String类型
                    success: () => {}
                });
            }
            resolve(null);
        } catch (err) {
            resolve(null);
        }
    });
};

在前端设置缓存时,需要考虑多个方面,以确保缓存的有效性、安全性和性能优化。以下是设置缓存时需要明确的几个关键内容:

1. 缓存策略(Caching Strategy)

缓存策略决定了如何在客户端和服务器之间管理缓存。常见的缓存策略包括:

  • Cache-First:优先从缓存获取数据,如果缓存中没有数据,再从服务器获取。
  • Network-First:优先从服务器获取数据,如果服务器不可用,再从缓存获取。
  • Cache-Only:只从缓存获取数据,不进行网络请求。
  • Network-Only:只从服务器获取数据,不使用缓存。
  • Stale-While-Revalidate:先从缓存获取数据,同时在后台请求服务器更新缓存。

2. 缓存位置(Cache Location)

缓存可以存储在不同的位置,每个位置都有其优缺点:

  • 浏览器缓存(Browser Cache) :使用浏览器的内置缓存机制,如 localStoragesessionStorage 和 IndexedDB
  • Service Worker 缓存:使用 Service Worker 拦截网络请求,并将其缓存。
  • 内存缓存(In-Memory Cache) :将数据存储在内存中,适用于短期缓存。

3. 缓存时长(Cache Duration)

缓存时长决定了数据在缓存中存储的时间。可以使用 HTTP 头部字段来控制缓存时长:

  • Cache-Control:设置缓存策略和时长,例如 max-ageno-cacheno-store 等。
  • Expires:设置资源的过期时间。
  • ETag:使用实体标签进行缓存验证,确保缓存数据的有效性。

4. 缓存的内容(What to Cache)

需要明确哪些内容需要缓存,以及缓存的粒度:

  • 静态资源:如 HTML、CSS、JavaScript 文件、图片等,通常适合长期缓存。
  • API 响应:如 JSON 数据,可以根据具体需求设置缓存时长。
  • 动态内容:如用户数据,可能需要更频繁地更新缓存。

5. 缓存的更新机制(Cache Invalidation)

缓存失效和更新机制是缓存管理的重要部分,确保缓存数据的准确性:

  • 时间失效:设置缓存的过期时间,到期后重新请求数据。
  • 版本控制:通过版本号或哈希值管理缓存,资源更新时自动失效旧缓存。
  • 手动失效:在特定情况下手动清除缓存,如用户登出时清除用户数据缓存。

6. 安全性(Security)

缓存数据的安全性需要特别注意,防止敏感数据泄漏:

  • 敏感数据不缓存:避免缓存用户敏感信息,如密码和个人身份信息。
  • HTTPS:确保缓存的数据在传输过程中使用 HTTPS 加密。

7. 性能优化(Performance Optimization)

缓存不仅可以提高性能,还可以减少服务器负载:

  • 减少重复请求:通过缓存减少对同一资源的重复请求。
  • 预加载和预取:在用户需要之前预加载资源,提高加载速度。
  • 压缩数据:使用 gzip 或 brotli 压缩缓存数据,减少传输大小