数据完整性测试之【三】Redis缓存和数据库表里的记录

news2024/9/24 13:18:56

本文为博主原创,未经授权,严禁转载及使用。
本文链接:https://blog.csdn.net/zyooooxie/article/details/119377944

前面分享过 接口返回值 和 表记录 的校验 、 导出的CSV、Excel文件 和 表记录 的校验,最近 我们项目常常用到Redis,也得对缓存做校验,分享下;

【实际这篇博客推迟发布N个月】

个人博客:https://blog.csdn.net/zyooooxie

【以下所有内容仅为个人项目经历,如有不同,纯属正常】

需求

日常工作中,我这边刷缓存的操作大概2种: 某些新建、删除操作 刷新相关的缓存、定时Job跑 刷全部缓存;本期讲得是 第二种:大批量的key,如何来和表记录做校验。

拿到 相关缓存的 name、value

设计重点:

  1. SCAN命令;
  2. key不同类型时,如何获取 value;
  3. 只要key的name时 【need_keyName=True】,返回的是 {key_name1:None, key_name2:None} ;要key的value时 【need_keyName=False】,返回的是 {key_name1:value1, key_name2:value2};
"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
"""

def get_key_useSCAN(key_name: str, count: int = 5000, key_type: str = 'string', need_keyName: bool = False) -> dict:
    """

    :param key_name:
    :param count: scan 让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素
    :param key_type:
    :param need_keyName:只需要name,不需要value
    :return:只要key的name时,返回的是 {key_name1:None, key_name2:None} ;要key的value时,返回的是 {key_name1:value1, key_name2:value2}
    """
    key_name = '*{}*'.format(key_name)
    Log.info(key_name)

    host, port, pwd = host1, port1, pwd1
    master_list = get_master_list(host=host, port=port, pwd=pwd)

    res_dict = dict()

    for ml in master_list:
        host_m, port_m = ml.split(":")
        Log.debug('{}, {}'.format(host_m, port_m))

        rc = redis_connect(host_m, port_m, pwd)
        cursor = 0

        while True:

            cursor, data = rc.scan(cursor, key_name, count=count)

            Log.debug('游标:{},长度:{}'.format(cursor, len(data)))
            # Log.debug(data)

            if need_keyName:
                data = dict.fromkeys(data)
                res_dict.update(data)

            else:

                for d in data:
                    value = rc.get(d) if key_type == 'string' else (rc.hgetall(d) if key_type == 'hash' else
                                                                    (rc.lrange(d, 0, -1) if key_type == 'list'
                                                                     else rc.smembers(d)))
                    # Log.debug(d)
                    res_dict.update({d: value})

            if not bool(cursor):
                break

        rc.close()

    # Log.debug(res_dict)
    Log.info('redis中 相关key的数量是 {}'.format(len(res_dict)))

    return res_dict

校验name

在这里插入图片描述

因为 chat* 搜出来的所有相关key的value 都是 ‘true’,故我把校验重点放在 key的name;

设计重点:

  1. sql执行后,拿到结果;
  2. 依据 mobile_or_union 传的值,手动构造 key_name;
  3. 返回的是 key_name的list;
"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
"""

def get_key_useSQL_chatMobile_or_chatUnionId(mobile_or_union: str, db: Connection, cur: Cursor) -> list:
    sql = """
    select t2.union_id, group_concat(distinct t3.org_code),t2.mobile 
    from table_cgc t1 
    
    left join table_cgc_member t2
    on t1.chat_id = t2.chat_id
    
    left join table_cgc_org_relation t3
    on t1.chat_id = t3.chat_id
         
    where t1.delete_flag = 0
    and t2.delete_flag = 0
    and t3.delete_flag = 0
    and t2.union_id is not null

     {} and t2.mobile is not null
    group by t2.union_id;

    """.format('' if mobile_or_union == 'mobile' else '#')

    data = fetch_sql(sql=sql, db_name='zy_db', db=db, cur=cur, fetch_mode='fetchall')
    # Log.debug(data)

    res_list = list()

    for d in data:

        mobile = d[-1]
        union_id = d[0]

        for oc in d[1].split(','):

            if mobile_or_union == 'mobile':
                res = ''.join(['chat:mobile:add:', oc, ':', mobile])

            else:
                res = ''.join(['chat:unionId:add:', oc, ':', union_id])

            res_list.append(res)

    # Log.debug(res_list)
    Log.info('手动处理后,缓存的数量为 {}'.format(len(res_list)))

    return res_list
	
