Python实现Redis缓存MySQL数据并支持数据同步

news2024/11/26 6:22:04
简介

本文讲讲如何用Redis做MySQL的读缓存,提升数据库访问性能。

MySQL是一种很常用的关系型数据库,用于持久化数据,并存放在磁盘上。但如果有大数据量的读写,靠MySQL单点就会捉襟见肘,尽管可以在MySQL本身做优化,比如用更好的SQL语句设计、索引等等。也会用主从设计集群设计来优化性能。甚至借助工具做成分布式数据库。不过还有一种简单的方式来提升读性能,就是在MySQL的前面放一个缓存,比如Redis。Redis是一种高性能的内存数据库,用作缓存非常合适。Redis还支持分布式集群,来优化读写性能。Redis也可以持久化数据到磁盘,但Redis的持久化一定程度上会有丢数据的可能,因此数据完整性要求高的场合用MySQL更合适,而Redis用作缓存。

本文的重点是要解决数据的查询和更新过程中数据库的一致性问题。话不多说,开始上菜。

查询一致性

查询数据时:

  1. 先从Redis读取,如果存在则直接返回;
  2. 如果不存在则向MySQL查询数据;
    1. 把从MySQL读取的数据更新到Redis;
    2. 返回从MySQL读取的数据;

添加过期时间:

  1. Redis的记录添加过期时间;
    1. 如果没有记录,创建记录时会添加过期时间;
    2. 如果有记录:
      1. 如果过期时间内没有被查询,自动被Redis删除数据;
      2. 如果过期时间内被查询,重置过期时间,续期;

这样可以定时清除Redis中查询不频繁的数据,增加数据读取速度。

更新一致性

数据更新时:

  1. 先查询redis,如果有数据,先删除缓存数据
  2. 然后先写入更新数据到redis,并设置过期时间
  3. 最后再写入更新数据到mysql:
    1. 如果写入mysql失败,回滚(删除)redis和mysql的数据
MySQL表设计

使用学生表作为例子,存储学生的学号、姓名、生日、电话,主键为学号,建表语句如下:

# 以Linux命令行为例
mysql -uroot -pPASSWORD
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE tb_student(
    stu_id INT AUTO_INCREMENT PRIMARY KEY NOT NULL,
    stu_name VARCHAR(20) NOT NULL,
    stu_birth DATE,
    stu_phone VARCHAR(100)
);

插入数据:

# 多插入几条不一样的数据
INSERT INTO tb_student(
    stu_name,
    stu_birth,
    stu_phone
) VALUES (
    'Tom',
    '1900-11-11',
    15000000000
);

数据如下:

Redis数据设计

使用Redis的hash类型存储MySQL读缓存,因为hash类型一个表可以对应多个键值对:

hmset stu_id:1 stu_name 'Tom' stu_birth '1900-11-11' stu_phone '15000000000'

同时设置过期时间10分钟:

expire stu_id:1 600
Python代码

完整代码如下:

import pymysql
import redis


