软件开发架构师

Python MySQL事务、引擎、索引及第三方库sqlalchemy

架构 133 2020-05-19 09:55

本节内容

1、数据库介绍
2、事务
3、引擎
4、索引
5、ORM sqlalchemy

1、数据库介绍

什么是数据库?

  数据库(Database)是按照数据结构来组织、存储和管理数据的仓库,每个数据库都有一个或多个不同的API用于创建,访问,管理,搜索和复制所保存的数据。我们也可以将数据存储在文件中,但是在文件中读写数据速度相对较慢。所以,现在我们使用关系型数据库管理系统(RDBMS)来存储和管理的大数据量。所谓的关系型数据库,是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。RDBMS即关系数据库管理系统(Relational Database Management System)的特点:
  1.数据以表格的形式出现
  2.每行为各种记录名称
  3.每列为记录名称所对应的数据域
  4.许多的行和列组成一张表单
  5.若干的表单组成database

术语

  在我们开始学习MySQL 数据库前,让我们先了解下RDBMS的一些术语:

  • 数据库:数据库是一些关联表的集合。
  • 数据表:表是数据的矩阵。在一个数据库中的表看起来像一个简单的电子表格。
  • 列:一列(数据元素) 包含了相同的数据, 例如邮政编码的数据。
  • 行:一行(=元组,或记录)是一组相关的数据,例如一条用户订阅的数据。
  • 冗余:存储两倍数据,冗余可以使系统速度更快。(表的规范化程度越高,表与表之间的关系就越多;查询时可能经常需要在多个表之间进行连接查询;而进行连接操作会降低查询速度。例如,学生的信息存储在student表中,院系信息存储在department表中。通过student表中的dept_id字段与department表建立关联关系。如果要查询一个学生所在系的名称,必须从student表中查找学生所在院系的编号(dept_id),然后根据这个编号去department查找系的名称。如果经常需要进行这个操作时,连接查询会浪费很多的时间。因此可以在student表中增加一个冗余字段dept_name,该字段用来存储学生所在院系的名称。这样就不用每次都进行连接操作了。)
  • 主键:主键是唯一的。一个数据表中只能包含一个主键。你可以使用主键来查询数据。
  • 外键:外键用于关联两个表。
  • 复合键:复合键(组合键)将多个列作为一个索引键,一般用于复合索引。
  • 索引:使用索引可快速访问数据库表中的特定信息。索引是对数据库表中一列或多列的值进行排序的一种结构。类似于书籍的目录。
  • 参照完整性:参照的完整性要求关系中不允许引用不存在的实体。与实体完整性是关系模型必须满足的完整性约束条件,目的是保证数据的一致性。

MySQL数据库

  Mysql是最流行的关系型数据库管理系统,在WEB应用方面MySQL是最好的RDBMS(Relational Database Management System:关系数据库管理系统)应用软件之一。由瑞典MySQL AB公司开发,目前属于Oracle公司。MySQL是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。

  • Mysql是开源的,所以你不需要支付额外的费用。
  • Mysql支持大型的数据库。可以处理拥有上千万条记录的大型数据库。
  • MySQL使用标准的SQL数据语言形式。
  • Mysql可以允许于多个系统上,并且支持多种语言。这些编程语言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。
  • Mysql对PHP有很好的支持,PHP是目前最流行的Web开发语言。
  • MySQL支持大型数据库,支持5000万条记录的数据仓库,32位系统表文件最大可支持4GB,64位系统支持最大的表文件为8TB。
  • Mysql是可以定制的,采用了GPL协议,你可以修改源码来开发自己的Mysql系统。

2、事务

  MySQL 事务主要用于处理操作量大,复杂度高的数据。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!

  • 在MySQL中只有使用了Innodb数据库引擎的数据库或表才支持事务;
  • 事务处理可以用来维护数据库的完整性,保证成批的SQL语句要么全部执行,要么全部不执行;
  • 事务用来管理insert,update,delete语句。

一般来说,事务满足4个特性(ACID): Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性),最重要的特性是原子性和一致性。
      1、原子性:事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。
      2、一致性:事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
      3、隔离性:当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
      4、持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

在MySQL中,默认自动向数据库提交结果,用户在执行数据库操作过程中,不需要使用START TRANSACTION语句开始事务,不需要应用COMMIT或者ROLLBACK提交事务或事务回滚。