"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
"""

if __name__ == "__main__":
    pass

    db_m, cur_m = connect_db(db_name='zy_db')

    # --------

    # string类型
    # 只对比 name

    # # abc = get_key_useSQL_chatMobile_or_chatUnionId(db=db_m, cur=cur_m, mobile_or_union='mobile')
    # abc = get_key_useSQL_chatMobile_or_chatUnionId(db=db_m, cur=cur_m, mobile_or_union='unionId')
    # abc = dict.fromkeys(abc)
    #
    # # ABC = get_key_useSCAN('chat:mobile:add', need_keyName=True)
    # ABC = get_key_useSCAN('chat:unionId:add', need_keyName=True)
    #
    # # Log.info('手动删掉的:{}'.format(ABC.popitem()))  # 主动修改abc,看执行结果
    #
    # compare_dict_key(abc, ABC)
    # compare_dict_key(ABC, abc)

    # --------

    close_db(db_m, cur_m)

校验name、value

在这里插入图片描述

设计重点:

  1. sql在zy_db执行后,拿到结果;取division_code字段值得到 division_code_list;
  2. 组成 data_dict;
  3. sql_ 在zyooooxie_db执行后,拿到结果;
  4. 根据实际key(hash类型)的all fields,生成res_dict;
  5. 返回的是 key:value的dict
"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
"""

def get_key_useSQL_deptRegionInfo(db: Connection, cur: Cursor) -> dict:
    sql = """
	SELECT
        re.region_id,
        re.division_code,
        GROUP_CONCAT(DISTINCT dept.user_id) 
    FROM
        table_relation re
    JOIN
        table_dept dept
        ON re.region_id = dept.department_id
			
    WHERE
        re.delete_flag = 0
        AND dept.delete_flag = 0
		
    GROUP BY
        re.division_code;
    """
    data = fetch_sql(sql=sql, db_name='zy_db', db=db, cur=cur, fetch_mode='fetchall')
    # Log.debug(data)

    division_code_list = [d[1] for d in data]

    data_dict = {d[1]: (d[0], d[-1]) for d in data}
    Log.debug(data_dict)

    if len(division_code_list) == 1:
        division_code = "('{}')".format(division_code_list[0])

    elif len(division_code_list) == 0:
        raise Exception('长度为空-查不到数据')

    else:
        division_code = tuple(division_code_list)

    sql_ = """
    SELECT dept_code, division_code FROM table_zddi WHERE division_code IN {} ; 
    """.format(division_code)

    data_ = fetch_sql(sql=sql_, db_name='zyooooxie_db', fetch_mode='fetchall')
    # Log.debug(data_)

    res_dict = dict()

    for d_ in data_:
        dept_code = d_[0]

        key = 'dept:region:info:{}'.format(dept_code)

        value = data_dict.get(d_[-1])
        new_value = {'regionId': value[0], 'userId': value[1]}

        res_dict.update({key: new_value})

    # Log.debug(res_dict)
    Log.info('手动处理后,缓存的数量为 {}'.format(len(res_dict)))

    return res_dict