class DatabaseCache:
    def __init__(self):
        # 连接MySQL
        self.mysql = pymysql.connect(
            host='192.168.173.140',
            port=3306,
            user='root',
            password='123456',
            database='testdb',
            charset='utf8mb4'
        )

        # 连接Redis
        self.redis = redis.Redis(
            host='192.168.173.140',
            port=6379,
            db=0,
            password='123456',
            decode_responses=True
        )

    def get_data(self, stu_id):
        # 根据学生id查询,name为redis hash表名
        name = f'stu_id:{stu_id}'
        res = self.redis.hgetall(name)

        # 查询到内容res为非空字典,否则为空字典
        if res:
            # 先查询Redis中是否存在数据,存在则直接返回,并且重置过期时间10分钟
            self.redis.expire(name, 600)
            print(f'get_data:redis有数据: {res}')
            return res

        else:
            # 没有查询到数据
            with self.mysql.cursor() as cursor:
                try:
                    # 从mysql查询
                    cursor.execute(
                        f'select stu_name, stu_birth, stu_phone from tb_student where stu_id={stu_id}'
                    )
                    data = cursor.fetchall()
                    print(f'get_data:redis无数据:mysql数据: {data}')

                    # 把查询结果写入Redis中,并设置过期时间10分钟
                    stu_name, stu_birth, stu_phone = data[0]  # 解包
                    cache_data = {
                        'stu_name': stu_name,
                        'stu_birth': str(stu_birth),
                        'stu_phone': stu_phone
                    }
                    self.redis.hset(name, mapping=cache_data)
                    self.redis.expire(name, 600)  # 设置过期时间

                    # 然后返回数据
                    return data

                except Exception as err:
                    print(f'{err=}')

    def put_data(self, data: list):
        stu_id, stu_name, stu_birth, stu_phone = data
        name = f'stu_id:{stu_id}'

        # 先查询redis中是否有数据,如果有先删除
        res = self.redis.hgetall(name)
        if res:
            # 如果只是部分更新,也可以不删除
            keys = self.redis.hkeys(name)
            self.redis.hdel(name, *keys)

        # 然后先写入新数据到redis
        new_data = {
            'stu_name': stu_name,
            'stu_birth': stu_birth,
            'stu_phone': stu_phone
        }
        self.redis.hset(name, mapping=new_data)
        self.redis.expire(name, 600)

        # 更新完redis,再写入mysql
        with self.mysql.cursor() as cursor:
            try:
                # 执行失败会抛异常
                cursor.execute(f'select stu_id from tb_student where stu_id={stu_id}')
                _res = cursor.fetchone()  # 无记录返回None
                if _res:
                    sql = f'update tb_student set stu_name="{stu_name}", stu_birth="{stu_birth}", ' \
                          f'stu_phone="{stu_phone}" where stu_id={stu_id}'
                    cursor.execute(sql)
                else:
                    cursor.execute(
                        f'insert into tb_student values ({stu_id}, "{stu_name}", "{stu_birth}", "{stu_phone}")'
                    )
                self.mysql.commit()
                print('redis有数据:mysql写入数据成功')

            except Exception as err:
                # 如果写入mysql不成功,需要回退redis,即删除redis中刚刚写入的数据
                keys = self.redis.hkeys(name)
                self.redis.hdel(name, *keys)
                self.mysql.rollback()  # mysql也要回退
                print(f'{err=}')

    def close(self):
        self.mysql.close()
        self.redis.close()


if __name__ == '__main__':
    db = DatabaseCache()
    db.get_data(2)
    db.put_data([5, 'Eve', '1995-01-01', '18800003333'])
    db.close()

完。

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

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

相关文章

【Python大纲】S0 系列博文大纲

系列博文大纲 Python 环境建议系列博文相关书籍系列博文大纲阶段进展 Python 环境建议 “工欲善其事、必先利其器”,“三分手艺、七分家伙”,选择一个好用的开发环境对于编程而言至关重要。博主不过多赘述,提出的建议是:Anaconda…

Invalid prop: custom validator check failed for prop “percentage

在使用element 进度条时出现如下报错: Invalid prop: custom validator check failed for prop “percentage 这是因为percentage的取值范围为0-100 检查下自己的值是否超出这个范围了

eclipse svn插件安装

1.进入eclipse的help->Eclipse Marketplace,如下图所示: 2.输入“svn”,再按回车,如下图: 3.这我选择的是 Subversive,点击后面的“install”按钮,如下图 Eclipse 下连接 SVN 库有两种插件 —— Subclipse 与 Subversive &…

想要精通算法和SQL的成长之路 - 预测赢家

想要精通算法和SQL的成长之路 - 预测赢家 前言一. 预测赢家二. 石子游戏(预测赢家的进阶版)2.1 博弈论 前言 想要精通算法和SQL的成长之路 - 系列导航 一. 预测赢家 原题链接 主要思路: 我们定义dp[i][j]:在区间 [i, j] 之间先…

QT 6.5下载安装及配置教程

一、下载 今天打算换一个QT安装版本,去官网看了一眼发现变成了在线安装。 本来官方下载的就慢,现在更是雪上加霜,现在给大家推荐几种下载方式。 1、国内镜像 中国科学技术大学:http://mirrors.ustc.edu.cn/qtproject/ 清华大学:https://mirrors.tuna.ts…

