首页 > 数据库 >数据库SqlServer迁移PostgreSql实践

数据库SqlServer迁移PostgreSql实践

时间:2023-06-30 14:31:55浏览次数:161  
标签:PostgreSql defined 数据库 SqlServer table 迁移 self sqlstype


SqlServer属于商业数据库,不可能像Mysql等数据库一样,去解析相关的数据库binlog,从而实现增量数据的回放,结合应用属性,最后确定采用离线迁移方式,从SqlServer中将表数据全部读出,然后将数据写入到pg中,采用此种方案的弊病就是程序端需停止写入(应用可将部分数据缓存到本地),等待数据库迁移完成后,程序端再迁移至PostGresql,迁移方法如下

数据库SqlServer迁移PostgreSql实践_表结构

背景
公司某内部系统属于商业产品,数据库性能已出现明显问题,服务经常卡死,员工经常反馈数据无法查询或不能及时查询,该系统所使用的数据库为SqlServer,SqlServer数据库属于商业数据库,依赖厂商的维护,且维护成本高,效率低,且存在版权等问题,考虑将该系统的数据库,迁移至PostGresql数据库,属于BSD的开源数据库,不存在版本问题,公司也有部分系统采用pg,维护成本也将大大减低。

迁移原理

SqlServer属于商业数据库,不可能像Mysql等数据库一样,去解析相关的数据库binlog,从而实现增量数据的回放,结合应用属性,最后确定采用离线迁移方式,从SqlServer中将表数据全部读出,然后将数据写入到pg中,采用此种方案的弊病就是程序端需停止写入(应用可将部分数据缓存到本地),等待数据库迁移完成后,程序端再迁移至PostGresql,迁移方法如下: 

数据库SqlServer迁移PostgreSql实践_数据库_02

表结构迁移原理
表结构主要包含字段,索引,主键,外键等信息组成,主要采用开源工具sqlserver2pg进行表结构的转换

表结构转换
从SqlServer中读写表结构的字段信息,并对字段类型进行转换,转换核心代码如下

