PHP中为什么mysqli需要实例化,而mysql不需要?

PHP中为什么mysqli需要实例化,而mysql不需要?,第1张

mysqli也不一定需要实例化,之所以你要实例化是因为你是要以面向对象的方式来开发这个程序,但是你要是用面向过程的方式来写也是可以的,百度里面有例子你可以看一下

一、面向对象

<?php

$mysqli =new mysqli("localhost", "my_user", "my_password", "world")//实例化对象

/* check connection */

if (mysqli_connect_errno()) {

printf("Connect failed: %s\n", mysqli_connect_error())

exit()

}

printf("Host information: %s\n", $mysqli->host_info)

/* close connection */

$mysqli->close()

?>

二、面向过程

<?php

$link = mysqli_connect("localhost", "my_user", "my_password", "world")

/* check connection */

if (!$link) {

printf("Connect failed: %s\n", mysqli_connect_error())

exit()

}

printf("Host information: %s\n", mysqli_get_host_info($link))

/* close connection */

mysqli_close($link)

?>

在项目的一次需求中,需要对一个表增加字段,然而在执行增加字段的sql语句时,卡住了很久都没提交到Mysql完成,而此时对外接口服务请求也卡住了,这时中断卡住的alter table 语句,服务慢慢恢复正常,如果不搞清楚这个问题的根源,不敢增加字段,因为会直接影响到服务

通过show processlist 查看到在alter table语句执行卡住过程中,累计了大量状态为 Waiting for table metadata lock 的记录

然后查看当前的事务状态 执行 select * from information_schema.innodb_trx\G

发现了其中一条已经运行了很久的事务,我怀疑跟这个运行很久的而且没有提交的事务有关。

在本地mysql开多个终端测试

session 1: 开启事务,执行select 语句,但不提交事务

session 2:执行增加字段sql

执行被阻塞了

可以看到alter table语句的状态为Waiting for table metadata lock

session 3 : 再次查询t1表

也被阻塞了

select * from t1 再次查询t1表也是 Waiting for table metadata lock状态,说明由于 metadata lock的存在,会导致后面正常的查询都会因为等待锁而阻塞

再查看当前事务运行状态:

可以看到,session1的事务由于还没提交,所以这里能看到它的状态还是running

这时我们commit session1的事务,看看效果

session 1:

session 2:

session 3:

可以看到session1的事务提交后,session2 和session3 都正常执行了, 他们完成的时间分别是30秒和7秒

通过上面的还原测试,可以知道是由于事务没有提交而给表加了锁,导致后面alter语句因为等待锁而阻塞,从而影响后面的正常请求。

那说明我们的项目是默认开启了事务吗?

继续排查,项目是使用flask-sqlchemy的插件来管理mysql接入,然后查了下文档

在实例化sqlchemy的时候,会创建一个用于跟Mysql交互的session对象,看看源码

从 SignallingSession类的定义看来,autocommit=False,说明默认都给所有的sql执行开启事务,也就是说,哪怕是纯select语句,不需要加锁的select,我们的项目默认也需要开启事务,这对于Mysql MVCC的版本控制来说,是没必要的。

解决办法:就是在实例化SQLAlchemy的时候,给一个参数,修改的session的autocommit=True:

来自官网的介绍:

意思就是为了保证事务的串行执行,而启用的一个锁,这个锁只会在事务结束的时候释放,因此在事务提交或回滚钱,任何对这个表做的DDL *** 作,都是会阻塞的

这个 Metadata lock 是MySQL在5.5.3版本后引入了,为的是防止5.5.3以前的一个bug的出现:

当一个会话在主库执行DML *** 作还没提交时,另一个会话对同一个对象执行了DDL *** 作如drop table,而由于MySQL的binlog是基于事务提交的先后顺序进行记录的,因此在从库上应用时,就出现Q了先drop table,然后再向table中insert的情况,导致从库应用出错。

表对象缓存: 是将某个表对象的字典信息(定义内容)缓存到内存中,用来提高对表的访问效率。某个表被访问过一次后,只要服务器没有关闭且表定义没有被修改的条件下,访问该表,只需要从内存中找到这个已经缓存起来的对象做相应 *** 作即可。

用户访问表时,表对象在缓存时: 通过HASH算法找到TABLE_SHARE,然后每个线程构造各自的实例化TABLE即可。

用户访问表时,当表没有被缓存的情况下: 第一需要打开表,首先需要从系统表中将这个表的所有信息都读入内存中,这些信息包括表名、库名、所有列信息、列的默认值、表的字符集、对应的frm文件路径、所属存储引擎、主键等,将这些信息构造一个TABLE_SHARE结构体,这个结构体是表对象缓存的第一层,所有用户共享访问且为静态不允许修改,它是缓存在table_def_cache(由参数table_definition_cache控制)中的。

而真正与用户打交道的是TABLE_SHARE的衍生品,它对应结构体为TABLE,在被使用前需要将TABLE_SHARE结构体实例化TABLE才能被使用,由每个线程构造各自的实例化TABLE即可。(实例化的TABLE由table_open_cache及table_open_cache_instance控制)

注意1: DDL *** 作时会将所有instance锁住,而DML *** 作时instance之间互不干扰。

(DDL statements still require a lock on the entire cache, but such statements are much less frequent than DML statements.)

注意2: 一个线程中如果打开表过多,超过一个instance限制的大小时,是不能跨instance缓存的

(instance大小:table_open_cache / table_open_cache_instances)

表缓存涉及其他参数: 通过下面参数判断table_open_cache参数设置是否合理

table_open_cache_hit:能够从table open cache的free list中找到table则为命中,+1

table_open_cache_misses:与table_open_cache_hit相反,如果找不到则需要重新实例化则+1,通常发生在初始化第一次加载表或超过table_open_cache的设置被淘汰后需要重新实例化。

table_open_cache_overflow:table cache淘汰的数量,每次淘汰+1

opened_tables:已经打开的表数。如果Opened_tables很大,那么table_open_cache的值可能太小了。

open_tables:总的instance (table cache)的总数


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/8337960.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-15
下一篇 2023-04-15

发表评论

登录后才能评论

评论列表(0条)

保存