MySQL到Greenplum迁移分析

数据类型对比

  MySQL PostgreSQL comments
数值类型 TINYINT SMALLINT gp中无zerofill属性及unsigned类型,所以为了数据不越界需使用大一精度的数据类型匹配
SMALLINT SMALLINT
MEDIUMINT INTEGER
INT|INTEGER INTEGER
BIGINT BIGINT
TINYINT UNSIGNED SMALLINT
SMALLINT UNSIGNED INTEGER
MEDIUMINT UNSIGNED INTEGER
INT UNSIGNED BIGINT
BIGINT UNSIGNED NUMERIC(20)
BIT BIT
FLOAT REAL
FLOAT UNSIGNED DOUBLE PRECISION
DOUBLE|REAL|DOUBLE PRECISION DOUBLE PRECISION
DECIMAL|DEC|NUMERIC|FIXED NUMERIC
字符类型 CHAR CHARACTER|CHAR  
VARCHAR CHARACTER VARYING|VARCHAR  
TINYTEXT TEXT  
TEXT TEXT  
MEDIUMTEXT TEXT  
LONGTEXT TEXT  
BINARY|CHAR BYTE BYTEA  
VARBINARY BYTEA  
TINYBLOB BYTEA  
BLOB BYTEA  
MEDIUMBLOB BYTEA  
LONGBLOB BYTEA  
时间类型 DATE DATE  
TIME TIME  
YEAR  
DATETIME TIMESTAMP  
TIMESTAMP TIMESTAMP  
其他类型 BOOL|BOOLEAN BOOLEAN  
ENUM CREATE TYPE … AS ENUM  
SET  

语法对比

2.1 limit

MySQL:

or

 

Greenplum:

2.2 replace

MySQL:

Greenplum:

不支持该语法,需要使用函数实现,例:

2.3 insert into … on duplicate key update

MySQL:

Greenplum:

不支持该语法,需要使用函数实现,例:

2.4 select … into outfile

MySQL:

Greenplum:

2.5 自增列

MySQL:

列加auto_increment属性,例:create table a(id int auto_increment primary key)

获取当前值:select last_insert_id()

 

Greenplum:

字段类型使用serial,例:create table a(id serial primary key)

获取当前值:select currval(‘a_id_seq’)

2.6 注释

MySQL:

使用#或–

Greenplum:

使用–

2.7 执行存储过程

MySQL:

Greenplum:

Greenplum并无存储过程,使用函数代替,所以执行:

常用函数对比

3.1 时间函数

3.1.1 时间转字符串

MySQL:date_format()

例:select date_format(now(),’%Y%m%d%H%i%s’)

Greenplum:to_char()

例:select to_char(now(), ‘YYYYMMDDHH24MISS’)

3.1.2 字符串转时间

MySQL:str_to_date()

例:select str_to_date(‘20171120′,’%Y%m%d%H%i%s’)

Greenplum:to_date(),to_timestamp()

例:select to_date(‘20171120’, ‘YYYYMMDD’)

select to_date(‘20171120’, ‘YYYYMMDDHH24MISS’)

3.1.3 时间计算

MySQL:date_add()

例:select date_add(now(), interval 2 day)

Greenplum:直接计算

例:select now() + interval ‘2 day’

3.2 字符函数

3.2.1 空字符串处理

MySQL:ifnull

例:select ifnull(null,‘default’)

Greenplum:coalesce

例:select coalesce(null,‘default’)

3.2.2 字符串拼接

MySQL:concat()

例:select concat(‘abc’,‘def’)

Greenplum:||

例:select ‘abc’||’def’

数据迁移

Greenplum数据导入3种方式:

4.1 COPY命令

COPY需要经过master,仅建议在小数据量时使用。无法并行导入,在大量数据导入时效率很低,不过多介绍。

例:COPY tablea FROM ‘/data/tablea_data’;

4.2 使用外部表

外部表以及4.3中的gpload都需要使用gpfdist服务。

gpfdist是Greenplum自带的一个并行文件服务,原理如下图:

gpfdist为每个segment提供并行读写数据文件的服务。

 

1、先启动gpfdist服务,例:

-d 指定数据目录 -p指定服务端口 -l 指定日志文件

将数据文件放入该目录下

2、创建外部表,例:

  • 从外部表导入数据,例:

或者先创建,后导入:

4.3 gpload

通过配置yaml控制文件来进行数据导入,同样依赖gpfdist服务。

例:

1、编辑a.yml文件

2、进行导入:

 

delete\update where… in语句 导致死锁问题分析

1、问题描述
测试中发现delete\update语句在并发测试时经常会导致发生死锁。
2、问题分析
在mariadb中,update或delete使用 where(…,…,…) in (…,…,..) 的写法会导致全表扫描,加大锁冲突的概率,造成死锁。
例1:

场景1:

