士搏出惊涛骇流而不沉沦,懦夫在风平浪静也会溺水。
oslo.db库为OpenStack其他组件提供了针对不同后端数据库的数据库连接,并提供了各种数据库操作的辅助工具类和方法。OpenStack各组件使用SQLAlchemy框架实现对数据库的连接、查询等操作,因此oslo.db并不是一个完整的ORM库,也没有封装执行SQL语句;其只是对SQLAlchemy进行了封装,其与SQLAlchemy结合使用使得在OpenStack各组件中实现数据库连接、查询等操作变得更加简单。因此,本文不对SQLAlchemy的使用进行详细介绍,如果需要了解SQLAlchemy的使用,可以参考python对Mysql操作和使用ORM框架(SQLAlchemy)和SQLAlchemy技术文档。本文将结合oslo.db的使用详细分析其实现方式。
Session Handling
使用方法
Session handling指的是使用oslo_db.sqlalchemy.enginefacade模块管理数据库连接、会话和事务处理等,该模块一般以装饰器形式使用,也可以使用with语句进行调用。
Session handling可以为一个函数提供装饰器,也可以作为一个上下文管理器传递一个Session或Connection对象。这两种方式都需要用到一个上下文对象。这个对象可以是任何一个类实例。下面这个例子便是使用上下文管理器形式的Session handling的使用方式:
1 | from oslo_db.sqlalchemy import enginefacade |
在这个例子中,首先定义了一个MyContext作为上下文管理器,然后分别在some_reader_api_function(context)和some_writer_api_function(context, x, y)来进行一些数据库操作。在some_reader_api_function(context)中,通过enginefacade.reader使用定义的上下文管理器生成一个数据库连接Session对象,然后使用Session对象的query()方法进行查询偶作;而在some_writer_api_function(context, x, y)中,则通过enginefacade.writer使用定义的上下文管理器生成一个数据库连接Session对象,然后使用这个Session对象的add()方法进行插入操作。其中,使用了enginefacade模块中的reader和writer两个重要的变量,这两个变量分别表示一个reader和writer函数的全局调用入口,关于reader和writer会在之后进行详细分析。除了这种方式之外,还可以使用装饰器模式使用enginefacade模块中的装饰器方法,使用方法如下所示:
1 | from oslo_db.sqlalchemy import enginefacade |
首先,在定义MyContext类时,使用了@enginefacade.transaction_context_provider装饰器为其设置Session等属性,该装饰器可用于设置Session、Connection、Transaction等属性。在使用时,则可以直接使用@enginefacade.writer和@enginefacade.reader从MyContext对象中获取对应的session,并用其进行数据库操作。
1 |
|
当不需要一个具体的数据库Session对象时,可以直接使用@enginefacade.reader.connection获取一个临时的数据库连接进行数据库相关操作。使用方式是使用@engingefacade.reader.connection装饰器,为上下文对象context添加一个数据库连接对象connection,直接使用该对象进行数据库操作即可。
需要注意的是,无论是context.session还是context.connection,都需要在一个writer或reader声明的对象中。否则,使用时将抛出异常。
当然,装饰器也可以用在一个类或对象的方法中,使用方式如下:
1 | class DatabaseAccessLayer(object): |
此时,需要注意的是,enginefacade的装饰器必须在classmethod装饰器之前使用,否则会抛出TypeError异常。
实现原理
在1.1节中详细介绍了Session Handling,即enginefacade的几种使用场景和使用方法,本节将据此分析enginefacade中的几个重要的类和方法,以及其实现原理。
_TransactionFactory类
_TransactionFactory
类是_TransactionContext
对象的一个工厂类,_TransactionContext
对象可以使用该类生成一个数据库Session或一个数据库连接Connection对象;而_TransactionFactory
对象会通过读取配置文件中数据库的配置信息创建相应的Session和Connection对象以进行数据库的相关操作。
_TransactionContext类
_TransactionContext
类的每一个实例化对象都代表了单个数据库事务,其中,定义了factory和global_factory两个_TransactionFactory
对象属性,factory用来为每一个_TransactionContext
对象创建数据库连接或Session,而global_factory会被一个全局的_context_manager
在创建一个新的_TransactionContext
对象时使用。在_TransactionContext
类中还定义了session、connection、transaction来分别表示一个数据库session、连接、事务对象,用于进行具体的数据库操作。最后,_TransactionContext
类还定义了一个mode属性来表示数据库操作的状态。在oslo_db的enginefacade中定义了3种数据库操作状态:
_TransactionContextManager类
_TransactionContextManager
类为数据库事务处理提供了上下文管理器context-management和装饰器。_TransactionContextManager
对象将_TransactionFactory
对象和用户定义的_TransactionContext
上下文对象集成到一起,以完成对数据库的事务处理等操作。1.1节中提到的writer和reader都是一个全局的_TransactionContextManager
对象_context_manager
的两个不同的方法,分别用来产生一个用于写操作或读操作的_TransactionContextManager
对象。
ModelBase
ModelBase是oslo.db为OpenStack各个组件封装的Model的基类。通常,在将一个数据库表抽象为一个类时,定义的类会继承ModelBase类。如Nova中为抽象所有数据库表而定义的基类:
1 | from oslo_db.sqlalchemy import models |
NovaBase类便继承了oslo.db中的ModelBase和TimestampMixin类。ModelBase的使用就是这么简单。接下来,将通过ModelBase的实现分析该类的主要作用。与ModelBase相关的类与方法都定义在oslo_db.sqlalchemy.models模块下,该模块中主要定义了一下几个类:
- ModelBase类:该类作为所有数据库表抽象的基类,提供了save、get、update、keys、items等常用的数据库操作方法,程序员在为数据库表定义数据结构时,都需要继承该类,然后使用或覆写这些方法完成对一个数据库表的所有CURD操作。
- ModelIterator类:该类主要用于对数据库表中数据进行迭代查询的操作。
- TimestampMixin类:由于OpenStack多数数据库表中都定义了创建时间和更新时间字段,因此该类专门用来获取记录的创建时间和更新时间,如果为空则可以自动设置为当前时间。
- SoftDeleteMixin类:OpenStack多数数据库表都为定义了删除记录标志和删除记录时间,该类则是专门用来获取记录的删除记录标志和删除时间。
由此可以看出,oslo_db.sqlalchemy.models模块为OpenStack其他组件进行数据库操作和定义创建一个一组模板类,可以在具体实现数据库操作时很好的封装数据库表的数据结构。
DB API backup support
OpenStack项目众多,为了使OpenStack项目可以使用统一的数据库后端API支持,oslo.db为OpenStack其他项目提供了统一的数据库后端操作API支持。oslo.db定义了DBAPI类统一管理OpenStack其他组件数据库后端操作的API,该类在初始化是需要指定backend_name和backend_mapping属性,在使用时,首先调用该类的类方法from_config()方法创建DBAPI对象,接着调用_load_backend()方法,通过oslo.utils中定义的导入模块的方法将对应数据库操作的API导入,然后就可以调用相应的API进行数据库操作了。具体使用方法如下:
1 | from oslo_config import cfg |
oslo.db除了定义DBAPI统一管理数据库后端API,还定义了多个装饰器辅助进行数据库操作,如@wrap_db_retry可以捕获数据库操作过程中抛出的异常,然后重连数据库等。其使用方法如下:
1 |
|
上述示例中表示如果更新service_group表操作抛出异常则重试该操作,最大重试次数为5次,设置retry_on_deadlock为True,表示如果操作中发生死锁也会进行重试操作。
DB migration extensions
oslo.db中还定义了与数据库相关的扩展操作,这些操作主要定义在oslo_db.sqlalchemy.migration模块中。其中,主要定义了db_version_control(engine, abs_path, version=None)方法进行版本控制,定义了db_sync(engine, abs_path, version=None, init_version=0, sanity_check=True)方法进行数据库的数据同步。
原文链接:https://blog.csdn.net/Bill_Xiang_/article/details/78592389