MySQL字符集和copy_and_convert

MySQL字符集转换优化
针对MySQL在业务压力测试中出现的copy_and_convert操作导致CPU消耗问题进行探讨,分析了字符集转换过程,并提出两种解决方案。

关于copy_and_convert
   
在对MySQL做业务压力测试的时候,我们在perf结果中发现 copy_and_convert 是一个耗费cpu的操作。这个函数的意思,就是在字符集之间做内容转换。
    如果源和目标的字符集相同,就可以直接用memcpy,这显然比做字符集转换(按字节或字长拷贝更快,和节省cpu)

 当整个系统是CPU瓶颈时,我们希望能够减少这种cpu消耗。

 

 一次查询涉及的拷贝
   
如果我们执行一个简单的select语句,会涉及到两部分的内容:metadata和data。MySQL的返回包是能够“自解析”的,也就是说不仅有内容,还有描述信息。这些描述信息我们称为metadata。

    在将这两部分信息返回给客户端的时候,就涉及到字符串拷贝,如果源字符集和目标字符集不同,就需要作转换。

 

字符集里面可以定义的部分
1、  表的字符集
    比如我们创建如下的表


CREATE TABLE `t` (
  `c` int(11) DEFAULT NULL,
  `d` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=gbk;

    这就定义了这个表的字符集,要求存入数据按照gbk格式存储。在我们查询的时候,从表中读出的数据,就是我们上文说到的“源字符集”,在这里是gbk.

比如
Insert t values(2012, ‘南方很冷’);

 

2、连接字符集

    在连接的时候我们可以在mysql客户端指定参数 mysql -default-character-set=gbk ,表示我们接收的内容希望是gbk编码,这个就是上文说的“目标字符集”。

    一般我们会被建议:连接字符集和表字符集相同。

    有的文章说防止乱码—-乱码倒是不会,实际的原因就是在内容拷贝时避免字符集转换计算。

 

 本文要说的“但是”

 

但是即使这么做,在压力测试(或者你gdb)时,还是会发现会调用到 copy_and_convert。

 

原因有二:
    首先,metadata部分,是固定的使用utf8,这个值可以从show variables like ‘character_set_system’;的结果看到,hardcode,配置无法修改。

    其次,虽然我们定义表字符集是gbk, 但是整型字段是是Latin1存储,
    #define my_charset_numeric      my_charset_latin1,配置无法修改。

 因此在我们上面的设置中,一次查询,在传回metadata的时候需要utf8->gbk, 返回结果中c这个字段需要latin1->gbk.

 

问题和建议

从上面分析知道,这存在的问题就是,无论用户怎么设置,都无法完全避免这种转换

 由于数字类型转成字符串后,使用什么字符集都占用一样的空间。


1)       在代码上最简单的修改可以如下:
将 #define my_charset_numeric      my_charset_latin1 改成
#define my_charset_numeric      my_charset_utf8_general_ci

 这样跟metadata部分相同,然后建表和客户端都是使用utf-8。

 

 2)      更灵活一点是把character_set_system改成可配置,然后整型存储按照用户定义的字符集。

# global settings pid_dir: '~/.pg_chameleon/pid/' log_dir: '~/.pg_chameleon/logs/' log_dest: file log_level: info log_days_keep: 10 rollbar_key: '' rollbar_env: '' dump_json: No alert_log_collection_enable: false alert_log_kafka_server: 127.0.0.1:9092 alert_log_kafka_topic: my_log # type_override allows the user to override the default type conversion # into a different one. type_override: # "tinyint(1)": # override_to: boolean # override_tables: # - "*" # specify the compress properties when creating tables compress_properties: compresstype: 0 compress_level: 0 compress_chunk_size: 4096 compress_prealloc_chunks: 0 compress_byte_convert: false compress_diff_convert: false # postgres destination connection pg_conn: host: "localhost" port: "5432" user: "usr_replica" password: "never_commit_passwords" database: "db_replica" charset: "utf8" params: # maintenance_work_mem: "1G" # param1: value1 # param2: value2 sources: mysql: readers: 4 writers: 4 retry: 3 db_conn: host: "localhost" port: "3306" user: "usr_replica" password: "never_commit_passwords" charset: 'utf8' connect_timeout: 10 schema_mappings: delphis_mediterranea: loxodonta_africana limit_tables: - delphis_mediterranea.foo skip_tables: - delphis_mediterranea.bar enable_compress: No compress_tables: - delphis_mediterranea.foo grant_select_to: - usr_readonly lock_timeout: "120s" my_server_id: 100 replica_batch_size: 10000 replay_max_rows: 10000 batch_retention: '1 day' copy_max_memory: "300M" copy_mode: 'file' out_dir: /tmp csv_dir: /tmp contain_columns: No column_split: ',' sleep_loop: 1 on_error_replay: continue on_error_read: continue auto_maintenance: "disabled" index_parallel_workers: 2 gtid_enable: false type: mysql skip_events: insert: - delphis_mediterranea.foo # skips inserts on delphis_mediterranea.foo delete: - delphis_mediterranea # skips deletes on schema delphis_mediterranea update: keep_existing_schema: No migrate_default_value: Yes mysql_restart_config: No is_create_index: Yes index_dir: '~/.pg_chameleon/index/' is_skip_completed_tables: No with_datacheck: No slice_size: 100000 csv_files_threshold: csv_dir_space_threshold:
最新发布
07-05
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值