MySQL数据库(3)

本文涉及的产品
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: MySQL数据库(3)

表操作

一般情况下,我们针对只要通过索引列去搜搜都可以 命中 索引(通过索引结构加速查找)。

但是,还是会有一些特殊的情况,让我们无法命中索引(即使创建了索引),这也是需要大家在开发中要注意的。

类型不一致

select * from big where name = 123;     -- 未命中
select * from big where email = 123;    -- 未命中
特殊的主键:
    select * from big where id = "123"; -- 命中

使用不等于

select * from big where name != "xx";              -- 未命中
select * from big where email != "wupeiqi@live.com";  -- 未命中
特殊的主键:
    select * from big where id != 123;  -- 命中

or,当or条件中有未建立索引的列才失效。

select * from big where id = 123 or password="xx";          -- 未命中
select * from big where name = "wupeiqi" or password="xx";  -- 未命中
特别的:
    select * from big where id = 10 or password="xx" and name="xx"; -- 命中

排序,当根据索引排序时候,选择的映射如果不是索引,则不走索引。

select * from big order by name asc;     -- 未命中
select * from big order by name desc;    -- 未命中
特别的主键:
    select * from big order by id desc;  -- 命中

like,模糊匹配时。

select * from big where name like "%u-12-19999";    -- 未命中
select * from big where name like "_u-12-19999";    -- 未命中
select * from big where name like "wu-%-10";        -- 未命中
特别的:
    select * from big where name like "wu-1111-%";  -- 命中
    select * from big where name like "wuw-%";      -- 命中

使用函数

select * from big where reverse(name) = "wupeiqi";  -- 未命中
特别的:
    select * from big where name = reverse("wupeiqi");  -- 命中

最左前缀,如果是联合索引,要遵循最左前缀原则。

如果联合索引为:(name,password)
    name and password       -- 命中
    name                    -- 命中
    password                -- 未命中
    name or password        -- 未命中

事务

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


例如:xx给oo 转账 100,那就会涉及2个步骤。


xx账户 减100


oo账户 加 100


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


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


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

原子性(Atomicity)


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

一致性(Consistency)


执行的前后数据的完整性保持一致。

隔离性(Isolation)


一个事务执行的过程中,不应该受到其他事务的干扰。

持久性(Durability)


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

begin;
update users set amount=amount-2 where id=1;
update users set amount=amount+2 where id=2;
commit;
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()

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


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


表级锁,即A操作表时,其他人对整个表都不能操作,等待A操作完之后,才能继续。


行级锁,即A操作表时,其他人对指定的行数据不能操作,其他行可以操作,等待A操作完之后,才能继续。


MYISAM支持表锁,不支持行锁;

InnoDB引擎支持行锁和表锁。


即:在MYISAM下如果要加锁,无论怎么加都会是表锁。

   在InnoDB引擎支持下如果是基于索引查询的数据则是行级锁,否则就是表锁。


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


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

而select则默认不会申请锁。

select * from xxx;

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;

排它锁

排它锁( 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;
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()

数据库连接池

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()

推荐使用上下文管理方式:

# 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)
相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
Ubuntu 关系型数据库 MySQL
【MySQL】Navicat/SQLyog连接Ubuntu中的数据库(MySQL)
【MySQL】Navicat/SQLyog连接Ubuntu中的数据库(MySQL)
|
8月前
|
存储 关系型数据库 MySQL
第02章 MySQL的数据目录【1.MySQL架构篇】【MySQL高级】
第02章 MySQL的数据目录【1.MySQL架构篇】【MySQL高级】
511 0
|
7月前
|
SQL 关系型数据库 MySQL
【MySQL】 MySQL数据库基础
【MySQL】 MySQL数据库基础
|
5月前
|
存储 关系型数据库 MySQL
MySQL数据库的使用
MySQL数据库的使用
22 0
|
8月前
|
SQL 存储 关系型数据库
MYSQL数据库2
MYSQL数据库2
112 0
|
8月前
|
SQL 关系型数据库 MySQL
MYSQL数据库3
MYSQL数据库3
36 0
|
3月前
|
关系型数据库 MySQL 数据库
【MySQL数据库】初识MySQL数据库、安装MySQL
【MySQL数据库】初识MySQL数据库、安装MySQL
【MySQL数据库】初识MySQL数据库、安装MySQL
|
4月前
|
关系型数据库 MySQL PHP
MySQL 创建数据库
MySQL 创建数据库
|
4月前
|
关系型数据库 MySQL
Mysql的安装与配置
Mysql的安装与配置
39 2
|
4月前
|
关系型数据库 MySQL 数据库
零基础带你学习MySQL—创建数据库(一)
零基础带你学习MySQL—创建数据库(一)