day26-python操作MySQL和实战

这篇具有很好参考价值的文章主要介绍了day26-python操作MySQL和实战。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 事务

innodb引擎中支持事务,myisam不支持。

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `name` varchar(32) DEFAULT NULL,
  `amount` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

day26-python操作MySQL和实战

例如:李杰 给 武沛齐 转账 100,那就会涉及2个步骤。

  • 李杰账户 减100
  • 武沛齐账户 加 100

这两个步骤必须同时完成才算完成,并且如果第一个完成、第二步失败,还是回滚到初始状态。

事务,就是来解决这种情况的。 大白话:要成功都成功;要失败都失败。

事务的具有四大特性(ACID):

  • 原子性(Atomicity)

    原子性是指事务包含的所有操作不可分割,要么全部成功,要么全部失败回滚。
    
  • 一致性(Consistency)

    执行的前后数据的完整性保持一致。
    
  • 隔离性(Isolation)

    一个事务执行的过程中,不应该受到其他事务的干扰。
    
  • 持久性(Durability)

    事务一旦结束,数据就持久到数据库
    

1.1 MySQL客户端

mysql> select * from users;
+----+---------+---------+
| id | name    | amount  |
+----+---------+---------+
|  1 | wupeiqi |    5    |
|  2 |  alex   |    6    |
+----+---------+---------+
3 rows in set (0.00 sec)

mysql> begin;  -- 开启事务 start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> update users set amount=amount-2 where id=1;   -- 执行操作
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> update users set amount=amount+2 where id=2;   -- 执行操作
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> commit;  -- 提交事务  rollback;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from users;
+----+---------+---------+
| id | name    | amount  |
+----+---------+---------+
|  1 | wupeiqi |    3    |
|  2 |  ale x  |    8    |
+----+---------+---------+
3 rows in set (0.00 sec)
mysql> select * from users;
+----+---------+---------+
| id | name    | amount  |
+----+---------+---------+
|  1 | wupeiqi |    3    |
|  2 |  ale x  |    8    |
+----+---------+---------+
3 rows in set (0.00 sec)

mysql> begin; -- 开启事务
Query OK, 0 rows affected (0.00 sec)

mysql> update users set amount=amount-2 where id=1; -- 执行操作(此时数据库中的值已修改)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> rollback; -- 事务回滚(回到原来的状态)
Query OK, 0 rows affected (0.00 sec)

mysql> select * from users;
+----+---------+---------+
| id | name    | amount  |
+----+---------+---------+
|  1 | wupeiqi |    3    |
|  2 |  ale x  |    8    |
+----+---------+---------+
3 rows in set (0.00 sec)

1.2 Python代码

import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8", db='userdb')
cursor = conn.cursor()

# 开启事务
conn.begin()

try:
    cursor.execute("update users set amount=1 where id=1")
    int('asdf')
    cursor.execute("update tran set amount=2 where id=2")
except Exception as e:
    # 回滚
    print("回滚")
    conn.rollback()
else:
    # 提交
    print("提交")
    conn.commit()

cursor.close()
conn.close()

2. 锁

在用MySQL时,不知你是否会疑问:同时有很多做更新、插入、删除动作,MySQL如何保证数据不出错呢?

MySQL中自带了锁的功能,可以帮助我们实现开发过程中遇到的同时处理数据的情况。对于数据库中的锁,从锁的范围来讲有:

  • 表级锁,即A操作表时,其他人对整个表都不能操作,等待A操作完之后,才能继续。
  • 行级锁,即A操作表时,其他人对指定的行数据不能操作,其他行可以操作,等待A操作完之后,才能继续。
MYISAM支持表锁,不支持行锁;
InnoDB引擎支持行锁和表锁。

即:在MYISAM下如果要加锁,无论怎么加都会是表锁。
    在InnoDB引擎支持下如果是基于索引查询的数据则是行级锁,否则就是表锁。

所以,一般情况下我们会选择使用innodb引擎,并且在 搜索 时也会使用索引(命中索引)。

