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

简介

本文讲讲如何用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 redisclass DatabaseCache:def __init__(self):# 连接MySQLself.mysql = pymysql.connect(host='192.168.173.140',port=3306,user='root',password='123456',database='testdb',charset='utf8mb4')# 连接Redisself.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 reselse:# 没有查询到数据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 dataexcept Exception as err:print(f'{err=}')def put_data(self, data: list):stu_id, stu_name, stu_birth, stu_phone = dataname = f'stu_id:{stu_id}'# 先查询redis中是否有数据,如果有先删除res = self.redis.hgetall(name)if res:# 如果只是部分更新,也可以不删除keys = self.redis.hkeys(name)self.redis.hdel(name, *keys)# 然后先写入新数据到redisnew_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,再写入mysqlwith self.mysql.cursor() as cursor:try:# 执行失败会抛异常cursor.execute(f'select stu_id from tb_student where stu_id={stu_id}')_res = cursor.fetchone()  # 无记录返回Noneif _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.rhkb.cn/news/138024.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Qt httpclient

记录一次Qt中处理https请求的操作 构造函数 get onFinished函数: onCompleted是对外的信号,这里接收的数据主要是文本类 post form post json Form 与 Json的差别是http header 的设置 文件下载处理 这里与服务器有个约定,文件长度不能小于…

springboot整合sentinel完成限流

1、直入正题,下载sentinel的jar包 1.1 直接到Sentinel官网里的releases下即可下载最新版本,Sentinel官方下载地址,直接下载jar包即可。不过慢,可能下载不下来 1.2 可以去gitee去下载jar包 1.3 下载完成后,进行打包…

68、Spring Data JPA 的 方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体)

1、方法名关键字查询(全自动,既不需要提供sql语句,也不需要提供方法体) 2、Query查询(半自动:提供 SQL 或 JPQL 查询) 3、自定义查询(全手动) ★ 方法名关键字查询&…

简明 SQL 组合查询指南:掌握 UNION 实现数据筛选

在SQL中,组合查询是一种将多个SELECT查询结果合并的操作,通常使用UNION和UNION ALL两种方式。 UNION 用于合并多个查询结果集,同时去除重复的行,即只保留一份相同的数据。UNION ALL 也用于合并多个查询结果集,但不去除…

3D模型格式转换工具HOOPS Exchange与iBase-t的Solumina集成:支持用户查询与编辑模型

iBase-t是一家软件公司,致力于简化复杂产品的构建和维护。iBase-t 于 1986 年在南加州成立,提供的解决方案可确保全球范围内制造、质量以及维护、修理和大修 (MRO) 运营的数字连续性。iBase-t 的 Solumina 制造运营平台是一种云原生解决方案,…

PX4 通过 Vision 实现 Position、Altitude 和 Offboard 模式

本文通过 VINS-Fusion 的里程计信息为 PX4 提供视觉信息,从而达到 视觉定高和定点 的目的 主要工作为创建一个将 vins 里程计信息发布给 Mavros 的 /mavros/vision_pose/pose 话题 首先创建一个工作空间 mkdir -p ~/catkin_ws/src/vision_to_mavros/src/ cd ~/ca…

贝叶斯滤波计算4d毫米波聚类目标动静属性

机器人学中有些问题是二值问题,对于这种二值问题的概率评估问题可以用二值贝叶斯滤波器binary Bayes filter来解决的。比如机器人前方有一个门,机器人想判断这个门是开是关。这个二值状态是固定的,并不会随着测量数据变量的改变而改变。就像门…

企业架构LNMP学习笔记46

PHP测试连接代码&#xff1a; php代码测试使用memcached&#xff1a; 示例代码&#xff1a; <?php //实例化类 $mem new memcached(); //调用连接memcached方法 注意连接地址和端口号 $mem->addServer(192.168.17.114,11211); //存数据 var_dump($mem->set(name,l…

python基于轻量级卷积神经网络模型开发构建眼疾识别系统

