1.MySQL最新动态是码调否不再开源mysql不开源了么
2.如何杀掉空闲事务
MySQL最新动态是否不再开源mysql不开源了么
MySQL最新动态:是否不再开源?
MySQL一直以来都是开源数据库领域中的佼佼者,但近期一些传言称MySQL可能不再开源,码调引起了众多开发者的码调担忧和关注。那么,码调MySQL是码调否真的不再开源了呢?
我们需要了解MySQL的历史和现状。MySQL最初由瑞典MySQL AB公司开发,码调直播聚合app源码年被Sun Microsystems(后来被Oracle收购)收购。码调由于Sun Microsystems当时是码调一个开源软件的倡导者,MySQL在被收购后依旧保持了开源的码调身份,并继续在开源社区中发展壮大。码调然而,码调年Oracle收购了Sun Microsystems,码调MySQL也成为了Oracle的码调财产。
虽然Oracle同样也是码调一个开源支持者,但MySQL在Oracle的码调exceljs 源码管理下不再是一个独立的开源项目,而是成为了Oracle旗下的商业产品。由于Oracle拥有MySQL的知识产权,因此Oracle也有权利将MySQL的许可证更改为其他类型的许可证,比如闭源许可证。
因此,一些人认为MySQL可能不再开源,但实际上,MySQL至今依旧是一个开源项目。MySQL使用的是双许可证,分为GPL和商业许可证两种类型。GPL许可证意味着MySQL的源码是开源的,可以自由修改和分发,但是如果将MySQL源码嵌入一个软件中发布,则该软件也必须采用GPL许可证。mysql 源码而商业许可证则可以让用户使用MySQL的源码构建闭源软件。
虽然MySQL的源码依旧是开源的,但由于Oracle掌握着MySQL的知识产权,因此Oracle可以限制MySQL的发展方向。Oracle可以在自己的商业产品中加强MySQL的集成,但同时限制其与其他开源软件的集成,这就导致了MySQL开发社区的不满。此外,Oracle还限制了MySQL的名字和商标的使用,这也对MySQL的市场推广带来了一些阻碍。
虽然MySQL受到了一些限制,但仍然被广泛使用和支持。MySQL在全球拥有大量的用户和开发者社区,从而使MySQL Documentation、eoyoocms 源码MySQL Development、MySQL Bugs等MySQL官方网站十分活跃。同时,MySQL也拥有众多的开源项目和衍生版本,比如MariaDB和Percona等,这些衍生版本都吸收了MySQL社区贡献的代码和特性,积极推进MySQL技术的发展。
MySQL虽然在Oracle的掌管下有了一些改变,但它始终是一个受欢迎的开源项目。MySQL的源码依旧是开源的,而它的市场地位也越来越坚实。当然,我们不能忽视对MySQL的官方支持和维护,在MySQL发展过程中,preparedstatement 源码MySQL的使用者和开发者要与MySQL官方密切合作,共同推动MySQL的发展。
如何杀掉空闲事务
本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: /tech/database/how_to_kill_idle_trx.html 我们经常遇到一个情况,就是网络断开或程序Bug导致COMMIT/ROLLBACK语句没有传到数
本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明网址: /tech/database/how_to_kill_idle_trx.html
我们经常遇到一个情况,就是网络断开或程序Bug导致COMMIT/ROLLBACK语句没有传到数据库,也没有释放线程,但是线上事务锁定等待严重,连接数暴涨,尤其在测试库这种情况很多,线上也偶有发生,于是想为MySQL增加一个杀掉空闲事务的功能。
那么如何实现呢,通过MySQL Server层有很多不确定因素,最保险还是在存储引擎层实现,我们用的几乎都是InnoDB/XtraDB,所以就基于Percona来修改了,Oracle版的MySQL也可以照着修改。
需求:
1. 一个事务启动,如果事务内最后一个语句执行完超过一个时间(innodb_idle_trx_timeout),就应该关闭链接。
2. 如果事务是纯读事务,因为不加锁,所以无害,不需要关闭,保持即可。
虽然这个思路被Percona的指出Alexey Kopytov可能存在“Even though SELECT queries do not place row locks by default (there are exceptions), they can still block undo log records from being purged.”的问题,但是我们确实有场景SELECT是绝对不能kill的,除非之后的INSERT/UPDATE/DELETE发生了,所以我根据我们的业务特点来修改。
跟Percona的Yasufumi Kinoshita和Alexey Kopytov提出过纯SELECT事务不应被kill,但通过一个参数控制的方案还没有被Alexey Kopytov接受,作为通用处理我提出了用两个变量分别控制纯读事务的空闲超时时间和有锁事务的空闲超时时间,还在等待Percona的回复,因为这个方案还在测试,就先不开放修改了,当然如果你很熟悉MYSQL源码,我提出这个思路你肯定知道怎么分成这两个参数控制了。
根据这两个需求我们来设计方法,首先想到这个功能肯定是放在InnoDB Master Thread最方便,Master Thread每秒调度一次,可以顺便检查空闲事务,然后关闭,因为在事务中操作trx->mysql_thd并不安全,所以一般来说最好在InnoDB层换成Thread ID操作,并且InnoDB中除了ha_innodb.cc,其他地方不能饮用THD,所以Master Thread中需要的线程数值,都需要在ha_innodb中计算好传递整型或布尔型返回值给master thread调用。
首先,我们要增加一个参数:idle_trx_timeout,它表示事务多久没有下一条语句发生就超时关闭。
在storage/innodb_plugin/srv/srv0srv.c的“/* plugin options */”注释下增加如下代码注册idle_trx_timeout变量。
static MYSQL_SYSVAR_LONG(idle_trx_timeout, srv_idle_trx_timeout,
PLUGIN_VAR_RQCMDARG,
"If zero then this function no effect, if no-zero then wait idle_trx_timeout seconds this transaction will be closed",
"Seconds of Idle-Transaction timeout",
NULL, NULL, 0, 0, LONG_MAX, 0);
代码往下找在innobase_system_variables结构体内加上:
MYSQL_SYSVAR(idle_trx_timeout),
有了这个变量,我们需要在Master Thread(storage/innodb_plugin/srv/srv0srv.c )中执行检测函数查找空闲事务。在loop循环的if (sync_array_print_long_waits(&waiter, &sema)判断后加上这段判断
if (srv_idle_trx_timeout && trx_sys) {
trx_t* trx;
time_t now;
rescan_idle:
now = time(NULL);
mutex_enter(&kernel_mutex);
trx = UT_LIST_GET_FIRST(trx_sys->mysql_trx_list); # 从当前事务列表里获取第一个事务
while (trx) { # 依次循环每个事务进行检查
if (trx->conc_state == TRX_ACTIVE
&& trx->mysql_thd
&& innobase_thd_is_idle(trx->mysql_thd)) { # 如果事务还活着并且它的状态时空闲的
ib_int_t start_time = innobase_thd_get_start_time(trx->mysql_thd); # 获取线程最后一个语句的开始时间
ulong thd_id = innobase_thd_get_thread_id(trx->mysql_thd); #获取线程ID,因为存储引擎内直接操作THD不安全
if (trx->last_stmt_start != start_time) { # 如果事务最后语句起始时间不等于线程最后语句起始时间说明事务是新起的
trx->idle_start = now; # 更新事务的空闲起始时间
trx->last_stmt_start = start_time; # 更新事务的最后语句起始时间
} else if (difftime(now, trx->idle_start) # 如果事务不是新起的,已经执行了一部分则判断空闲时间有多长了
> srv_idle_trx_timeout) { # 如果空闲时间超过阈值则杀掉链接
/* kill the session */
mutex_exit(&kernel_mutex);
thd_kill(thd_id); # 杀链接
goto rescan_idle;
}
}
trx = UT_LIST_GET_NEXT(mysql_trx_list, trx); # 检查下一个事务
}
mutex_exit(&kernel_mutex);
}
其中trx中的变量是新加的,在storage/innodb_plugin/include/trx0trx.h的trx_truct加上需要的变量:
struct trx_struct{
...
time_t idle_start;
ib_int_t last_stmt_start;
...
}
这里有几个函数是自定义的:
ibool innobase_thd_is_idle(const void* thd);
ib_int_t innobase_thd_get_start_time(const void* thd);
ulong innobase_thd_get_thread_id(const void* thd);
这些函数在ha_innodb.cc中实现,需要在storage/innodb_plugin/srv/srv0srv.c头文件定义下加上这些函数的引用形势。
然后在storage/innodb_plugin/handler/ha_innodb.cc 中定义这些函数的实现:
extern "C"
ibool
innobase_thd_is_idle(
const void* thd) /*!{
return(((const THD*)thd)->command == COM_SLEEP);
}
extern "C"
ib_int_t
innobase_thd_get_start_time(
const void* thd) /*!{
return((ib_int_t)((const THD*)thd)->start_time);
}
extern "C"
ulong
innobase_thd_get_thread_id(
const void* thd)
{
return(thd_get_thread_id((const THD*) thd));
}
还有最重要的thd_kill函数负责杀线程的,在sql/sql_class.cc中,找个地方定义这个函数:
void thd_kill(ulong id)
{
THD *tmp;
VOID(pthread_mutex_lock(&LOCK_thread_count));
I_List_iterator it(threads);
while ((tmp=it++))
{
if (tmp->command == COM_DAEMON || tmp->is_have_lock_thd == 0 ) # 如果是DAEMON线程和不含锁的线程就不要kill了
continue;
if (tmp->thread_id == id)
{
pthread_mutex_lock(&tmp->LOCK_thd_data);
break;
}
}
VOID(pthread_mutex_unlock(&LOCK_thread_count));
if (tmp)
{
tmp->awake(THD::KILL_CONNECTION);
pthread_mutex_unlock(&tmp->LOCK_thd_data);
}
}
为了存储引擎能引用到这个函数,我们要把它定义到plugin中:
include/mysql/plugin.h和include/mysql/plugin.h中加上
void thd_kill(unsigned long id);
如何判定线程的is_have_lock_thd值?首先在THD中加上这个变量(sql/sql_class.cc):
class THD :public Statement,
public Open_tables_state
{
....
uint is_have_lock_thd;
....
}
然后在SQL的必经之路mysql_execute_command拦上一刀,判断是有锁操作发生了还是事务提交或新起事务。
switch (lex->sql_command) {
case SQLCOM_REPLACE:
case SQLCOM_REPLACE_SELECT:
case SQLCOM_UPDATE:
case SQLCOM_UPDATE_MULTI:
case SQLCOM_DELETE:
case SQLCOM_DELETE_MULTI:
case SQLCOM_INSERT:
case SQLCOM_INSERT_SELECT:
thd->is_have_lock_thd = 1;
break;
case SQLCOM_COMMIT:
case SQLCOM_ROLLBACK:
case SQLCOM_XA_START:
case SQLCOM_XA_END:
case SQLCOM_XA_PREPARE:
case SQLCOM_XA_COMMIT:
case SQLCOM_XA_ROLLBACK:
case SQLCOM_XA_RECOVER:
thd->is_have_lock_thd = 0;
break;
}
为了尽可能兼容Percona的补丁,能引用的都引用了Percona的操作,有些函数调用是在层次太多看不下去了就简化了。
另外还有一个版本是我自己弄的,在THD中增加了一个last_sql_end_time,在do_command结束后更新last_sql_end_time,然后在事务中拿到THD查看last_sql_end_time就可以得出idle时间,Oracle版我还是建议这么做,不要去改trx_struct结构体了,那个感觉更危险。