音效素材网提供各类素材,打造精品素材网站!

站内导航 站长工具 投稿中心 手机访问

音效素材

Openstack 使用migrate进行数据库升级实现方案详细介绍
日期:2016-12-04 15:17:27   来源:脚本之家

Openstack 使用migrate进行数据库升级实现方案详细介绍

OpenStack中随着版本的切换,新版本加入一些数据库表或者增加字段等是必然的事情,如何比较容易的进行这些数据库升级的适配和管理,这里就要用到oslo_db中的migrate了,这里以为M版本的heat为例,讲解一下migrate管理db的原理。

我们使用migrate需要用到的主要包含以下两部分:1.versions里面的为版本号+数据库适配脚本;2.migrate.cfg为migrate需要用到的配置文件,两部分的命名是固定的。


使用migrate进行数据库升级非常简单,heat这边提供了heat-manage db_sync, db_version的命令用来升级db以及查看当前db的版本号,这里以执行heat-manages db_sync,看下migrate的过程。



def db_sync(engine, version=None): 
  path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 
            'migrate_repo') 
  return oslo_migration.db_sync(engine, path, version, 
                 init_version=INIT_VERSION) 

heat代码的入口在这里,需要传入engine用来连接db,version为我们需要升级到的版本,这里没有传,可以看到heat这边是直接使用oslo_migrate的db_sync方法,我们看下三方库中的这个方法。

def db_sync(engine, abs_path, version=None, init_version=0, sanity_check=True): 
  """Upgrade or downgrade a database. 
 
  Function runs the upgrade() or downgrade() functions in change scripts. 
 
  :param engine:    SQLAlchemy engine instance for a given database   //连接数据库 
  :param abs_path:   Absolute path to migrate repository.         //migrate仓库的绝对路径 
  :param version:   Database will upgrade/downgrade until this version. //需要升级或者降级到的版本号,如果不传则默认升级到最新版本 
             If None - database will update to the latest 
             available version. 
  :param init_version: Initial database version               //数据库的初始版本号,会以该初始版本为起点升级 
  :param sanity_check: Require schema sanity checking for all tables    //合理性检查 
  """ 
 
  if version is not None: 
    try: 
      version = int(version) 
    except ValueError: 
      raise exception.DBMigrationError(_("version should be an integer")) 
 
  current_version = db_version(engine, abs_path, init_version) 
  repository = _find_migrate_repo(abs_path) 
  if sanity_check: 
    _db_schema_sanity_check(engine) 
  if version is None or version > current_version: 
    migration = versioning_api.upgrade(engine, repository, version) 
  else: 
    migration = versioning_api.downgrade(engine, repository, 
                       version) 
  if sanity_check: 
    _db_schema_sanity_check(engine) 
 
  return migration 

代码很清晰,简洁。可以看到,整个过程就是先查询下当前db的版本,然后声明一个migrate仓库示例,对db做合理性检查(主要是针对mysql),然后根据传入的version和当前的version决定是升级或者降低,最后再次检查,整个migrate就完成了。

首先是查询当前数据库的版本,



def db_version(engine, abs_path, init_version): 
  """Show the current version of the repository. 
 
  :param engine: SQLAlchemy engine instance for a given database 
  :param abs_path: Absolute path to migrate repository 
  :param init_version: Initial database version 
  """ 
  repository = _find_migrate_repo(abs_path) 
  try: 
    return versioning_api.db_version(engine, repository) 
  except versioning_exceptions.DatabaseNotControlledError: 
    meta = sqlalchemy.MetaData() 
    meta.reflect(bind=engine) 
    tables = meta.tables 
    if len(tables) == 0 or 'alembic_version' in tables: 
      db_version_control(engine, abs_path, version=init_version) 
      return versioning_api.db_version(engine, repository) 
    else: 
      raise exception.DBMigrationError( 
        _("The database is not under version control, but has " 
         "tables. Please stamp the current version of the schema " 
         "manually.")) 

首先是根据传入的绝对路径,构造一个仓库对象的示例,这里比较关键,初始化方法如下,可以看到我们前面提到migrate.cfg和versions就是在这里被使用的,而且名字也是固定的,必须为migrate.cfg和versions。

class Repository(pathed.Pathed): 
  """A project's change script repository""" 
 
  _config = 'migrate.cfg' 
  _versions = 'versions' 
 
  def __init__(self, path): 
    log.debug('Loading repository %s...' % path) 
    self.verify(path) 
    super(Repository, self).__init__(path) 
    self.config = cfgparse.Config(os.path.join(self.path, self._config)) 
    self.versions = version.Collection(os.path.join(self.path, 
                           self._versions)) 
    log.debug('Repository %s loaded successfully' % path) 
    log.debug('Config: %r' % self.config.to_dict()) 

这里会验证我们传入的path下面是否存在versions和migrate.cfg,因此我们的代码目录结构也必须按照这个放。self.config主要是用来管理migrate.cfg的配置,self.versions主要用来管理如何升级,repository对象另外还包含3个比较重要的属性,latest:最新的版本(versions中版本号最大的),这里是73,version_table:用来记录和管理migrate版本号的数据库表,这里是migrate_version,id用来存放我们管理的数据库标示,这里是heat,后两项都是从数据库里面取。

@property 
  def latest(self): 
    """API to :attr:`migrate.versioning.version.Collection.latest`""" 
    return self.versions.latest 
 
  @property 
  def version_table(self): 
    """Returns version_table name specified in config""" 
    return self.config.get('db_settings', 'version_table') 
 
  @property 
  def id(self): 
    """Returns repository id specified in config""" 
    return self.config.get('db_settings', 'repository_id') 

回到之前的代码

try: 
  return versioning_api.db_version(engine, repository) 
