库表字符集不一致导致的全表扫描问题

背景:

当数据库的建库字符集和表不一样时,在库下针对表创建存储过程可能导致全表扫描

如下例:

drop database if exists xx1;
drop database if exists xx2;
create database xx1 character set utf8;
create database xx2 character set gbk;

 

然后分别在xx1 和 xx2下执行:

CREATE TABLE t1 ( `col1` varchar(10) NOT NULL, `col2` bigint(11) NOT NULL DEFAULT '0', PRIMARY KEY (`col1`)) ENGINE=InnoDB DEFAULT CHARSET=gbk;
insert into t1 values ('ac',2),('del',3),('sp',5),('tr',12);
DELIMITER //
CREATE PROCEDURE proc1(IN isn CHAR(20))
BEGIN
     START TRANSACTION;
     SELECT col2 FROM t1 WHERE col1= isn FOR UPDATE;
     COMMIT;
END //
DELIMITER ;

 

在5.5版本中通过show processlist可以看到SQL进行了转换(简单的锁住行记录,执行存储过程,再show processlist):

use xx1;

call proc1(‘tr’); 参数isn会被展开成:NAME_CONST(‘isn’,_utf8 0x7472 COLLATE ‘utf8_general_ci’)

use xx2;

call proc1(‘tr’); 参数isn会被展开成:NAME_CONST(‘isn’,_gbk 0x7472 COLLATE ‘gbk_chinese_ci’)

在5.6版本里使用ROW模式时,不会展开sql,但内部采取的是同样的策略。

由于name_const中引用的的字符集和表定义不同,因此xx1.proc1()每次调用都会产生两次全表扫描。但奇怪的是,如果单独执行上述SQL(而不是调用存储过程),就不会全表扫描。

还好MySQL5.6开始有了optimizer_trace这个利器,打开optimizer trace就一目了然了,对比单独执行SQL和执行存储过程:

call xx1.proc1(‘tr’)
| SELECT col2 FROM t1 WHERE col1=  NAME_CONST(‘isn’,_utf8’tr’ COLLATE ‘utf8_general_ci’) FOR UPDATE | {
“steps”: [
{
“join_preparation”: {
“select#”: 1,
“steps”: [
{
           “expanded_query”: “/* select#1 */ select `t1`.`col2` AS `col2` from `t1` where (convert(`t1`.`col1` using utf8) = isn@0)”
}
]
}
},
{
“join_optimization”: {
“select#”: 1,
“steps”: [
{
“condition_processing”: {
“condition”: “WHERE”,
“original_condition”: “(convert(`t1`.`col1` using utf8) = isn@0)”,
“steps”: [
{
“transformation”: “equality_propagation”,
“resulting_condition”: “(convert(`t1`.`col1` using utf8) = isn@0)”
},
{
“transformation”: “constant_propagation”,
“resulting_condition”: “(convert(`t1`.`col1` using utf8) = isn@0)”
},
{
“transformation”: “trivial_condition_removal”,
“resulting_condition”: “(convert(`t1`.`col1` using utf8) = isn@0)”
}
]
}
},
{
“table_dependencies”: [
{
“table”: “`t1`”,
“row_may_be_null”: false,
“map_bit”: 0,
“depends_on_map_bits”: [
]
}
]
},
{
“ref_optimizer_key_uses”: [
]
},
{
“rows_estimation”: [
{
“table”: “`t1`”,
“table_scan”: {
“rows”: 4,
“cost”: 1
}
}
]
},
{
“considered_execution_plans”: [
{
“plan_prefix”: [
],
“table”: “`t1`”,
“best_access_path”: {
“considered_access_paths”: [
{
“access_type”: “scan”,
“rows”: 4,
“cost”: 1.8,
“chosen”: true
}
]
},
“cost_for_plan”: 1.8,
“rows_for_plan”: 4,
“chosen”: true
}
]
},
{
“attaching_conditions_to_tables”: {
“original_condition”: “(convert(`t1`.`col1` using utf8) = isn@0)”,
“attached_conditions_computation”: [
],
“attached_conditions_summary”: [
{
“table”: “`t1`”,
“attached”: “(convert(`t1`.`col1` using utf8) = isn@0)”
}
]
}
},
{
“refine_plan”: [
{
“table”: “`t1`”,
“access_type”: “table_scan”
}
]
}
]
}
},
{
“join_execution”: {
“select#”: 1,
“steps”: [
]
}
}
]
}
call xx2.proc1(‘tr’)
SELECT col2 FROM t1 WHERE col1=  NAME_CONST(‘isn’,_gbk’tr’ COLLATE ‘gbk_chinese_ci’) FOR UPDATE | {
“steps”: [
{
“join_preparation”: {
“select#”: 1,
“steps”: [
{
“expanded_query”: “/* select#1 */ select `t1`.`col2` AS `col2` from `t1` where (`t1`.`col1` = isn@0)”
}
]
}
},
{
“join_optimization”: {
“select#”: 1,
“steps”: [
{
“condition_processing”: {
“condition”: “WHERE”,
“original_condition”: “(`t1`.`col1` = isn@0)”,
“steps”: [
{
“transformation”: “equality_propagation”,
“resulting_condition”: “multiple equal(isn@0, `t1`.`col1`)”
},
{
“transformation”: “constant_propagation”,
“resulting_condition”: “multiple equal(isn@0, `t1`.`col1`)”
},
{
“transformation”: “trivial_condition_removal”,
“resulting_condition”: “multiple equal(isn@0, `t1`.`col1`)”
}
]
}
},
{
“table_dependencies”: [
{
“table”: “`t1`”,
“row_may_be_null”: false,
“map_bit”: 0,
“depends_on_map_bits”: [
]
}
]
},
{
“ref_optimizer_key_uses”: [
{
“table”: “`t1`”,
“field”: “col1″,
“equals”: “isn@0″,
“null_rejecting”: false
}
]
},
{
“rows_estimation”: [
{
“table”: “`t1`”,
“rows”: 1,
“cost”: 1,
“table_type”: “const”,
“empty”: false
}
]
},
{
“condition_on_constant_tables”: “1”,
“condition_value”: true
},
{
“attaching_conditions_to_tables”: {
“original_condition”: “1”,
“attached_conditions_computation”: [
],
“attached_conditions_summary”: [
]
}
},
{
“refine_plan”: [
]
}
]
}
},
{
“join_execution”: {
“select#”: 1,
“steps”: [
]
}
}
]
}
use xx1;