接下来的操作就基于innodb引擎来操作:

CREATE TABLE `L1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `count` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8;

day26-python操作MySQL和实战

在innodb引擎中,update、insert、delete的行为内部都会先申请锁(排它锁),申请到之后才执行相关操作,最后再释放锁。

所以,当多个人同时像数据库执行:insert、update、delete等操作时,内部加锁后会排队逐一执行。

而select则默认不会申请锁。

select * from xxx;

如果,你想要让select去申请锁,则需要配合 事务 + 特殊语法来实现。

  • for update,排它锁,加锁之后,其他不可以读写。

    begin; 
    	select * from L1 where name="武沛齐" for update;    -- name列不是索引(表锁)
    commit;
    
    begin; -- 或者 start transaction;
    	select * from L1 where id=1 for update;			  -- id列是索引(行锁)
    commit;
    
  • lock in share mode ,共享锁,加锁之后,其他可读但不可写。

    begin; 
    	select * from L1 where name="武沛齐" lock in share mode;    -- 假设name列不是索引(表锁)
    commit;
    
    begin; -- 或者 start transaction;
    	select * from L1 where id=1 lock in share mode;           -- id列是索引(行锁)
    commit;
    

2.1 排它锁

排它锁( for update),加锁之后,其他事务不可以读写。

应用场景:总共100件商品,每次购买一件需要让商品个数减1 。

A: 访问页面查看商品剩余 100
B: 访问页面查看商品剩余 100

此时 A、B 同时下单,那么他们同时执行SQL:
	update goods set count=count-1 where id=3
由于Innodb引擎内部会加锁,所以他们两个即使同一时刻执行,内部也会排序逐步执行。


但是,当商品剩余 1个时,就需要注意了。
A: 访问页面查看商品剩余 1
B: 访问页面查看商品剩余 1

此时 A、B 同时下单,那么他们同时执行SQL:
	update goods set count=count-1 where id=3
这样剩余数量就会出现 -1,很显然这是不正确的,所以应该怎么办呢?


这种情况下,可以利用 排它锁,在更新之前先查询剩余数量,只有数量 >0 才可以购买,所以,下单时应该执行:
	begin; -- start transaction;
	select count from goods where id=3 for update;
	-- 获取个数进行判断
	if 个数>0:
		update goods set count=count-1 where id=3;
	else:
		-- 已售罄
	commit;

基于Python代码示例:

import pymysql
import threading


def task():
    conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='root123', charset="utf8", db='userdb')
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    # cursor = conn.cursor()
	
    # 开启事务
    conn.begin()

    cursor.execute("select id,age from tran where id=2 for update")
    # fetchall      ( {"id":1,"age":10},{"id":2,"age":10}, )   ((1,10),(2,10))
    # {"id":1,"age":10}   (1,10)
    result = cursor.fetchone()
    current_age = result['age']
    
    if current_age > 0:
        cursor.execute("update tran set age=age-1 where id=2")
    else:
        print("已售罄")

    conn.commit()

    cursor.close()
    conn.close()


def run():
    for i in range(5):
        t = threading.Thread(target=task)
        t.start()


if __name__ == '__main__':
    run()

2.2 共享锁

共享锁( lock in share mode),可以读,但不允许写。

加锁之后,后续其他事物可以可以进行读,但不允许写(update、delete、insert),因为写的默认也会加锁。

Locking Read Examples

Suppose that you want to insert a new row into a table child, and make sure that the child row has a parent row in table parent. Your application code can ensure referential integrity throughout this sequence of operations.

First, use a consistent read to query the table PARENT and verify that the parent row exists. Can you safely insert the child row to table CHILD? No, because some other session could delete the parent row in the moment between your SELECT and your INSERT, without you being aware of it.

To avoid this potential issue, perform the SELECT using LOCK IN SHARE MODE:

SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

After the LOCK IN SHARE MODE query returns the parent 'Jones', you can safely add the child record to the CHILD table and commit the transaction. Any transaction that tries to acquire an exclusive lock in the applicable row in the PARENT table waits until you are finished, that is, until the data in all tables is in a consistent state.

3. 数据库连接池

day26-python操作MySQL和实战

在操作数据库时需要使用数据库连接池。

pip3.9 install pymysql
pip3.9 install dbutils
import threading
import pymysql
from dbutils.pooled_db import PooledDB

MYSQL_DB_POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=5,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=3,  # 链接池中最多闲置的链接,0和None不限制
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    # ping MySQL服务端,检查是否服务可用。
    # 如:0 = None = never, 1 = default = whenever it is requested, 
    # 2 = when a cursor is created, 4 = when a query is executed, 7 = always
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root123',
    database='userdb',
    charset='utf8'
)


def task():
    # 去连接池获取一个连接
    conn = MYSQL_DB_POOL.connection()
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    
    cursor.execute('select sleep(2)')
    result = cursor.fetchall()
    print(result)

    cursor.close()
    # 将连接交换给连接池
    conn.close()

def run():
    for i in range(10):
        t = threading.Thread(target=task)
        t.start()


if __name__ == '__main__':
    run()

4. SQL工具类

基于数据库连接池开发一个公共的SQL操作类,方便以后操作数据库。

4.1 单例和方法

# db.py
import pymysql
from dbutils.pooled_db import PooledDB


class DBHelper(object):

    def __init__(self):
        # TODO 此处配置,可以去配置文件中读取。
        self.pool = PooledDB(
            creator=pymysql,  # 使用链接数据库的模块
            maxconnections=5,  # 连接池允许的最大连接数,0和None表示不限制连接数
            mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
            maxcached=3,  # 链接池中最多闲置的链接,0和None不限制
            blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
            setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
            ping=0,
            # ping MySQL服务端,检查是否服务可用。# 如:0 = None = never, 1 = default = whenever it is requested, 2 = when a cursor is created, 4 = when a query is executed, 7 = always
            host='127.0.0.1',
            port=3306,
            user='root',
            password='root123',
            database='userdb',
            charset='utf8'
        )

    def get_conn_cursor(self):
        conn = self.pool.connection()
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        return conn, cursor

    def close_conn_cursor(self, *args):
        for item in args:
            item.close()

    def exec(self, sql, **kwargs):
        conn, cursor = self.get_conn_cursor()

        cursor.execute(sql, kwargs)
        conn.commit()

        self.close_conn_cursor(conn, cursor)

    def fetch_one(self, sql, **kwargs):
        conn, cursor = self.get_conn_cursor()

        cursor.execute(sql, kwargs)
        result = cursor.fetchone()

        self.close_conn_cursor(conn, cursor)
        return result

    def fetch_all(self, sql, **kwargs):
        conn, cursor = self.get_conn_cursor()

        cursor.execute(sql, kwargs)
        result = cursor.fetchall()

        self.close_conn_cursor(conn, cursor)

        return result


db = DBHelper()

from db import db

db.exec("insert into d1(name) values(%(name)s)", name="武沛齐666")

ret = db.fetch_one("select * from d1")
print(ret)

ret = db.fetch_one("select * from d1 where id=%(nid)s", nid=3)
print(ret)

ret = db.fetch_all("select * from d1")
print(ret)

ret = db.fetch_all("select * from d1 where id>%(nid)s", nid=2)
print(ret)

4.2 上下文管理

如果你想要让他也支持 with 上下文管理。

with 获取连接:
	执行SQL(执行完毕后,自动将连接交还给连接池)

# db_context.py
import threading
import pymysql
from dbutils.pooled_db import PooledDB

POOL = PooledDB(
    creator=pymysql,  # 使用链接数据库的模块
    maxconnections=5,  # 连接池允许的最大连接数,0和None表示不限制连接数
    mincached=2,  # 初始化时,链接池中至少创建的空闲的链接,0表示不创建
    maxcached=3,  # 链接池中最多闲置的链接,0和None不限制
    blocking=True,  # 连接池中如果没有可用连接后,是否阻塞等待。True,等待;False,不等待然后报错
    setsession=[],  # 开始会话前执行的命令列表。如:["set datestyle to ...", "set time zone ..."]
    ping=0,
    host='127.0.0.1',
    port=3306,
    user='root',
    password='root123',
    database='userdb',
    charset='utf8'
)


class Connect(object):
    def __init__(self):
        self.conn = conn = POOL.connection()
        self.cursor = conn.cursor(pymysql.cursors.DictCursor)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.cursor.close()
        self.conn.close()

    def exec(self, sql, **kwargs):
        self.cursor.execute(sql, kwargs)
        self.conn.commit()

    def fetch_one(self, sql, **kwargs):
        self.cursor.execute(sql, kwargs)
        result = self.cursor.fetchone()
        return result

    def fetch_all(self, sql, **kwargs):
        self.cursor.execute(sql, kwargs)
        result = self.cursor.fetchall()
        return result

from db_context import Connect

with Connect() as obj:
    # print(obj.conn)
    # print(obj.cursor)
    ret = obj.fetch_one("select * from d1")
    print(ret)

    ret = obj.fetch_one("select * from d1 where id=%(id)s", id=3)
    print(ret)

5.其他

navicat,是一个桌面应用,让我们可以更加方便的管理MySQL数据库。

  • mac系统:https://www.macdo.cn/17030.html
  • win系统:
    • 链接: https://pan.baidu.com/s/13cjbrBquz9vjVqKgWoCQ1w 密码: qstp
    • 链接: https://pan.baidu.com/s/1JULIIwQA5s0qN98KP8UXHA 密码: p18f

总结

本节内容比较重要,也是开发中经常会使用到的技能。

  • 事务,解决批量操作同时成功或失败的问题。
  • 锁,解决并发处理的问题。
  • 数据库连接池,解决多个人请求连接数据库的问题。
  • SQL工具类,解决连接数据库代码重复的问题。
  • navicat工具

大作业:开发博客系统

请基于你掌握的所有技能,实现 day24 博客系统的所有功能。

根据如下的业务需求设计相应的表结构,内部需涵盖如下功能。

  • 注册
  • 登录
  • 发布博客
  • 查看博客列表,显示博客标题、创建时间、阅读数量、评论数量、赞数量等。(支持分页查看)
  • 博客详细,显示博文详细、评论 等。
    • 发表评论
    • 赞 or 踩
    • 阅读数量 + 1

可参考如下图片来设计相应的表结构。

1. 注册和登录

day26-python操作MySQL和实战

2. 文章列表

day26-python操作MySQL和实战

3. 文章详细

day26-python操作MySQL和实战

4. 评论 & 阅读 & 赞 & 踩

day26-python操作MySQL和实战

注意:假设都是一级评论(不能回复评论)。

参考代码:https://files.cnblogs.com/files/blogs/814578/blog.zip?t=1714289879&download=true文章来源地址https://www.toymoban.com/news/detail-861076.html

到了这里,关于day26-python操作MySQL和实战的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 【100天精通python】Day32:使用python操作数据库_MySQL下载、安装、配置、使用实战

    目录  专栏导读  1 MySQL概述 2 MySQL下载安装 2.1 下载  2.2 安装 2.3 配置

    2024年02月12日
    浏览(10)
  • Mysql进阶- InnoDB引擎架构

    Mysql进阶- InnoDB引擎架构

    InnoDB的逻辑存储结构如下图所示:  1). 表空间 是InnoDB存储引擎逻辑结构的最高层, 如果用户启用了参数      innodb_file_per_table(在 8.0版本中默认开启) ,则每张表都会有一个表空间(xxx.ibd),一个mysql实例可以对应多个表空间, 用于存储记录、索引等数据 。 2).   段 ,分为

    2024年02月04日
    浏览(11)
  • MySQL架构 & InnoDB存储引擎

    MySQL架构 & InnoDB存储引擎

    我们在开发的时候,我们都需要对业务数据进行存储,这个时候,你们就会用到MySQL 、O racal 等数据库。 MySQL它是一个关系型数据库, 这种关系型数据库就有Oracal 、 MySQL,以及最近很火的PgSQL 等。 那什么是关系型数据库呢? 就是它是 基于我们的SQL语句去执行操作的 。 其实

    2024年02月07日
    浏览(36)
  • MySQL—MySQL的存储引擎之InnoDB

    MySQL—MySQL的存储引擎之InnoDB

    存储引擎 说明 MyISAM 高速引擎,拥有较高的插入,查询速度,但不支持事务 InnoDB 5.5版本后MySQL的默认数据库存储引擎,支持事务和行级锁,比MyISAM处理速度稍慢 ISAM MyISAM的前身,MySQL5.0以后不再默认安装 MRG_MyISAM 将多个表联合成一个表使用,在超大规模数据存储时很有用 Me

    2024年04月26日
    浏览(11)
  • 谈谈MySQL的InnoDB存储引擎

    谈谈MySQL的InnoDB存储引擎

    大家好,我是易安! 今天我们谈一谈MySQL中InnoDB存储引擎。InnoDB存储引擎作为我们最常用到的存储引擎之一,充分熟悉它的的实现和运行原理,有助于我们更好地创建和维护数据库表。 InnoDB主要包括了内存池、后台线程以及存储文件。内存池又是由多个内存块组成的,主要包

    2024年02月03日
    浏览(11)
  • MySQL 的 InnoDB 存储引擎简介

    MySQL 的 InnoDB 存储引擎简介

    MySQL 是世界上最流行的开源关系型数据库管理系统之一,而其中的存储引擎则是其关键组成部分之一。InnoDB 存储引擎在 MySQL 中扮演了重要角色,提供了许多高级功能和性能优化,适用于各种应用程序和工作负载。本文将深入介绍 InnoDB 存储引擎的各个方面,以帮助您更好地理

    2024年02月08日
    浏览(16)
  • MySQL高级10-InnoDB引擎存储架构

    MySQL高级10-InnoDB引擎存储架构

    表空间(Tablespace):一个mysql实例,及一个数据库实例,可以对应多个表空间(ibd文件),用于存储记录,索引等数据。 段(Segment):分为数据段(Leaf node segment)、索引段(Non-leaf node segment)、回滚段(Rollback segment),InnoDB是索引组织表,数据段就是B+树的叶子节点(Leaf node segment),

    2024年02月08日
    浏览(13)
  • 【MYSQL】存储引擎MyISAM和InnoDB

    【MYSQL】存储引擎MyISAM和InnoDB

    查看MySQL提供所有的存储引擎 mysql show engines; mysql常用引擎包括:MYISAM、Innodb、Memory、MERGE 1、MYISAM:全表锁,拥有较高的执行速度,不支持事务,不支持外键,并发性能差,占用空间相对较小,对事务完整性没有要求,以select、insert为主的应用基本上可以使用这引擎 2、Innod

    2024年01月18日
    浏览(11)
  • 《MySQL系列-InnoDB引擎07》MySQL备份与恢复

    《MySQL系列-InnoDB引擎07》MySQL备份与恢复

    为保证在服务器宕机、磁盘损坏、RAID卡损坏等意外情况下,数据不丢失,或者最小程度的丢失,每个DBA应该时刻关注数据库备份情况。 MySQL数据库提供了大多数工具(如 mysqldump,ibbackup,replication )都能很好的完成备份工作,当然也可以通过第三方工具完成,如 xtrabacup,LVM快照备份

    2024年01月22日
    浏览(12)
  • ⑩⑧【MySQL】InnoDB架构、事务原理、MVCC多版本并发控制

    ⑩⑧【MySQL】InnoDB架构、事务原理、MVCC多版本并发控制

    个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~ InnoDB逻辑存储结构 : 🚀 表空间(idb文件) :一个MySQL实例可以对应多个表空间,用于存储记录、索引等数

    2024年02月04日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包