存档

文章标签 ‘mysql’

php中set names与mysql_set_charset

2010年4月13日 没有评论

今天看到大家在讨论,发现这是个很严重而又容易疏忽的问题,我以前也一直是用set names,遂记录下来,也提醒自己一把。

1.set names与mysql_set_charset有什么区别?

一般情况下, 使用”SET NAMES”就足够了, 也是可以保证正确的. 那么为什么手册又要说推荐使用 mysqli_set_charset(PHP>=5.0.5)呢。手册里面也没有明确说明。我们可以看下php扩展的源代码:

//php-5.2.11-SRC/ext/mysqli/mysqli_nonapi.c line 342
PHP_FUNCTION(mysqli_set_charset)
{
    MY_MYSQL            *mysql;
    zval                *mysql_link;
    char                *cs_name = NULL;
    unsigned int        len;
    if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis()
                , "Os", &mysql_link, mysqli_link_class_entry, &cs_name, &len) == FAILURE) {
        return;
    }
    MYSQLI_FETCH_RESOURCE(mysql, MY_MYSQL*, &mysql_link, "mysqli_link", MYSQLI_STATUS_VALID);
    if (mysql_set_character_set(mysql->mysql, cs_name)) {
                //** 调用libmysql的对应函数
        RETURN_FALSE;
    }
    RETURN_TRUE;
}

可以看到php的mysql扩展是直接调用了mysql的mysql_set_character_set函数,接下来看看mysql的代码

//mysql-5.1.30-SRC/libmysql/client.c, line 3166:
int STDCALL mysql_set_character_set(MYSQL *mysql, const char *cs_name)
{
  struct charset_info_st *cs;
  const char *save_csdir= charsets_dir;
  if (mysql->options.charset_dir)
    charsets_dir= mysql->options.charset_dir;
  if (strlen(cs_name) < MY_CS_NAME_SIZE &&
     (cs= get_charset_by_csname(cs_name, MY_CS_PRIMARY, MYF(0))))
  {
    char buff[MY_CS_NAME_SIZE + 10];
    charsets_dir= save_csdir;
    /* Skip execution of "SET NAMES" for pre-4.1 servers */
    if (mysql_get_server_version(mysql) < 40100)       return 0;     sprintf(buff, "SET NAMES %s", cs_name);     if (!mysql_real_query(mysql, buff, strlen(buff)))     {       mysql->charset= cs;
    }
  }
  //以下省略

可以看到,除了调用real_query设置set names,还设置了mysql的charset变量。

2.这样有什么影响?

mysql_real_escape_string会受到影响,它与mysql_escape_string的区别就 是,  它会考虑”当前”字符集。如果仅仅使用set names,mysql_real_escape_string可能会失效。

例子:

$mysqli = new mysqli("localhost", "user", "pass", "test", 3306);

/* check connection */
if (mysqli_connect_errno()) {
    printf("Connect failed: %s\n", mysqli_connect_error());
    exit();
}

$mysqli->query('SET NAMES gbk'); //使用set names设置字符集
$city = chr(0xbf).chr(0x5c); //0xbf5c是个有效的gbk字符,模拟用户输入
$city = $mysqli->real_escape_string ($city);//使用real_escape进行过滤

/* this query will fail, cause we didn't escape $city */
 if (!$mysqli->query("INSERT into myCity(name) VALUES ('$city')")) {
    print "INSERT into myCity (name) VALUES ('$city')\n";
    printf("Error: %s\n", $mysqli->error);
}

var_dump($city);

var_dump($mysqli->client_encoding());

$mysqli->close();

3.解决方案

mysqli_set_charset函数对PHP和Mysql有版本要求,必须当mysql版本大于5,PHP版本大于5.0.5时,此函数才有效。至于另一个mysql_set_charset函数,则更要求PHP版本大于5.2.3时才能有效。对于mysql4.1以上版本,使用”SET character_set_client=binary;”
推荐使用mysql_set_charset设置字符集的方案,只有在环境不允许的情况下,我们才推荐使用第二种binary编码的方案。但是无论在什么情况下,都禁止使用”SET NAMES”来作为设置字符集的操作。

---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2010/04/13/php-in-the-set-names-and-mysql_set_charset.html
---------------------------------------------------------------

分类: 程序员 标签: ,

mysql学习笔记-1

2009年10月13日 没有评论

clip_image001

mysql最不同也是最重要的特性,就是它的存储引擎架构。如图,存储引擎只处理mysql server发送的请求,不处理sql语句的解析与缓存等内容。但innodb是一个例外,因为innodb有外键定义,而mysql server目前还没有实现相关内容。

在mysql内部,每个连接都有一个线程来处理这个连接上面的请求(所以事务是不能跨越在两个连接上的,一个连接在同一时间也只能启动一个事务)。

在sql语句的parser,将sql解析成树状结构,这里可能会重写sql决定使用哪个索引等。在query cache存储sql语句与result,如果命中,就不再到存储引擎进行查询了。Query cache只存储select语句。

mysql server本身使用表级锁,如ALTER TABLE使用的就是mysql server提供的。行级锁是在存储引擎实现的,各存储引擎都有自己实现行级锁的方式,mysql server对此不需要了解。

大多数存储引擎的隔离级别是read committed,但Mysql的默认隔离级别却是repeatable read。innodb是通过多版本的方式解决repeatable read带来的幻读问题的。附:

l read uncommited即脏读,一个事务修改了一行,另一个事务也可以读到该行。如果第一个事务执行了回滚,那么第二个事务读取的就是从来没有正式出现过的值。

l read commited即一致读,试图通过只读取提交的值的方式来解决脏读的问题,但是这又引起了不可重复读取的问题。一个事务执行一个查询,读取了大量的数据行。在它结束读取之前,另一个事务可能完成了对数据行的更改。当第一个事务试图再次执行同一个查询,服务器就会返回不同的结果。

l repeatable read即可重复读,在一个事务对数据行执行读取或写入操作时锁定了这些数据行。但是这种方式又引发了幻想读的问题。因为只能锁定读取或写入的行,不能阻止另一个事务插入数据,后期执行同样的查询会产生更多的结果。

l sieralizable完全串行,用的太少,就不写了。

默认情况下,mysql开启autocommit,只有调用start transaction,它一直认为每个sql都是一个单独的事务。如果关闭了autocommit,除非你调用了commit或者rollback,你会一直在一个事务里面。当调用commit或者rollback后,它会自动的帮你在启动一个事务。

因为存储引擎自己实现事务,所以一个事务无法跨越多重存储引擎类型的表。

在一个事务中任何时候都可以获取锁,但只有事务结束时才会释放锁。lock in share mode是允许其他session读但不能写,读取的数据行如果被其他事物锁着,就会等待直到那个事务提交。for update则是排他锁,不允许读或者写。

Innodb实现Mutiversion concurrency control(MVCC),通过为每行数据记录两个隐藏的值,这两个值分别为数据创建点与过期点(binlog中的那些版本号)。增删查的处理就不用多说了,改的时候,innodb通过增加一个数据行的拷贝来完成,拷贝的创建号与原数据行的删除号就是当前系统号。

遗留问题:

在repeatable read下,innodb是怎么处理这个lock in share mode的呢?

---------------------------------------------------------------
本站作品根据创作共同协议进行授权, 转载时请务必以超链接形式标明文章原始出处
原文地址:http://www.mirecle.com/2009/10/13/mysql-study-notes-1.html
---------------------------------------------------------------

分类: 程序员 标签: