Python全栈(三)数据库优化之7.MySQL高级-事务、索引、账户管理和存储引擎介绍

一、事务

1.事务引出

事务广泛用于订单系统、银行系统等多种场景。
例如:
A用户和B用户是银行的储户,现在A要给B转账500元,那么需要做以下几件事:
检查A的账户余额>500元;
A账户中扣除500元;
B账户中增加500元。
正常的流程走下来,A账户扣了500,B账户加了500;
那如果A账户扣了钱之后,系统出故障了,A会白白损失了500,而B也没有收到本该属于他的500。
以上的案例中,隐藏着一个前提条件:A扣钱和B加钱,要么同时成功,要么同时失败。事务的需求就在于此。

2.事务定义

事务是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。
例如,银行转帐工作:从一个帐号扣款并使另一个帐号增款,这两个操作要么都执行,要么都不执行。所以,应该把他们看成一个事务。
事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
那么至少需要三个步骤:
(1)检查账户1的余额高于或者等于100美元;
(2)从账户1余额中减去100美元;
(3)在帐户2余额中增加100美元。

begin;
update bank1 set money = money - 100 where id = 1;

此时没有commit,查询时数据不会发生变化,即体现了事物的隔离性。

update bank2 set money = money + 100 where id = 1;
commit;

此时两个表中的数据均发生变化,一个减少100,一个增加100。
以上两步操作组成一个完整的事务。
开启事务后每次执行SQL语句后不会自动提交,要提交才能操作成功。

3.事务的四大特性(ACID)

(1)原子性(Atomicity):
一个事务被视为一个不可分割的最小工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务来说,不可能只执行其中的一部分操作,这就是事务的原子性。
(2)一致性(Consistency):
数据库总是从一个一致性的状态转换到另一个一致性的状态。
在前面的例子中,一致性确保了,即使在执行两条update语句之间时系统崩溃,银行账户中也不会损失100美元,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中。
(3)隔离性(Isolation):
通常来说,一个事务所作的修改在最终提交以前,对其他事务是不可见的。
在前面的例子中,当执行完第一update语句、第四条语句还未开始时,此时有另外的一个账户汇总程序开始运行,则其看到帐户1的余额并没有被减去100美元。
在一个命令行中,开启一个事务

begin;
update goods set name = 'palyer' where id = 3;

未提交
在另一个命令行中执行另一个操作

update goods set name = 'Player' where id = 3;

此时会出现堵塞,因为前一个命令行未提交事务,即前一个在操作的时候,其他对该数据库的操作会被锁定,从而保证了数据的一致性。
(4)持久性(Durability):
一旦事务提交,则其所做的修改会永久保存到数据库。
此时即使系统崩溃,修改的数据也不会丢失。

4.事务操作

表的引擎类型必须是innodb类型才可以使用事务,这是mysql表的默认引擎。
开启事务
语法:start transation;或者begin;
开启事务后执行修改命令,变更会维护到本地缓存中,而不维护到物理表中。
提交事务
语法:commit;
将缓存中的数据变更维护到物理表中。
回滚事务
语法:rollback;
放弃缓存中变更的数据。
练习
现在操作在一个事务中1给2转100,同时另一个事务中1给3转100.
数据如下

+----+-----+
| id | num |
+----+-----+
|  1 | 100 |
|  2 | 200 |
|  3 |   0 |
+----+-----+

操作过程如图
事务操作
事务1提交后,事务2对id1的更改会报错,因此需要回滚,从而保证了数据的一致性。
修改数据的命令会自动的触发事务,包括insert、update、delete;
如果对数据库的操作涉及多次数据的修改,并且成功一起成功,否则一起会滚到之前的数据,需要在SQL语句中有手动开启事务。

二、索引

1.引入

在图书馆中是如何找到一本书的?
一般的应用系统对比数据库的读写比例在10:1左右(即有10次查询操作时有1次写的操作),而且插入操作和更新操作很少出现性能问题,遇到最多、最容易出问题还是一些复杂的查询操作,所以查询语句的优化显然是重中之重。
当数据库中数据量很大时,查找数据会变得很慢,所以引入索引进行优化。

2.定义

索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的引用指针。
通俗地说,数据库索引就像一本书前面的目录,它们包含着对数据表里所有记录的引用指针。
索引的目的
索引的目的在于提高查询效率,可以类比字典,如果要查“mysql”这个单词,我们肯定需要定位到m字母,然后从下往下找到y字母,再找到剩下的sql。
索引原理
生活中随处可见索引的例子,如词典、火车站的车次表、图书的目录等。它们的原理都是一样的,通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。
数据库也是一样,但显然要复杂许多,因为不仅面临着等值查询,还有范围查询(>、<、between、in)、模糊查询(like)、并集查询(or)等等。
如下图
索引原理
索引使用的数据结构一般是B树
索引分为:

  • 单值索引:单个字段;
  • 复合索引:多个字段;
  • 唯一索引:列的值必须唯一,应用于身份证号、电话号等。

3.索引的使用

查看索引:
语法:show index from 表名;

show index from money;

打印

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| money |          0 | PRIMARY  |            1 | id          | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
1 row in set (0.01 sec)

主键会自动创建索引;
创建索引:
语法:create index 字段名称 on 表名(字段名称(长度));

create index idx_num on money(num);

再查看索引

+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| money |          0 | PRIMARY  |            1 | id          | A         |           3 |     NULL | NULL   |      | BTREE      |         |               |
| money |          1 | idx_num  |            1 | num         | A         |           2 |     NULL | NULL   |      | BTREE      |         |               |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
2 rows in set (0.00 sec)

如果指定字段是字符串,需要指定长度,建议长度与定义字段时的长度一致;
字段类型如果不是字符串,可以不填写长度部分。
创建唯一索引:

create unique index idx_num on money(num);

删除索引:
语法:drop index 索引名称 on 表名;

4.索引练习

创建测试表test:

create table test(title varchar(10));

使用python程序向表中加入十万条数据:

from pymysql import *
def main():
    conn = connect(
        host='127.0.0.1',
        port=3306,
        db='demo',
        user='root',
        passwd='root',
        charset='utf8')
    cur = conn.cursor()
    for i in range(100000):
        cur.execute('insert into test values("ts-%d")' % i)
    conn.commit()

if __name__ == '__main__':
    main()

开启运行时间监测:

set profiling=1;

查找第100000条数据:

select * from test where title='ts-99999';

打印

+----------+
| title    |
+----------+
| ts-99999 |
+----------+
1 row in set (0.04 sec)

查看执行的时间:

show profiles;

打印

+----------+------------+-------------------------------------------+
| Query_ID | Duration   | Query                                     |
+----------+------------+-------------------------------------------+
|        1 | 0.03794450 | select * from test where title='ts-99999' |
+----------+------------+-------------------------------------------+
1 row in set, 1 warning (0.00 sec)

为表title_index的title列创建索引:

create index title_index on test(title(10));

再次执行查询语句:

select * from test where title='ts-99999';

打印

+----------+
| title    |
+----------+
| ts-99999 |
+----------+
1 row in set (0.00 sec)

再次查看执行的时间:

show profiles;

打印

+----------+------------+---------------------------------------------+
| Query_ID | Duration   | Query                                       |
+----------+------------+---------------------------------------------+
|        1 | 0.03794450 | select * from test where title='ts-99999'   |
|        2 | 0.26895350 | create index title_index on test(title(10)) |
|        3 | 0.00031875 | select * from test where title='ts-99999'   |
+----------+------------+---------------------------------------------+
3 rows in set, 1 warning (0.00 sec)

显然,建立了索引后查询速度有了很大提升,即建立索引可以大幅提高查询效率。
适合建立索引的情况:

  • 主键自动建立索引;
  • 频繁作为查询条件的字段应该建立索引;
  • 查询中与其他表关联的字段,外键关系建立索引;
  • 在高并发的情况下创建复合索引;
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度 (建立索引的顺序跟排序的顺序保持一致)。

不适合建立索引的情况:

  • 频繁更新的字段不适合建立索引;
  • where条件里面用不到的字段不创建索引;
  • 表记录太少,当表中数据量超过三百万条数据,可以考虑建立索引;
  • 数据重复且平均的表字段,比如性别,国籍。

三、账户管理

在生产环境下操作数据库时,绝对不可以使用root账户连接,而是创建特定的账户,授予这个账户特定的操作权限,然后连接进行操作,主要的操作就是数据的crud
MySQL账户体系:
根据账户所具有的权限的不同,MySQL的账户可以分为以下几种:

  • 服务实例级账号:启动了一个mysqld,即为一个数据库实例;如果某用户如root,拥有服务实例级分配的权限,那么该账号就可以删除所有的数据库、连同这些库中的表;
  • 数据库级别账号:对特定数据库执行增删改查的所有操作;
  • 数据表级别账号:对特定表执行增删改查等所有操作;
  • 字段级别的权限:对某些表的特定字段进行操作;
  • 存储程序级别的账号:对存储程序进行增删改查的操作。
    账户的操作主要包括创建账户、删除账户、修改密码、授权权限等。

