a4 - 性能优化 - 搜索单词 - 2022年10月2日00:20:59

a4 - 性能优化 - 搜索单词 - 2022年10月2日00:20:59

黄鹏宇 341 2022-10-01

场景及接口

  1. 根据卡片ID,获取单词 wordinfos/search?word-card-id-list=1444,1445,1446,1447,1448
  2. 根据单词列表,获取单词 wordinfos/search?wordlist=attention,big,
  3. 搜索单词

最终效果

同时获取36张卡片(180个单词),从24秒减少到400ms!!!
3uT0j8zlCp

优化方案

1. 使用FutureTask

2. 对以下两个属性也添加缓存

  1. 单词的收藏
  2. 自定义释义

3. 缓存模型

1. 单词的收藏

redis set
key为 mark_word#[OPENID]

image-1664639861966


  • sadd <key> <value1> <value2>
    sAdd(key,[...])

  • srem <key><value1><value>
    setRemove

  • sismember <key> <value>
    sHasKey

2. 自定义释义

使用redis hash,key为self_def#[OPENID]

image-1664640239248

  • 增、改
    HMSET <key> <field1> <value>
    hmset

  • HDEL <key> <field1> [field2]

  • HGET <key> <field>
    hget

具体实现

在springboot启动后,加载缓存

@Component
public class LoadInitData implements ApplicationRunner {
    @Autowired
    RedisUtil redisUtil;

    @Autowired
    WordGroupMapper wordGroupMapper;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("项目启动时加载数据到缓存中");
        // 加载所有标记的单词
        loadAllMarkWord();
        // 加载所有自定义释义
        loadAllSelfDefineWord();
    }

    // 加载所有的标记的单词
    public void loadAllMarkWord() {
        ArrayList<MarkWordList> markWordListGroupByOpenid = wordGroupMapper.getMarkWordListGroupByOpenid();
        for (MarkWordList markWordList : markWordListGroupByOpenid) {
            String openid = markWordList.getOpenid();
            String wordListStr = markWordList.getWordListStr();
            String[] split = wordListStr.split(",");
            redisUtil.sSet(CacheConstant.CACHE_MARK_WORD + openid, split);
        }
    }
    
     public void loadAllSelfDefineWord() {
        ArrayList<SelfDefineWordList> allSelfDefineWord = dictMapper.findAllSelfDefineWord();
        for (SelfDefineWordList selfDefineWordList : allSelfDefineWord) {
            String openid = selfDefineWordList.getOpenid();
            // word:::self_def &&& word:::self_def
            String wordListStr = selfDefineWordList.getWordListStr();
            String[] wordAndDefList = wordListStr.split("&&&");
            // 用&&&分割单词,用:::分割释义
            // 构造成map
            Map<String, Object> map = new HashMap<>();
            for (String singleWordAndDef : wordAndDefList) {
                // 要避免def为空的情况
                String[] splitStr = singleWordAndDef.split(":::");
                if (splitStr.length == 2) {
                    map.put(splitStr[0], splitStr[1]);
                } else {
                    map.put(splitStr[0], "");
                }
            }
            redisUtil.hmset(CacheConstant.CACHE_SELF_DEFINE_WORD + openid, map);
        }
    }
}

注意需要修改数据库的group_concat_max_len参数

SELECT openid,count(*),GROUP_CONCAT(word) AS 'wordList' FROM `mark_word` GROUP BY openid
SELECT openid,GROUP_CONCAT(word,":::",self_def SEPARATOR '&&&') AS wordlist FROM `self_def_word` GROUP BY openid
  1. 变更时怎么处理
    变更时redis和mysql同时改变
  2. 以后做多节点有影响吗
    有影响
  3. 怎么解决redis和mysql不一致的问题
    不会不一致

其余要优化的地方,在we分析中查看慢接口