基于强化学习的节能路由(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

员工电脑怎么监控(怎样远程监控员工电脑)

随着科技的发展,企业对于员工电脑的监控已经成为了企业管理的重要手段。通过监控员工电脑,企业可以更好地管理员工的工作效率、保护企业数据安全以及维护企业的正常运营。本文将详细介绍如何监控公司员工电脑的方法和步骤。 监控员工电脑的目的 提高工作…

Selenium-介绍下其他骚操作

Chrome DevTools 简介 Chrome DevTools 是一组直接内置在基于 Chromium 的浏览器(如 Chrome、Opera 和 Microsoft Edge)中的工具,用于帮助开发人员调试和研究网站。 借助 Chrome DevTools,开发人员可以更深入地访问网站&#xf…

vsc使用flake8的一个小问题

当单行写入代码长度过长时候PEP8会直接提示报错,在flake8中的单行代码限制是小于80但是对于一些需要精准import的代码来说远不够用,网络上有人说直接在josn文件中做变动,但是当导入相关命令的时候,会显示该命令不可用,…

一步到位:用Python实现PC屏幕截图并自动发送邮件,实现屏幕监控

在当前的数字化世界中,自动化已经成为我们日常生活和工作中的关键部分。它不仅提高了效率,还节省了大量的时间和精力。在这篇文章中,我们将探讨如何使用Python来实现一个特定的自动化任务 - PC屏幕截图自动发送到指定的邮箱。 这个任务可能看…

Vulnhub系列靶机-Hackadmeic.RTB1

文章目录 Vulnhub系列靶机-Hackadmeic.RTB11. 信息收集1.1 主机扫描1.2 端口扫描1.3 目录爆破 2. 漏洞探测3. 漏洞利用3.1 反弹Shell 4. 内核提权 Vulnhub系列靶机-Hackadmeic.RTB1 1. 信息收集 1.1 主机扫描 arp-scan -l1.2 端口扫描 nmap -A -p- 192.168.188.184扫描到了…

大功率光伏应用不同多电平变换器拓扑的比较研究(Simulink)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

美团2024届秋招笔试第一场编程【小美走公路】

题目描述: 有一个环形的公路,上面共有n站,现在给定了顺时针第i站到第i1站之间的距离(特殊的,也给出了第n站到第 1 站的距离)。小美想沿着公路第x站走到第y站,她想知道最短的距离是多少&#xf…

自然语言处理(一):基于统计的方法表示单词

文章目录 1. 共现矩阵2. 点互信息3. 降维(奇异值分解) 1. 共现矩阵 将一句话的上下文大小窗口设置为1,用向量来表示单词频数,如: 将每个单词的频数向量求出,得到如下表格,即共现矩阵&#x…

分别用Python和Go实现对文件夹及其子文件夹里的文件进行批量重命名

文章目录 问题阐述上代码结果如何 问题阐述 最近在继续提高自己的go技术时,从网上一些平台获取到了一些学习资料,然后下载到本地后,文件的命名是真的像衣托答辩: 除了上述的文件,还有一mol多神奇的命名,害…

GROMACS Tutorial 1: Lysozyme in Water 中文实战教程

GROMACS Tutorial 1: Lysozyme in Water 中文实战教程 前言系统环境特别强调一、预处理阶段1.1 补全原子或残基1.2 删除水分子1.3 生成top文件等位置限制文件 二、定义盒子及添加溶剂2.1 定义盒子2.2 加入溶剂 三、添加离子3.1 使用mdp参数文件生成tpr文件3.2 离子的加入3.3 添…

MasterAlign相机参数设置-曝光时间调节

相机参数设置-曝光时间调节操作说明 相机参数的设置对于获取清晰、准确的图像至关重要。曝光时间是其中一个关键参数,它直接影响图像的亮度和清晰度。以下是关于曝光时间调节的详细操作步骤,以帮助您轻松进行设置。 步骤一:登录系统 首先&…

避免分库分表,绿普惠的分布式数据库选型与实践

作者:翻墨,绿普惠科技(北京)有限公司架构师 一项数据显示,今天大气中的二氧化碳水平比过去 65 万年高了 27%。主要原因来自于工业化需求下的煤炭燃烧、汽车尾气。随着人类活动造成的温室效应加剧,环保越来越…

从服务器指定位置下载文件

从服务器指定位置下载文件 下载文件转换成流,这里说两种流的方式:1. 文件流2. 字节流 下载文件转换成流,这里说两种流的方式: 1. 文件流 2. 字节流 一,字节流 String filePath“/opt/peoject/file/123/pdf”; //这个是你服务上存放文件位置…

公司监控员工上网记录,具体能监控到哪些内容?

在信息时代,随着技术的发展和商业竞争的加剧,公司对员工的监控变得越来越普遍。本文将从多个角度探讨公司监控员工电脑的利与弊,以期为读者提供全面的认识。 公司可以监控员工电脑吗 答案是可以的,但需要遵守相关法律法规和公司的…