except versioning_exceptions.DatabaseNotControlledError: 
  meta = sqlalchemy.MetaData() 
  meta.reflect(bind=engine) 
  tables = meta.tables 
  if len(tables) == 0 or 'alembic_version' in tables: 
    db_version_control(engine, abs_path, version=init_version) 
    return versioning_api.db_version(engine, repository) 
  else: 
    raise exception.DBMigrationError( 
      _("The database is not under version control, but has " 
       "tables. Please stamp the current version of the schema " 
       "manually.")) 

这里会根据数据库引擎和刚才的repository实例对象获取当前数据库的版本号,其实就是从migrate本身所在数据表中去查找当前的版本号(version_version),假如是第一次使用migrate,由于还没有建立migrate_version表,所以引发异常。这里会去查一下当前数据库中的所有数据表,如果已有其他数据库表,则会引发DBMigrationError的异常,因为migrate必须在建立其他数据表之前先建立才能管控所有的数据表,假如我们之前没有使用migrate机制但是想在后面的db控制中使用起来,这里有2个思路:手动插入migrate_version数据表并配置合适的版本或者手动插入alebic_version。

继续往下面看,如果是第一次使用migrate,



db_version_control(engine, abs_path, version=init_version) 

会建立migrate_version,并设置合适的初始值,也就是传入的init_version,后续的数据库升级等操作就可以通过migrate管控起来了。migrate_version表建立起来后,就回到了最初的流程,根据我们db_sync传入的版本号和当前的版本号,migrate会执行versions里面每个版本的upgrade()方法直至升级完成并更新migrate_version中的版本号。

  使用migrate的好处在于,可以很方便的集中记录和管理每次对数据库的变动,在后续升级过程中,一键式完成对应的适配操作,非常方便,并且不会出现重复增加字段等操作,在开发过程中,我们只要知道了migrate的原理,就能很方便的使用起来了。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

    您感兴趣的教程

    在docker中安装mysql详解

    本篇文章主要介绍了在docker中安装mysql详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编...

    详解 安装 docker mysql

    win10中文输入法仅在桌面显示怎么办?

    win10中文输入法仅在桌面显示怎么办?

    win10系统使用搜狗,QQ输入法只有在显示桌面的时候才出来,在使用其他程序输入框里面却只能输入字母数字,win10中...

    win10 中文输入法

    一分钟掌握linux系统目录结构

    这篇文章主要介绍了linux系统目录结构,通过结构图和多张表格了解linux系统目录结构,感兴趣的小伙伴们可以参考一...

    结构 目录 系统 linux

    PHP程序员玩转Linux系列 Linux和Windows安装

    这篇文章主要为大家详细介绍了PHP程序员玩转Linux系列文章,Linux和Windows安装nginx教程,具有一定的参考价值,感兴趣...

    玩转 程序员 安装 系列 PHP

    win10怎么安装杜比音效Doby V4.1 win10安装杜

    第四代杜比®家庭影院®技术包含了一整套协同工作的技术,让PC 发出清晰的环绕声同时第四代杜比家庭影院技术...

    win10杜比音效

    纯CSS实现iOS风格打开关闭选择框功能

    这篇文章主要介绍了纯CSS实现iOS风格打开关闭选择框,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作...

    css ios c

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的办法

    Win7如何给C盘扩容 Win7系统电脑C盘扩容的

    Win7给电脑C盘扩容的办法大家知道吗?当系统分区C盘空间不足时,就需要给它扩容了,如果不管,C盘没有足够的空间...

    Win7 C盘 扩容

    百度推广竞品词的投放策略

    SEM是基于关键词搜索的营销活动。作为推广人员,我们所做的工作,就是打理成千上万的关键词,关注它们的质量度...

    百度推广 竞品词

    Visual Studio Code(vscode) git的使用教程

    这篇文章主要介绍了详解Visual Studio Code(vscode) git的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。...

    教程 Studio Visual Code git

    七牛云储存创始人分享七牛的创立故事与

    这篇文章主要介绍了七牛云储存创始人分享七牛的创立故事与对Go语言的应用,七牛选用Go语言这门新兴的编程语言进行...

    七牛 Go语言

    Win10预览版Mobile 10547即将发布 9月19日上午

    微软副总裁Gabriel Aul的Twitter透露了 Win10 Mobile预览版10536即将发布,他表示该版本已进入内部慢速版阶段,发布时间目...

    Win10 预览版

    HTML标签meta总结,HTML5 head meta 属性整理

    移动前端开发中添加一些webkit专属的HTML5头部标签,帮助浏览器更好解析HTML代码,更好地将移动web前端页面表现出来...

    移动端html5模拟长按事件的实现方法

    这篇文章主要介绍了移动端html5模拟长按事件的实现方法的相关资料,小编觉得挺不错的,现在分享给大家,也给大家...

    移动端 html5 长按

    HTML常用meta大全(推荐)

    这篇文章主要介绍了HTML常用meta大全(推荐),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参...

    cdr怎么把图片转换成位图? cdr图片转换为位图的教程

    cdr怎么把图片转换成位图? cdr图片转换为

    cdr怎么把图片转换成位图?cdr中插入的图片想要转换成位图,该怎么转换呢?下面我们就来看看cdr图片转换为位图的...

    cdr 图片 位图

    win10系统怎么录屏?win10系统自带录屏详细教程

    win10系统怎么录屏?win10系统自带录屏详细

    当我们是使用win10系统的时候,想要录制电脑上的画面,这时候有人会想到下个第三方软件,其实可以用电脑上的自带...

    win10 系统自带录屏 详细教程

    + 更多教程 +
    WIN服务器linux服务器FTP服务器DNS服务器其他