授予权限:

需要使用实例级账户登录后操作,以root为例
主要操作包括:

(1)查看所有用户:

所有用户及权限信息存储在mysql数据库的user表中;
查看user表的结构:

use mysql;
desc user;

打印

+------------------------+-----------------------------------+------+-----+-----------------------+-------+   
| Field                  | Type                              | Null | Key | Default               | Extra |   
+------------------------+-----------------------------------+------+-----+-----------------------+-------+   
| Host                   | char(60)                          | NO   | PRI |                       |       |   
| User                   | char(32)                          | NO   | PRI |                       |       |   
| Select_priv            | enum('N','Y')                     | NO   |     | N                     |       |   
| Insert_priv            | enum('N','Y')                     | NO   |     | N                     |       |   
| Update_priv            | enum('N','Y')                     | NO   |     | N                     |       |   
| Delete_priv            | enum('N','Y')                     | NO   |     | N                     |       |   
| Create_priv            | enum('N','Y')                     | NO   |     | N                     |       |   
| Drop_priv              | enum('N','Y')                     | NO   |     | N                     |       |   
| Reload_priv            | enum('N','Y')                     | NO   |     | N                     |       |   
| Shutdown_priv          | enum('N','Y')                     | NO   |     | N                     |       |   
| Process_priv           | enum('N','Y')                     | NO   |     | N                     |       |   
| File_priv              | enum('N','Y')                     | NO   |     | N                     |       |   
| Grant_priv             | enum('N','Y')                     | NO   |     | N                     |       |   
| References_priv        | enum('N','Y')                     | NO   |     | N                     |       |   
| Index_priv             | enum('N','Y')                     | NO   |     | N                     |       |   
| Alter_priv             | enum('N','Y')                     | NO   |     | N                     |       |   
| Show_db_priv           | enum('N','Y')                     | NO   |     | N                     |       |   
| Super_priv             | enum('N','Y')                     | NO   |     | N                     |       |   
| Create_tmp_table_priv  | enum('N','Y')                     | NO   |     | N                     |       |   
| Lock_tables_priv       | enum('N','Y')                     | NO   |     | N                     |       |   
| Execute_priv           | enum('N','Y')                     | NO   |     | N                     |       |   
| Repl_slave_priv        | enum('N','Y')                     | NO   |     | N                     |       |   
| Repl_client_priv       | enum('N','Y')                     | NO   |     | N                     |       |   
| Create_view_priv       | enum('N','Y')                     | NO   |     | N                     |       |   
| Show_view_priv         | enum('N','Y')                     | NO   |     | N                     |       |   
| Create_routine_priv    | enum('N','Y')                     | NO   |     | N                     |       |   
| Alter_routine_priv     | enum('N','Y')                     | NO   |     | N                     |       |   
| Create_user_priv       | enum('N','Y')                     | NO   |     | N                     |       |   
| Event_priv             | enum('N','Y')                     | NO   |     | N                     |       |   
| Trigger_priv           | enum('N','Y')                     | NO   |     | N                     |       |   
| Create_tablespace_priv | enum('N','Y')                     | NO   |     | N                     |       |   
| ssl_type               | enum('','ANY','X509','SPECIFIED') | NO   |     |                       |       |   
| ssl_cipher             | blob                              | NO   |     | NULL                  |       |   
| x509_issuer            | blob                              | NO   |     | NULL                  |       |   
| x509_subject           | blob                              | NO   |     | NULL                  |       |   
| max_questions          | int(11) unsigned                  | NO   |     | 0                     |       |   
| max_updates            | int(11) unsigned                  | NO   |     | 0                     |       |   
| max_connections        | int(11) unsigned                  | NO   |     | 0                     |       |   
| max_user_connections   | int(11) unsigned                  | NO   |     | 0                     |       |   
| plugin                 | char(64)                          | NO   |     | mysql_native_password |       |   
| authentication_string  | text                              | YES  |     | NULL                  |       |   
| password_expired       | enum('N','Y')                     | NO   |     | N                     |       |   
| password_last_changed  | timestamp                         | YES  |     | NULL                  |       |   
| password_lifetime      | smallint(5) unsigned              | YES  |     | NULL                  |       |   
| account_locked         | enum('N','Y')                     | NO   |     | N                     |       |   
+------------------------+-----------------------------------+------+-----+-----------------------+-------+   
45 rows in set (0.00 sec)                                                                                     