关闭自动提交:SET AUTOCOMMIT=0;  # 此后需要手动提交事务,应用COMMIT或ROLLBACK提交事务或回滚事务

开启自动提交:SET AUTOCOMMIT=1;

如果执行语句:START TRANSACTION(开始一个事务); 那么不论有无设置自动提交,均需要手动提交或回滚。

事务的周期由用户在命令提示符中输入START TRANSACTION指令开始,直至用户输入COMMIT结束。

3、引擎

MyISAM

  ISAM是一个定义明确且历经时间考验的数据表格管理方法,它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。因此,ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM的两个主要不足之处在于,它不支持事务处理,也不能够容错:如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把ISAM用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,MySQL能够支持这样的备份应用程序。

InnoDB

  它提供了事务控制能力功能,它确保一组命令全部执行成功,或者当任何一个命令出现错误时所有命令的结果都被回退,可以想像在电子银行中事务控制能力是非常重要的。支持COMMIT、ROLLBACK和其他事务特性。最新版本的Mysql已经计划移除对BDB的支持,转而全力发展InnoDB。

两种引擎的区别

  • InnoDB不支持FULLTEXT类型的索引;
  • InnoDB中不保存表的具体行数,也就是说,执行 select count(*) from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时,两种表的操作是一样的;
  • 对于 auto_increment 类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引;
  • delete from table时,InnoDB不会重新建立表,而是一行一行的删除;
  • load table from master 操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用;
  • 另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “�a%”;
  • MyISAM类型的表强调的是性能,其执行数度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持、外键等高级数据库功能;
  • MyISAM类型的二进制数据文件可以在不同操作系统中迁移。也就是可以直接从Windows系统拷贝到linux系统中使用。

引擎查看与修改

    1 .查看系统支持的存储引擎
        show engines;
    2 .查看表使用的存储引擎
        show create table table_name;

    3 修改表引擎方法
       alter table table_name engine=innodb;

4、索引

  MySQL索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。

  打个比方,如果合理的设计且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车。

  索引分单列索引和组合索引。

  单列索引,即一个索引只包含单个列,一个表可以有多个单列索引,但这不是组合索引。组合索引,即一个索引包含多个列,组合索引为“最左前缀”的结果,简单的理解就是只从最左面的开始组合。

  创建索引时,你需要确保该索引是应用在 SQL 查询语句的条件(一般作为 where 子句的条件)。

  实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录。

  上面都在说使用索引的好处,但过多的使用索引将会造成滥用。因此索引也会有它的缺点:

  1)虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不仅要保存数据,还要保存一下索引文件。

  2)建立索引会占用磁盘空间的索引文件。

普通索引

1.创建索引(与创建主键类似,主键是特殊的索引)

  这是最基本的索引,它没有任何限制。它有以下几种创建方式:

## 直接创建
create index ind_name on mytable (field_name);
## 修改表结构
alter table mytable add index [ind_name] (field_name); ## 建表时指定
create table mytable(      ID int not null,   username varchar(16) not null,   index [indexName] (username) );

 2.删除索引

drop index [ind_name] on mytable;

alter table mytable drop index [ind_name];

唯一索引

  它与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。它有以下几种创建方式:

1.创建索引

##创建索引
create unique index ind_name on mytable (field_name); 
 
##修改表结构
alter table mytable add unique [ind_name] (field_name); 
 
##建表时指定
create table mytable(   
  ID int not null,    
  username varchar(16) not null,   
  unique [ind_name] (username)   
);

 使用索引

  将索引名(ind_name)对应的字段名(field_name)作为查询条件查询。

 5、ORM:sqlalchemy模块

ORM介绍

  orm英文全称“object relational mapping”,即对象关系映射,简单来说我们类似python这种面向对象的程序来说一切皆对象,但是我们使用的数据库却都是关系型的,为了保证一致的使用习惯,通过orm将编程语言的对象模型和数据库的关系模型建立映射关系,这样我们在使用编程语言对数据库进行操作的时候可以直接使用编程语言的对象模型进行操作就可以了,而不用直接使用sql语言。

ORM优点

  1. 隐藏了数据访问细节,“封闭”的通用数据库交互,ORM的核心。他使得我们的通用数据库交互变得简单易行,并且完全不用考虑该死的SQL语句。快速开发,由此而来。
  2. ORM使我们构造固化数据结构变得简单易行。