常见的眼疾包括但不限于以下几种&#xff1a; 白内障&#xff1a;白内障是眼睛晶状体变得模糊或不透明&#xff0c;导致视力下降。它通常与年龄相关&#xff0c;但也可以由其他因素引起&#xff0c;如遗传、外伤、糖尿病等。 青光眼&#xff1a;青光眼是一组引起视神经损伤的眼…

【Hadoop】HDFS API 操作大全

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_Linux,大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1…

DC/DC开关电源学习笔记(十)Buck降压电路仿真及工程应用实例

(十)Buck降压电路仿真及工程应用实例 1. 仿真应用实例1.1 案例一1.2 案例二2. 工程应用实例2.1 数字DC/DC应用实例2.2 模拟DC/DC应用实例1. 仿真应用实例 1.1 案例一 仿真技术要求输入:输入电压30~90V,输出电压28V,输出电流最大10A,开关频率100KHz。我们按照参数极限工…

【Vue】使用vue-cli搭建SPA项目的路由,嵌套路由

一、SPA项目的构建 1、前期准备 我们的前期的准备是搭建好Node.js,测试&#xff1a; node -v npm -v2、利用Vue-cli来构建spa项目 2.1、什么是Vue-cli Vue CLI 是一个基于 Vue.js 的官方脚手架工具&#xff0c;用于自动生成vue.jswebpack的项目模板&#xff0c;它可以帮助开发者…

Qt(day5)

思维导图 将登录操作和数据库绑定 mywnd.h #ifndef MYWND_H #define MYWND_H#include <QMainWindow> #include<QLabel> #include<QLineEdit> #include<QPushButton> #include<QDebug> #include<QMessageBox> #include"second.h&qu…

零基础转行网络安全可以做什么工作,内附网络安全自学路线

一直在说网络安全行业好就业、薪资高、前景也好&#xff0c;但是大家对网络安全这个行业具体做什么工作可能还一知半解。所以今天来跟大家聊聊&#xff0c;网络安全学完可以找到什么样的工作&#xff0c;顺便把不同岗位的不同技术要求也说一下。 【点击文章末尾卡片&#xff0…

Spring Security 对请求的处理流程

文章目录 前言系统启动Spring Security 对请求的处理总结 前言 分析Spring Security的核心原理&#xff0c;可以从以下几个方面进行&#xff1a; 系统启动的时候Spring Security做了哪些事情&#xff1f;发起一次请求后Spring Security做了哪些事情&#xff1f; 系统启动 当…

Puppeteer基础入门、常见应用、利用谷歌插件编写Puppeteer脚本

前言 Puppeteer已经听说过很多次了&#xff0c;也见过一些与之相关的文章。但是一直没怎么研究过&#xff0c;现在来简单学习一下。 简介 Puppeteer 是一个 Node 库&#xff0c;它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。Puppeteer 默认以 headles…

【离网逆变器】离网逆变器型号由一个高频DC-DC升压转换器与全桥PI控制电压源逆变器级联组成、逆变器使用带LC滤波器的SPWM调制(Simulink)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

HTML5+CSS3+JS小实例:鼠标控制飞机的飞行方向

实例:鼠标控制飞机的飞行方向 技术栈:HTML+CSS+JS 效果: 源码: 【html】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" conten…

Vue项目前端代码防止被调试

项目背景 被安全测试针对了&#xff0c;总是调试我这不太安全的代码。前端代码深度混淆转成十六进制还不行&#xff0c;仍然找到加密方法&#xff0c;对后端数据进行解密。这次就修改了思路换种方法: 我承认阁下很强&#xff0c;但假如, 我是说假如打开控制台是空白页面&…

森林防火可视化智能监管与风险预警系统解决方案

一、方案背景 森林火灾是世界八大自然灾害之一&#xff0c;具有发生面广、突发性强、破坏性大、危险性高、处置扑救特别困难等特点&#xff0c;严重危及人民生命财产和森林资源安全&#xff0c;甚至引发生态灾难。有效预防和及时控制森林火灾是保护国家生态建设成果、推进生态…