当会话1:start transaction;
delete from b where (a,b,c,d,e) in ((1,1,1,1,1));
会话2:start transaction;
delete from b where (a,b,c,d,e) in ((1,1,2,1,1));
此时会话2会产生锁等待。
而场景2:
当会话1:start transaction;
delete from b where where a=1 and b=1 and c=1 and d=1 and e=1;
会话2:start transaction;
delete from b where where a=1 and b=1 and c=2 and d=1 and e=1;
不会产生锁等待。
例2:

3、解决办法

不使用delete\update where… in的写法,改为delete\update where … and …or
4、半一致性读仍会死锁问题
在read-committed隔离级别下,update会使用半一致性读,为什么还是有几率发生死锁?
根据场景复现后,show engine innodb status查看死锁信息,发现发生死锁的事务会占有很多锁,不符合innodb半一致读的特性:

5、Mariadb处理逻辑

在RC隔离级别下,在执行计划为全表扫描或全索引扫描的情况下innodb处理update的流程如下图:
6、源码分析
遍历记录的主函数为row_search_for_mysql()
调用sel_set_rec_lock()尝试加锁
如果加锁时产生锁等待,即:

进行半一致性读:


如果在半一致读时,等待的锁已被释放,则锁读当前版本

如果读取到历史版本则设置did_semi_consistent_read = TRUE;
根据did_semi_consistent_read的值设置prebuilt->row_read_type

调用解锁函数ha_innobase::unlock_row()


但是由于在上面的代码中prebuilt->new_rec_locks的值为0,所以不会对记录解锁!!!

测试MySQL不存在上述问题,应该是这块逻辑与MariaDB不同。

MariaDB&MySQL数据库及操作系统字符集设置及关系

1         LINUX操作系统

1.1       系统LANG变量

可以使用locale -a查看系统支持的字符集。
使用export LANG=zh_CN.utf8或zh_CN.gbk进行设置。
如使用ssh客户端连接服务器,如SecrueCRT等,需要设置与服务器相同的字符集才能正常显示。

2         数据库

2.1       数据库字符集变量

查看数据库系统字符集信息:
这些变量都可以通过set 及set global进行修改,重启后失效。

以下为每个变量介绍:

  • character_set_client :客户端字符集,可以通过修改my.cnf中[client]段中的default-character-set,重启客户端生效。
  • character_set_connection:连接字符集,可以通过修改my.cnf中[client]段中的default-character-set,重启客户端生效。
  • character_set_database:数据库默认字符集,创建数据库时指定,不指定则默认使用服务器字符集。建表时如不指定字符集则继承数据库默认字符集。Load data时数据文件字符集需与该值一致。
  • character_set_filesystem :文件系统字符集,linux系统默认为binary。该值不会影响乱码。
  • character_set_results:结果字符集,可以通过修改my.cnf中[client]段中的default-character-set,重启客户端生效。
  • character_set_server:服务器默认字符集,可以通过修改my.cnf中[mysqld]段中的character-set-server,重启服务端生效。如果不配置,则以编译代码时的-DDEFAULT_CHARSET选择为默认值。默认为latin1。
  • character_set_system:MariaDB系统自用字符集,恒为utf8。该值不会影响乱码。
SET NAMES ‘utf8’;  它相当于下面的三句命令:
SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;

2.1.1    表以及列字符集

创建表时指定表和列的字符集,如果不指定,则列从表继承,表从character_set_database继承。后期也可通过alter table命令修改。

2.2       SQL脚本

一个用户请求字符集转换完整流程:

1、Mysql客户端以character_set_client解析SQL语句

2、转化为character_set_connection发送到服务端(character_set_connection字符集必须大于等于character_set_client,否则会丢失数据。如utf8>gbk>latin1)

3、服务端转化为内部字符集

(同理,内部字符集必须大于等于character_set_connection,否则会丢失数据。
按照如下规则:
    A. 转化为每个数据字段的CHARACTER SET设定值;
    B. 若上述值不存在,则继承对应数据表的CHARACTER SET设定值
    C. 若上述值不存在,则继承对应数据库的 character_set_database设定值;
    D. 若上述值不存在,则继承character_set_server设定值。)

4、最后服务端将操作结果从内部操作字符集转换为character_set_results返回客户端。

sql脚本编码必须与character_set_client一致,否则会出现乱码。

2.3       LOAD导入数据文件

LOAD命令字符集转换完整流程:

1、以LOAD命令里指定的CHARACTER SET解析数据文件(如没有指定,则以character_set_database设定值解析)

2、服务端转化为内部字符集(规则同2.2)

如不在LOAD命令中指定,则数据文件编码必须与character_set_database一致,否则会出现编码错误无法导入。

2.4       导出数据文件

如不在导出命令中指定字符集,则不进行转换,以数据列的字符集直接导出。(如果表的不同字段设置的字符集不同,会导致同一数据文件存在多种编码格式,如:字段a以utf8编码,b以gbk编码。)
如在命令中指定字符集,则按照指定的字符集导出到文件中。