SELECT col2 FROM t1 WHERE col1=

NAME_CONST

(‘isn’,_utf8’tr’ COLLATE ‘utf8_general_ci’)

 FOR UPDATE

 SELECT col2 FROM t1 WHERE col1=  NAME_CONST(‘isn’,_utf8’tr’ COLLATE ‘utf8_general_ci’) FOR UPDATE | {

  “steps”: [

    {

      “join_preparation”: {

        “select#”: 1,

        “steps”: [

          {

            “expanded_query”: “/* select#1 */ select `t1`.`col2` AS `col2` from `t1` where (`t1`.`col1` = convert(NAME_CONST(‘isn’,(_utf8’tr’ collate utf8_general_ci)) using gbk))”

          }

        ]

      }

    },

    {

      “join_optimization”: {

        “select#”: 1,

        “steps”: [

          {

            “condition_processing”: {

              “condition”: “WHERE”,

              “original_condition”: “(`t1`.`col1` = convert(NAME_CONST(‘isn’,(_utf8’tr’ collate utf8_general_ci)) using gbk))”,

              “steps”: [

                {

                  “transformation”: “equality_propagation”,

                  “resulting_condition”: “multiple equal(convert(NAME_CONST(‘isn’,(_utf8’tr’ collate utf8_general_ci)) using gbk), `t1`.`col1`)”

                },

                {

                  “transformation”: “constant_propagation”,

                  “resulting_condition”: “multiple equal(convert(NAME_CONST(‘isn’,(_utf8’tr’ collate utf8_general_ci)) using gbk), `t1`.`col1`)”

                },

                {

                  “transformation”: “trivial_condition_removal”,

                  “resulting_condition”: “multiple equal(convert(NAME_CONST(‘isn’,(_utf8’tr’ collate utf8_general_ci)) using gbk), `t1`.`col1`)”

                }

              ]

            }

          },

          {

            “table_dependencies”: [

              {

                “table”: “`t1`”,

                “row_may_be_null”: false,

                “map_bit”: 0,

                “depends_on_map_bits”: [

                ]

              }

            ]

          },

          {

            “ref_optimizer_key_uses”: [

              {

                “table”: “`t1`”,

                “field”: “col1″,

                “equals”: “convert(NAME_CONST(‘isn’,(_utf8’tr’ collate utf8_general_ci)) using gbk)”,

                “null_rejecting”: false

              }

            ]

          },

          {

            “rows_estimation”: [

              {

                “table”: “`t1`”,

                “rows”: 1,

                “cost”: 1,

                “table_type”: “const”,

                “empty”: false

              }

            ]

          },

          {

            “condition_on_constant_tables”: “1”,

            “condition_value”: true

          },

          {

            “attaching_conditions_to_tables”: {

              “original_condition”: “1”,

              “attached_conditions_computation”: [

              ],

              “attached_conditions_summary”: [

              ]

            }

          },

          {

            “refine_plan”: [

            ]

          }

        ]

      }

    },

    {

      “join_execution”: {

        “select#”: 1,

        “steps”: [

        ]

      }

    }

  ]
}

 

 

注意这expanded_query一项,对于这种方式的查询,需要读取每一条记录,进行字符集转换再比较:

expanded_query”: “/* select#1 */ select `t1`.`col2` AS `col2` from `t1` where (convert(`t1`.`col1` using utf8) = isn@0)

由于本人对优化器模块了解不深,也不确定是否是Bug,先Report到了一个小bug到Bug list,ref:http://bugs.mysql.com/bug.php?id=76002

Updated @2015/2/25, 存储过程的输入列需要显式的定义输入字符集,否则其输入列的字符集将继承自库的字符集。参考bug中的回复。

 