sqlalchemy基本使用

    from sqlalchemy import *
    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy.orm import sessionmaker        

    ### 1.创建引擎连接数据库,可指定字符集。这里连接库为test,echo参数为True时,会显示每条执行的SQL语句,生产环境下可关闭
    db = sqlalchemy.create_engine('mysql+pymysql://username:pwd@localhost:3306/test?charset=utf8', echo=False)        
    
    ### 2.声明ORM基类
    Base = declarative_base()        # 工厂方法
    ### 3.建模型 (表结构映射)
    class Department(Base):
        __tablename__ = 'department'      # 表名
        
        RowID = Column(Integer, primary_key=True, autoincrement=True)        # 自增主键字段
        Dep_Code = Column(String(3), nullable=False, unique=True, index=True)
        Dep_Name = Column(String(20), nullable=True, default='--')
        FK_id = Column(Integer, ForeignKey('table.field'))                    # 外键
        
        __table_args__ = (
            UniqueConstraint('RowID', 'Dep_Code'),        # 联合唯一约束
            Index('Dep_Code', 'Dep_Name'),                # 联合索引
        )
    
    ### 4.创建表结构 (后台会自动建立一张表)
    Base.metadata.create_all(db)
    
    ### 建立会话,类似游标
    Session = sessionmaker(bind=db)        # 工厂方法
    session = Session()

    ## 插入记录,使用关系映射类
    dept_obj = Department(Dep_Code='004', Dep_Name='人事部')
    session.add(dept_obj)
    session.commit()

    ## 查询记录,默认查出所有结果
    data = session.query(Department).all()[:2]        # 结果分片    
    data = session.query(Department).get(Department.RowID=1)        # 只能返回一条结果,否则会异常        
    data = session.query(Department).filter(Department.RowID>1).offset(2).limit(1).all()    # 偏移2个位置,返回1条结果。limit()和offset)()常用于分页
    data = session.query(Department.Dep_Code, Department.Dep_Name).filter(Department.RowID<3).order_by(Department.RowID.desc()).all()    # 返回指定字段,倒序结果        
    data = session.query(Department).filter(text("RowID<:rid and Dep_Name=:name")).params(rid=3, name='WcwNina').all()    # 占位符设置变量参数    
    data = session.query(Department).filter(Department.Dep_Code=='003').filter(Department.Dep_Name=='软件部').all()         # 多条件查询,类似"and"。注意filter参数是bool型!        
    data = session.query(Department).filter(Department.RowID.between(1,3), Department.Dep_Name.like('%部')).all()         # "between"限定字段范围,"like"模糊查询        
    data = session.query(Department).filter(Department.RowID.in_([1,2,3])).all()                # "in_"范围查询        
    data = session.query(Department).filter(~Department.RowID.in_([1,2,3])).all()                # "~"排除范围,类似"!="        
    data = session.query(Department).filter(and_(Department.RowID<3, Department.Dep_Code!='003')).all()    # "and_",and查询        
    data = session.query(Department).filter(or_(Department.RowID<3, Department.Dep_Code=='003')).all()    # "or_",or查询        
    data = session.query(Users, Favor).filter(Users.id == Favor.nid).all()                        # 联表(内联)查询        
    data = session.query(Users).join(Favor, [on '外键条件']).filter(Users.id>1).all()            # "join"外键联表查询,默认内联查询
    data = session.query(Users).outerjoin(Favor, [on '外键条件']).filter(Users.id>1).all()        # "outerjoin"外键联表查询,外联查询
    
    ## 更新记录,查出记录后更新
    data = session.query(Department).filter(Department.Dep_Code=='003').first().update({Dep_Name: '软件部'})    # first()返回第一条结果
    session.commit()

    ## 统计
    counts = session.query(Department).filter(Department.Dep_Name.like('%部')).count()        # count()函数

    ## 分组
    from sqlalchemy import func            # func模块提供了一些常用函数
    session.query(func.count(Department.Dep_Name),func.max(RowID), Department.Dep_Name).group_by(Department.Dep_Name).all()
    
    ## 回滚
    session.rollback()
    data = session.query(Department).filter(Department.Dep_Code.in_(['004']))
View Code

   

  至此,转载请注明出处。

 <a id=Python MySQL事务、引擎、索引及第三方库sqlalchemy-4" alt="">

文章评论