"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
"""

if __name__ == "__main__":
    pass

    db_m, cur_m = connect_db(db_name='zy_db')

    # --------

    # # hash类型
    # # 对比 name、value

    abc = get_key_useSQL_deptRegionInfo(db=db_m, cur=cur_m)
    ABC = get_key_useSCAN('dept:region:info', key_type='hash')

    delete_ = ABC.popitem()
    Log.info(delete_)

    ABC.update({delete_[0]: 'test'})  # 主动修改ABC,看执行结果

    compare_dict_key_value(abc, ABC)
    compare_dict_key_value(ABC, abc)

    # --------

    close_db(db_m, cur_m)

公共方法:元素比较

"""
@blog: https://blog.csdn.net/zyooooxie
@qq: 153132336
"""


def compare_dict_key(dict1: Union[dict, list], dict2: Union[dict, list]) -> list:
    """

    :param dict1:
    :param dict2:
    :return:
    """
    if not isinstance(dict1, dict) or not isinstance(dict2, dict):
        Log.info('手动转换类型')
        dict1 = dict.fromkeys(dict1)
        dict2 = dict.fromkeys(dict2)

    not_in_list = list()
    for key in dict1:
        if key not in dict2:
            not_in_list.append(key)

    Log.info(not_in_list)
    Log.info(len(not_in_list))

    return not_in_list


def compare_dict_key_value(dict1: dict, dict2: dict) -> dict:
    """

    :param dict1:
    :param dict2:
    :return:
    """

    not_in_dict = dict()
    for key, value in dict1.items():
        if key not in dict2 or value != dict2.get(key):
            not_in_dict.update({key: value})

    Log.info(not_in_dict)
    Log.info(len(not_in_dict))
    return not_in_dict
    

本文链接:https://blog.csdn.net/zyooooxie/article/details/119377944

个人博客 https://blog.csdn.net/zyooooxie

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137494.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【大小端问题】

什么是大小端? 为什么存在大小端?如何判断计算机的大小端存储模式? 大小端是什么? 计算机在内存存储中有两中存储模式: 大端字节序存储模式和小端字节序存储模式。 大端存储模式,是指数据的低位保存在内…

API接口测试简介

今天继续给大家介绍渗透测试相关知识,本文主要内容是API接口测试简介。 免责声明: 本文所介绍的内容仅做学习交流使用,严禁利用文中技术进行非法行为,否则造成一切严重后果自负! 再次强调:严禁对未授权设备…

Hadoop高手之路8-Flume日志采集

文章目录Hadoop高手之路8-Flume日志采集一、Flume概述1. Flume简介2. Flume运行机制3. Flume日志采集系统结构图二、Flume的搭建1. 下载2. 上传3. 解压4. 配置环境变量5. 配置flume三、Flume入门使用1. 配置数据采集方案1) 查看官网2) 案例需求3) 创建新的配置文件4) 复制官网的…

公司业财一体化详解

一、传统财务会计如何手工做账1.没有财务系统(软件)时公司会计用手工记账,流程包括:建立总账;首先建立账簿,登记会计账簿时,应当将会计凭证日期、编号、业务内容摘要、金额和其他有关资料逐项计…

GAMES101作业5及框架梳理

闲言碎语 emmm,上一次写还是2022年4月份的事情了,真的有点恍如隔世,4月到9月主要是在准备保研的事情,然后10月到12月基本上是在适应实习生活(没错,保完研之后因为种种原因就直接开始实习了,害&…

[Vue]Vue3学习笔记(尚硅谷)

文章目录🥽 创建Vue3项目🌊 vue-cli🌊 vite🥽 项目结构🥽 Vue3开发者工具的安装🥽 初识setup🥽 ref 函数🥽 reactive函数🥽Vue3.0中的响应式原理🌊 vue2.x的响…

微服务架构解决方案介绍

1、微服务架构 目前微服务是非常火的架构或者说概念,也是在构建大型互联网项目时采用的架构方式。 1.1 单体架构 在软件设计中,经常提及和使用经典的3层模型,即表示层、业务逻辑层和数据访问层。 表示层:用于直接和用户交互&a…

内网穿透(mac,window,linux通用)1分钟实现外网访问电脑本地服务器

我们在做开发时,不想购买服务器,只想搭建我们本地的服务器,我们搭建的本地服务器只能供我们自己电脑的浏览器访问,或者处于同一个wifi下的手机访问,但是我们如果想让别人访问到我们的本地服务器,尤其做微信…

共享模型之管程(三)

1.Synchronized优化原理 1.1.轻量级锁(Lock Record) 1.1.1.简介 1>.轻量级锁的使用场景:如果一个对象虽然有多个线程访问,但是多个线程访问的时间是错开的(即没有竞争),那么可以使用轻量级锁来进行优化; 2>.轻量级锁对使用者是透明的,即语法仍然是"synchronized…

docker减少构建镜像大小

目录 1.原镜像大小 1.1 Dockerfile文件 1.2 hello文件 1.3 进入文件夹myprojecthello打包镜像 1.4查看打包的镜像 2.通过拆分文件夹减少镜像大小 2.1 创建两个文件夹 2.2 移动文件 2.3 打包镜像 3. 通过 .dockerignore 文件的方式 3.1 创建 world.txt文件 3.2 创建 …

【Spring(三)】DI入门案例(XML版)

文章目录前言DI入门案例总结前言 前面我们已经演示了IOC入门案例的介绍,里边还有一些东西是耦合的,接下来我们就来学习DI的入门案例来解决这个问题💪💪。 DI入门案例 我们先来想一下,你做DI这个首先得先让IOC容器管着b…

STM32 TIM PWM中阶操作:互补PWM输出

STM32 TIM PWM中阶操作详解:互补PWM输出 STM32 TIM可以输出管脚PWM信号适合多种场景使用,功能包括单线/非互补PWM输出,双线/互补PWM输出,以及死区时间和刹车控制等。 实际上,因为早期IP Core的缺陷,早期的…

万应低代码12月重点更新内容速递

速览版 详情版 低代码开发效率升级 01 动作流 动作编排过程中涉及到多条件判断时使用,即:满足某条件可执行一条分支,不满足则执行“其他”分支。 【使用场景】 ● 以“个人所得税计算”场景为例,不同收入水平的人输入不同的收…

人工智能对联生成 API 数据接口

人工智能对联生成 API 数据接口 基于百万数据训练,AI 训练与应答,多结果返回。 1. 产品功能 AI 基于百万历史对联数据训练应答模型;机器学习持续训练学习;一个上联可返回多个下联应答;毫秒级响应性能;数据…

关键字:final

文章目录一、final修饰类修饰方法修饰变量修饰属性修饰局部变量static final练习每日一考一、final final:最终的 final可以修饰的结构:类、方法、变量 修饰类 此类不能被其他类所继承 修饰方法 此方法不可以被重写 修饰变量 此时的“变量”就称为…

ACL论文总结

「博士毕业一年,我拿下 ACL Best Paper」 在不久前结束的自然语言处理NLP,领域顶级学术会议ACL2021上,字节跳动AL lab研究院许晶晶,完成了他的演讲。 在全球顶会做完分享后,许晶晶感到很欣慰,没想到&#…

自然语言处理重点 第11章 机器阅读理解 复习

机器阅读理解复习机器阅读理解概述机器阅读(MRC)理解与问答系统(QA)的区别:本章内容:MRC 任务分类:完形填空形式(cloze-style)选项形式片段抽取形式(span extraction)文本生成形式(free-answer/…

uCharts柱状图横向排列及不同条件下得数据颜色不同,雷达图的使用及各个参数的配置讲解

一:雷达图 1,建立一个盒子,内部存放uCharts图表 在data数据中return内部配置如下数据: chartData: {},//您可以通过修改 config-ucharts.js 文件中下标为 [radar] 的节点来配置全局默认参数,如都是默认参数,此处可以不传 opts 。实际应用过程中 opts 只需传入与全局默认…

【台式机DIY】我的第一台台式机电脑配置清单

文章目录[toc]【第一台台式机】一、电脑配置清单【电脑小白我科普】一.CPU1.选购:主流品牌2.选购:如何选择3.参数:接口4.参数:频率5.参数:核心和线程6.参数:功耗7.参数:缓存二.主板1.选购&#…

Django开发员工管理系统(Part I)

文章目录1. 准备工作1.1 创建django项目1.2 创建app1.3 配置settings.py文件,完成app注册2. 设计数据库表结构3. 在MySQL中生成表3.1 创建数据库3.2 修改配置文件,连接MySQL数据库3.3 通过django命令生成数据库表4. 编写部门列表4.1 (前段页面…