时间: 2024-11-01 00:41:10

库表字符集不一致导致的全表扫描问题的相关文章

oracle-数据库与项目字符集不一致,中文全是乱码

问题描述 数据库与项目字符集不一致,中文全是乱码 数据库迁移了,新的oracle字符集编码是AMERICAN_AMERICA.US7ASCII,项目编码为UTF-8,Java编码,现在页面上查询数据库中的中文都是乱码,试了好多方法都不行,求大神帮助- 解决方案 进入注册表,修改oracle的字符集,百度一下,有很多答案的 解决方案二: 导出时指定字符集导出,然后再导入新数据库就行了

大幅提升MySQL中InnoDB的全表扫描速度的方法_Mysql

 在 InnoDB中更加快速的全表扫描 一般来讲,大多数应用查询的时候都会用索引,查找很少的几行数据(主键查找或百行内的查询),但有时候我们需要全表查询.典型的全表扫描就是逻辑备份  (mysqldump) 和 online schema changes( 注:在线上对大表 schema 的操作,也是 facebook 的一个开源项目) (SELECT ... INTO OUTFILE).  在 Facebook我们用 mysqldump 来备份数据库. 正如你所知MySql提供两种备份方式,提

一条全表扫描sql语句的分析

今天在对生产系统做监控的时候,发现一个process的cpu消耗很高,抓取了对应的session和执行的sql语句. 发现是一个简单的update语句,这样一条如果CPU消耗较大,很可能是由于全表扫描的. UPDATE COMM_ACTIVITY SET COMM_ACTIVITY.EXTRACT_STATUS = NVL(:1 , EXTRACT_STATUS), COMM_ACTIVITY.SOURCE_TYPE = NVL(:2 , SOURCE_TYPE), OPERATOR_ID =

mysql修改表、字段、库的字符集

mysql修改表.字段.库的字符集 修改数据库字符集: ALTER DATABASE db_name DEFAULT CHARACTER SET character_name [COLLATE ...]; 把表默认的字符集和所有字符列(CHAR,VARCHAR,TEXT)改为新的字符集: ALTER TABLE test CONVERT TO CHARACTER SET character_name [COLLATE ...] 如:ALTER TABLE test CONVERT TO CHAR

SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析

在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index Scan))呢?是否所有情况都是如此?又该如何优化呢? 下面我们通过一些简单的例子来分析理解这些现象.下面的实验环境为SQL SERVER 2008,如果在不同版本有所区别,欢迎指正.   堆表单索引 首先我们构建我们测试需要实验环境,具体情况如下所示: DROP TABLE TEST    CRE

SQL中WHERE变量IS NULL条件导致全表扫描问题的解决方法_MsSql

复制代码 代码如下: SET @SQL = 'SELECT * FROM Comment with(nolock) WHERE 1=1    And (@ProjectIds Is Null or ProjectId = @ProjectIds)    And (@Scores is null or Score =@Scores)' 印象中记得,以前在做Oracle开发时,这种写法是会导致全表扫描的,用不上索引,不知道Sql Server里是否也是一样呢,于是做一个简单的测试1.建立测试用的表结

MongoDB Primary 为何持续出现 oplog 全表扫描?

线上某 MongoDB 复制集实例(包含 Primary.Secondary.Hidden 3个节点 ),Primary 节点突然 IOPS 很高,调查后发现,其中 Hidden 处于 RECOVERING 状态,同时 Priamry 上持续有一全表扫描 oplog 的操作,正是这个 oplog 的 COLLSCAN 导致IO很高. 2017-10-23T17:48:01.845+0800 I COMMAND [conn8766752] query local.oplog.rs query: {

PostgreSQL 全表 全字段 模糊查询的毫秒级高效实现 - 搜索引擎也颤抖了

标签 PostgreSQL , 分词 , 全文检索 , 全字段检索 , 任意字段检索 , 下拉框选择 , 搜索引擎 背景 在一些应用程序中,可能需要对表的所有字段进行检索,有些字段可能需要精准查询,有些字段可能需要模糊查询或全文检索. 比如一些前端页面下拉框的勾选和选择. 这种需求对于应用开发人员来说,会很蛋疼,因为写SQL很麻烦,例子: 之前写过一篇文章来解决这个问题 <PostgreSQL 行级 全文检索> 使用的是全文检索,而当用户的需求为模糊查询时? 如何来解决呢? 不难想到我之前写过

高水位线和全表扫描

   高水位线好比水库中储水的水位线,用于描述数据库中段的扩展方式.高水位线对全表扫描方式有着至关重要的影响.当使用delete 操作 表记录时,高水位线并不会下降,随之导致的是全表扫描的实际开销并没有任何减少.本文给出高水位线的描述,如何降低高水位线,以及高水 位线对全表扫描的影响.   一.何谓高水位线    如前所述,类似于水库中储水的水位线.只不过在数据库中用于描述段的扩展方式.     可以将数据段或索引段等想象为一个从左到右依次排开的一系列块.当这些块中未填充任何数据时,高水位线位于