主要字段说明:
Host表示允许访问的主机;
User表示用户名;
authentication_string表示密码,为加密后的值。
查看所有用户:

select host,user,authentication_string from user;
+-----------+---------------+-------------------------------------------+
| host      | user          | authentication_string                     |
+-----------+---------------+-------------------------------------------+
| localhost | root          | *81F5E21E35407D884A6CD4A731AEBFB6AF209E1B |
| localhost | mysql.session | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| localhost | mysql.sys     | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
| 127.0.0.1 | Tom           | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
| localhost | Tom2          | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 |
+-----------+---------------+-------------------------------------------+
5 rows in set (0.00 sec)

(2)创建账户、授权

需要使用实例级账户登录后操作,以root为例
常用权限主要包括:create、alter、drop、insert、update、delete、select
如果分配所有权限,可以使用all privileges
语法:
grant 权限列表 on 数据库 to '用户名'@'访问主机' identified by '密码';

grant select on jingdong.* to 'Leo'@'localhost' identified by '123456';
--创建一个Mary的账号,密码为12345678,可以任意电脑进行链接访问, 并且对jd数据库中的所有表拥有所有权限
grant all privileges on jd.* to "Mary"@"%" identified by "12345678"
--刷新权限
flush privileges;

说明:
可以操作数据库的所有表,方式为:jingdong.*;
访问主机通常使用 百分号% 表示此账户可以使用任何ip的主机登录访问此数据库;
访问主机可以设置成 localhost或具体的ip,表示只允许本机或特定主机访问。
查看用户有哪些权限:

show grants for Leo@localhost;

打印

+---------------------------------------------------+
| Grants for Leo@localhost                          |
+---------------------------------------------------+
| GRANT USAGE ON *.* TO 'Leo'@'localhost'           |
| GRANT SELECT ON `jingdong`.* TO 'Leo'@'localhost' |
+---------------------------------------------------+
2 rows in set (0.00 sec)                           

使用新创建的用户登录:

mysql -uLeo -p

查看数据库:

show databases;

打印

+--------------------+
| Database           |
+--------------------+
| information_schema |
| jingdong           |
+--------------------+
2 rows in set (0.00 sec)

进行删除操作

use jingdong;
drop table goods;

打印

ERROR 1142 (42000): DROP command denied to user 'Leo'@'localhost' for table 'goods'

报错,命令拒绝,因为只给Leo了查看权限,所以不能进行增加、修改、删除操作。

四、数据库存储引擎简介

1.MySQL体系结构:

  • 连接层
  • 服务层(核心)
  • 存储引擎层
  • 文件系统

如图:
MySQL体系结构
服务层
服务层是MySQL的核心,MySQL的核心服务层都在这一层,查询解析、SQL执行计划分析、SQL执行计划优化、查询缓存、以及跨存储引擎的功能都在这一层实现:存储过程,触发器,视图等。
下图是服务层的内部结构:
在这里插入图片描述
存储引擎层
负责MySQL中数据的存储与提取。
服务器中的查询执行引擎通过API与存储引擎进行通信,通过接口屏蔽了不同存储引擎之间的差异。
MySQL采用插件式的存储引擎。MySQL为我们提供了许多存储引擎,每种存储引擎有不同的特点。我们可以根据不同的业务特点,选择最适合的存储引擎。
如果对于存储引擎的性能不满意,可以通过修改源码来得到自己想要达到的性能。例如阿里巴巴的X-Engine,为了满足企业的需求facebook与google都对InnoDB存储引擎进行了扩充。

2.存储引擎操作

查看存储引擎:

show engines;

打印

+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
9 rows in set (0.01 sec)                                                                                            

查看默认的存储引擎:

show variables like '%storage_engines%';

打印

+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| disabled_storage_engines |       |
+--------------------------+-------+
1 row in set, 1 warning (0.00 sec)

小编那么拼,赞一个再撤!
欢迎大家加入群聊【python知识交流分享群】,进行技术交流:https://jq.qq.com/?_wv=1027&k=5m5AduZ
在这里插入图片描述

发布了51 篇原创文章 · 获赞 184 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/CUFEECR/article/details/103484221