sub convert_type
{
    my ($sqlstype, $sqlqual, $colname, $tablename, $typname, $schemaname) =
        @_;
    my $rettype;
    if (defined $types{$sqlstype})
    {
        if ((defined $sqlqual and defined($unqual{$types{$sqlstype}}))
            or not defined $sqlqual)
        {
           # This is one of the few types that have to be unqualified (binary type)
           $rettype = $types{$sqlstype};

           # but we might add a check constraint for binary data
           if ($sqlstype =~ 'binary' and defined $sqlqual) {
              print STDERR "convert_type: $sqlstype, $sqlqual, $colname\n";
              my $constraint;
              $constraint->{TYPE}  = 'CHECK_BINARY_LENGTH';
              $constraint->{TABLE} = $tablename;
              $constraint->{TEXT}  = "octet_length(" . format_identifier($colname) . ") <= $sqlqual";
              push @{$objects->{SCHEMAS}->{$schemaname}->{TABLES}->{$tablename}
                        ->{CONSTRAINTS}}, ($constraint);
           }
        }
        elsif (defined $sqlqual)
        {
            $rettype = ($types{$sqlstype} . "($sqlqual)");
        }
    }

    # A few special cases
    elsif ($sqlstype eq 'bit' and not defined $sqlqual)
    {
        $rettype = "boolean";
    }
    elsif ($sqlstype eq 'ntext' and not defined $sqlqual)
    {
        $rettype = "text";
    }

外键,索引,唯一键转换

主要是从sqlserver导出的表结构数据中,对相关的索引,外键等语句进行转换,转换核心代码如下

while (my ($schema, $refschema) = each %{$objects->{SCHEMAS}})
    {
        # Indexes
        # They don't have a schema qualifier. But their table has, and they are in the same schema as their table
        foreach my $table (sort keys %{$refschema->{TABLES}})
        {
            foreach
                my $index (
                   sort keys %{$refschema->{TABLES}->{$table}->{INDEXES}})
            {
                my $index_created = 0;
                my $idxref =
                    $refschema->{TABLES}->{$table}->{INDEXES}->{$index};
                my $idxdef .= "";
                if ($idxref->{DISABLE})
                {
                    $idxdef .= "-- ";
                }
                $idxdef .= "CREATE";
                if ($idxref->{UNIQUE})
                {
                    $idxdef .= " UNIQUE";
                }
                if (defined $idxref->{COLS})
                {
                   $idxdef .= " INDEX " . format_identifier($index) . " ON " . format_identifier($schema) . '.' . format_identifier($table) . " ("
                     . join(",", map{format_identifier_cols_index($_)} @{$idxref->{COLS}}) . ")";

                   if (defined $idxref->{INCLUDE}) {
                      $idxdef .= " INCLUDE (" .
                         join(",", map{format_identifier_cols_index($_)} @{$idxref->{INCLUDE}})
                         . ")";
                   }

                   if (not defined $idxref->{WHERE} and not defined $idxref->{DISABLE}) {
                      $idxdef .= ";\n";
                      print AFTER $idxdef;
                      # the possible comment would go to after file
                      $index_created = 1;
                   }

数据类型转换原理

数据类型转换

数据库SqlServer迁移PostgreSql实践_sql_03

函数类型转换

数据库SqlServer迁移PostgreSql实践_数据库_04

存储过程

视图部分需手动改造

迁移方法

表结构转换

./sqlserver2pgsql.pl -b before.sql -a after.sql -u unsure.sql -k /opt/data_migration/data-integration/  -sd test -sh 127.0.0.1 -sp 1433 -su user_only -sw 122132321 -pd  test -ph 192.168.1.1 -pp 15432 -pu postgres -pw 12345678  -pi 8  -po 8 -f script.sql

表结构导入pg

/usr/local/pgsql1201/bin/psql -h 127.0.0.1 -U postgres -p 15432 <before.sql

数据迁移

cd /opt/data_migration/data-integration/
sh kitchen.sh -file=migration.kjb  -level=detailed >migration.log

数据比对

#!/usr/bin/env python
# -*- coding: utf-8 -*-


"""
@author:jacker_zhou
@create_time: 2017-04-07
@overview: mssql pg 
"""

__author__ = 'jacker_zhou'
__version__ = '0.1'

import psycopg2,pymssql 
import types
import time
TableSpace='public.' 
class CompareDataBase(): 
    def __init__(self): 
        
        self.pgcnotallow=psycopg2.connect(database="test",host="127.0.0.1",port=15432,user="postgres",password="test") 
        
        self.mscnotallow=pymssql.connect(host="192.168.1.1",user="test",password="test",database="test") 
    def commit(self): 
        self.pgconn.commit() 
    def close(self): 
        self.pgconn.close() 
        self.msconn.close() 
    def rollback(self): 
        self.pgconn.rollback() 
    def exesyncdb(self): 
        mscursor=self.msconn.cursor() 
        sql=("SELECT COUNT(COLUMNNAME) AS CT,TABLENAME FROM (SELECT A.NAME AS COLUMNNAME,B.NAME AS TABLENAME FROM SYSCOLUMNS A RIGHT JOIN SYSOBJECTS B ON A.ID=B.ID WHERE B.TYPE='U' AND B.NAME NOT IN ('dtproperties','0626')) A GROUP BY TABLENAME ") 
        mscursor.execute(sql) 
        table=mscursor.fetchall()
        print ("total table %d"%len(table))
        if(table is None or len(table)<=0): 
            return 
        else: 
            for row in table: 
                self.executeTable(row[1],row[0]) 
                print ("%s is execute success"%row[1])
    def comparedb(self): 
        mscursor=self.msconn.cursor() 
        sql=("SELECT COUNT(COLUMNNAME) AS CT,TABLENAME FROM (SELECT A.NAME AS COLUMNNAME,B.NAME AS TABLENAME FROM SYSCOLUMNS A RIGHT JOIN SYSOBJECTS B ON A.ID=B.ID WHERE B.TYPE='U' AND B.NAME NOT IN ('dtproperties','0626')) A GROUP BY TABLENAME ") 
        mscursor.execute(sql) 
        table=mscursor.fetchall()
        print ("total table %d"%len(table))
        if(table is None or len(table)<=0): 
            return 
        else: 
            for row in table: 
                self.compareTable(row[1]) 
    def executeTable(self,tablename,count): 
        #print tablename 
        sql1="SELECT * FROM %s"%tablename 
        print (sql1)
        mscursor=self.msconn.cursor() 
        mscursor.execute(sql1) 
        table=mscursor.fetchall()
        if(table is None or len(table)<=0): 
            mscursor.close() 
            return 
        lst_result=self.initColumn(table)
        #print "column" 
        mscursor.close() 
        print ("execute sync  %s data to postgresql"%tablename)
        sql2=self.initPgSql(tablename,count)
        pgcursor=self.pgconn.cursor() 
        pgcursor.executemany(sql2,lst_result) 
        pgcursor.close()
    def compareTable(self,tablename): 
        #print tablename 
        sql1="SELECT count(*) FROM %s"%tablename 
        mscursor=self.msconn.cursor() 
        mscursor.execute(sql1) 
        ms_res=mscursor.fetchall() 
        mscursor.close() 
        pgcursor=self.pgconn.cursor()
        pgcursor.execute(sql1) 
        pg_res=pgcursor.fetchall() 
        pgcursor.close()
        res =""
        if ms_res[0][0] == pg_res[0][0]:
            res ="ok"
        else:
            res = "fail"

        print ("execute compare  table  %s data  postgresql: %s  mssql:%s result: %s"%(tablename,pg_res[0][0],ms_res[0][0],res))
    if __name__=="__main__": 
        sdb= CompareDataBase()
        start_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        print ("task start time %s"%start_time)
        try: 
            sdb.comparedb()
        except Exception as e: 
            print (e) 
            sdb.rollback() 
        else: 
            sdb.commit() 
        sdb.close() 
        end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        print ("task end time %s"%end_time)
        print ("ok........" )

参考

https://github.com/dalibo/sqlserver2pgsql

作者:古道轻风

标签:PostgreSql,defined,数据库,SqlServer,table,迁移,self,sqlstype
From: https://blog.51cto.com/chunyangi/6590470

相关文章

  • python连接mysql数据库
    连接mysql方式很多,这里先只介绍pymysql库连接mysql数据库。1.安装pymysqlpipinstallpymysql-ihttps://pypi.tuna.tsinghua.edu.cn/simple2.  建立mysql数据表 安装好mysql数据库之后,建立表并插入数据后如下: 表的结构:3.连接数据库连接数据......
  • 事务全攻略,MySQL数据库必学知识!
    前言从今天开始,健哥就带各位小伙伴学习数据库技术。数据库技术是Java开发中必不可少的一部分知识内容。也是非常重要的技术。本系列教程由浅入深,全面讲解数据库体系。非常适合零基础的小伙伴来学习。全文大约【1707】字,不说废话,只讲可以让你学到技术、明白原理的纯干货!本文......
  • 用dotLucene为数据库内容建立索引
    //建立索引的类publicclassIndexer{privateIndexWriterwriter;Documentdoc=newDocument();publicIndexer(stringDirectory){InitializeIndex(Directory);}......
  • postgresql 字符串转整数 int、integer
    postgresql字符串转整数int、integer --把'1234'转成整数selectcast('1234'asinteger);--用substring截取字符串,从第8个字符开始截取2个字符:结果是12selectcast(substring('1234abc12',8,2)asinteger)---使用to_number函数来转换成整数---to_number(text,......
  • 一个sqlite的例子,将图片存入sqlite数据库
          最近学习sqlite,在看了一些资料后,写了一个例子,可能对初学sqlite或者学习将图片插入数据库等的有用,把源码发出来,给需要参考的。也欢迎大家提出建议。         包含常用的sql操作,增删改查,同时也在例子中说明,sqlit的sql语句使用参数,可以用@也可以用$。     ......
  • mongodb数据库操作备忘
    db.SMU_REALDATA_PARAMETERS.update({},{$rename:{"interfaceID":"INTERFACEID"}},false,true); 修改field,字段名,不是修改值哟。 网上搜索mongodb语言和CheatSheet手册。https://www.mongodb.com/developer/products/mongodb/cheat-sheet/navcate工具和下面工具都可以。 ......
  • Linux 中重置数据库的 root 密码的技巧
    其中一项是设置数据库root帐户的密码-你必须保持私密,并仅在绝对需要时使用。如果你忘记了密码或需要重置密码(例如,当数据库管理员换人或被裁员!),这篇文章会派上用场。如果你是第一次设置MySQL或MariaDB数据库,你可以直接运行mysql_secure_installation来实现基本的安......
  • 清除SQL Server数据库日志(ldf文件)的几种方法
    随着系统运行时间的推移,数据库日志文件会变得越来越大,这时我们需要对日志文件进行备份或清理。  随着系统运行时间的推移,数据库日志文件会变得越来越大,这时我们需要对日志文件进行备份或清理。  解决方案1-直接删除本地ldf日志文件:(比较靠谱方案!)  1.......
  • MICROSOFT SQL SERVER TO POSTGRESQL MIGRATION USING PGLOADER
    Tocontinueourmigrationseries,today’spostwillfocusonpgloader.PgloaderisanotherOpenSourcedatamigrationutilityforPostgreSQLfromMySQLandSQLServer.Today’sdemowillmigrateasampledatabase(StackOverflow)fromMSSQ......
  • mybatis-plus框架配合数据库表时间字段自动更新问题
    背景在项目开发前的设计阶段,我们会根据需求分析、业务梳理的结果进行领域建模。通常有2种方式:实体设计优先数据库设计优先无论哪种方式,最终会创建数据库、数据表。通常在每一张表,会设计2个时间自动,创建时间和修改时间,这样在查询数据时能够清晰的看到数据行是什么时候创建......