mysql 5.6的gtid_mode_[MySQL 5.6] GTID实现、运维变化及存在的bug

本文的主要目的是记下跟gtid相关的backtrace,用于以后的问题排查。另外也会讨论目前在MySQL5.6.11版本中存在的bug。

前言:什么是GTID

什么是GTID呢, 简而言之,就是全局事务ID(global transaction identifier ),最初由google实现,官方MySQL在5.6才加入该功能,本文的起因在于5.6引入一大堆的gtid相关变量,深感困惑。

去年年中的时候,也写过一片简短的博客,大致介绍了下gtid是什么,http://mysqllover.com/?p=87。本文也不打算太多文字的介绍,因为网络上已经有大量的类似文章。

GTID的格式类似于:

7a07cd08-ac1b-11e2-9fcf-0010184e9e08:1

这是在我的一台服务器上生成的gtid记录,它在binlog中表现的事件类型就是:

GTID_LOG_EVENT:用于表示随后的事务的GTID

另外还有两种类型的GTID事件:

ANONYMOUS_GTID_LOG_EVENT :匿名GTID事件类型(暂且不论)

PREVIOUS_GTIDS_LOG_EVENT: 用于表示当前binlog文件之前已经执行过的GTID集合,记录在Binlog文件头,例如:

# at 120#130502 23:23:27 server id 119821  end_log_pos 231 CRC32 0x4f33bb48     Previous-GTIDs# 10a27632-a909-11e2-8bc7-0010184e9e08:1,# 7a07cd08-ac1b-11e2-9fcf-0010184e9e08:1-1129

这个字符串,用“:”分开,前面表示这个服务器的server_uuid,这是一个128位的随机字符串,在第一次启动时生成(函数generate_server_uuid),对应的variables是只读变量server_uuid。 它能以极高的概率保证全局唯一性,并存到文件DATA/auto.cnf中。因此要注意保护这个文件不要被删除或修改,不然就麻烦了。

第二部分是一个自增的事务ID号,事务id号+server_uuid来唯一标示一个事务。

除了单独的GTID外,还有一个GTID SET的概念。一个GTID SET的表示类似于:

7a07cd08-ac1b-11e2-9fcf-0010184e9e08:1-31

GTID_EXECUTED和GTID_PURGED是典型的GTID SET类型变量;在一个复制拓扑中,GTID_EXECUTED 可能包含好几组数据,例如:

mysql> show global variables like ‘%gtid_executed%’\G

*************************** 1. row ***************************

Variable_name: gtid_executed

Value: 10a27632-a909-11e2-8bc7-0010184e9e08:1-4,

153c0406-a909-11e2-8bc7-0010184e9e08:1-3,

7a07cd08-ac1b-11e2-9fcf-0010184e9e08:1-31,

f914fb74-a908-11e2-8bc6-0010184e9e08:1

本文讨论的内容包括:

一.主库上的gtid产生及记录

二.备库如何使用GTID复制

三.主备运维的变化

四.MySQL5.6.11存在的bug

一、主库上的Gtid

a.相关变量

主库上每个事务的Gtid包括变化的部分和不变的部分。在讨论之前,要弄清楚GTID维护的四个变量:

GTID_PURGED:已经被删除的binlog的事务,它是GTID_EXECUTED的子集,从MySQL5.6.9开始,该变量无法被设置。

GTID_OWNED:  表示正在执行的事务的gtid以及对应的线程ID。

例如如下:

mysql> show global variables like ‘%gtid_owned%’\G

*************************** 1. row ***************************

Variable_name: gtid_owned

Value: 7a07cd08-ac1b-11e2-9fcf-0010184e9e08:11560057#67:11560038#89:11560059#7:11560034#32:11560053#56:11560052#112:11560055#128:11560054#65:11559997#96:11560056#90:11560051#85:11560058#39:11560061#12:11560060#125:11560035#62:11560062#5

1 row in set (0.01 sec)

GTID_EXECUTED表示已经在该实例上执行过的事务; 执行RESET MASTER 会将该变量置空; 我们还可以通过设置GTID_NEXT执行一个空事务,来影响GTID_EXECUTED

GTID_NEXT是SESSION级别变量,表示下一个将被使用的GTID

在内存中也维护了与GTID_PURGED, GTID_OWNED, GTID_EXECUTED相对应的全局对象gtid_state。

gtid_state中维护了三个集合,其中logged_gtids对应GTID_EXECUTED, lost_gtids对应GTID_PURGED,owned_gtids对应GTID_OWNED

b.如何分配和使用GTID

在主库执行一个事务的过程中,关于Gtid主要涉及到以下几个部分:

事务开始,执行第一条SQL时,在写入第一个“BEGIN” 的QUERY EVENT 之前, 为binlog cache 的Group_cache中分配一个group(Group_cache::add_logged_group),并写入一个Gtid_log_event,此时并未为其分配事务id,backtrace 如下:

handler::ha_write_row->binlog_log_row->write_locked_table_maps->THD::binlog_write_table_map->binlog_start_trans_and_stmt->binlog_cache_data::write_event->Group_cache::add_logged_group

暂时还不清楚什么时候一个事务里会有多个gtid的group_cache.

在binlog group commit的flush阶段:

第一步,调用Group_cache::generate_automatic_gno来为当前线程生成一个gtid,分配给thd->owned_gtid,并加入到owned_gtids中,backtrace如下:

MYSQL_BIN_LOG::process_flush_stage_queue->MYSQL_BIN_LOG::flush_thread_caches->binlog_cache_mngr::flush->binlog_cache_data::flush->gtid_before_write_cache->Group_cache::generate_automatic_gno->Gtid_state::acquire_ownership->Owned_gtids::add_gtid_owner

也就是说,直到事务完成,准备把binlog刷到binlog cache时,才会去为其分配gtid.

当gtid_next的类型为AUTOMATIC时,调用generate_automatic_gno生成事务id(gno),分配流程大概如下:

1.gtid_state->lock_sidno(automatic_gtid.sidno) , 为当前sidno加锁,分配过程互斥

2.gtid_state->get_automatic_gno(automatic_gtid.sidno); 获取事务ID

|–>初始化候选(candidate)gno为1

|–>从logged_gtids[$sidno]中扫描,获取每个gno区间(iv):

|–>当candidate < iv->start(或者MAX_GNO,如果iv为NULL)时,判断candidate是否有被占用,如果没有的话,则使用该candidate,从函数返回,否则candidate++,继续本步骤

|–>将candidate设置为iv->end,iv指向下一个区间,继续第二步

从该过程可以看出,这里兼顾了区间存在碎片的场景,有可能分配的gno并不是全局最大的gno. 不过在主库不手动设置gtid_next的情况下,我们可以认为主库上的gno总是递增的。

3.gtid_state->acquire_ownership(thd, automatic_gtid);

|–>加入到owned_gtids集合中(owned_gtids.add_gtid_owner),并赋值给thd->owned_gtid= gtid

4.gtid_state->unlock_sidno(automatic_gtid.sidno);  解锁

第二步, 调用Gtid_state::update_on_flush将当前事务的grid加入到logged_gtids中,backtrace如下:

MYSQL_BIN_LOG::process_flush_stage_queue->MYSQL_BIN_LOG::flush_thread_caches->binlog_cache_mngr::flush->binlog_cache_data::flush->MYSQL_BIN_LOG::write_cache->Gtid_state::update_on_flush

在bin log group commit的commit阶段

调用Gtid_state::update_owned_gtids_impl 从owned_gtids中将当前事务的gtid移除,backtrace 如下:

MYSQL_BIN_LOG::ordered_commit->MYSQL_BIN_LOG::finish_commit->Gtid_state::update_owned_gtids_impl

上述步骤涉及到的是对logged_gtids和owned_gtids的修改。而lost_gtids除了启动时维护外,就是在执行Purge操作时维护。

例如,当我们执行purge binary logs to ‘mysql-bin.000205′ 时, mysql-bin.index先被更新掉,然后再根据index文件找到第一个binlog文件的PREVIOUS_GTIDS_LOG_EVENT事件,更新lost_gtids集合,backtrace如下:

purge_master_logs->MYSQL_BIN_LOG::purge_logs->MYSQL_BIN_LOG::init_gtid_sets->read_gtids_from_binlog->Previous_gtids_log_event::add_to_set->Gtid_set::add_gtid_encoding->Gtid_set::add_gno_interval

关于binlog group commit,参见之前写的博客:

c.如何持久化GTID

当重启MySQL后,我们看到GTID_EXECUTED和GTID_PURGED和重启前是一致的。

持久化GTID,是通过全局对象gtid_state来管理的。gtid_state在系统启动时调用函数gtid_server_init分配内存;如果打开了binlog,则会做进一步的初始化工作:

quoted code:

5419       if (mysql_bin_log.init_gtid_sets(

5420             const_cast(gtid_state->get_logged_gtids()),

5421             const_cast(gtid_state->get_lost_gtids()),

5422             opt_master_verify_checksum,

5423             true/*true=need lock*/))

5424         unireg_abort(1);

gtid_state 包含3个gtid集合:logged_gtids, lost_gtids, owned_gtids,前两个都是gtid_set类型, owned_gtids类型为Owned_gtids

MYSQL_BIN_LOG::init_gtid_sets 主要用于初始化logged_gtids和lost_gtids,该函数的逻辑简单描述下:

1.扫描mysql-index文件,搜集binlog文件名,并加入到filename_list中

2.从最后一个文件开始往前读,依次调用函数read_gtids_from_binlog:

|–>打开binlog文件,如果读取到PREVIOUS_GTIDS_LOG_EVENT事件

(1)无论如何,将其加入到logged_gtids(prev_gtids_ev->add_to_set(all_gtids))

(2)如果该文件是第一个binlog文件,将其加入到lost_gtids(prev_gtids_ev->add_to_set(prev_gtids))中.

|–>获取GTID_LOG_EVENT事件

(1) 读取该事件对应的sidno,sidno= gtid_ev->get_sidno(false);

这是一个32位的整型,用sidno来代表一个server_uuid,从1开始计算,这主要处于节省内存的考虑。维护在全局对象global_sid_map中。

当sidno还没加入到map时,调用global_sid_map->add_sid(sid),sidno从1开始递增。

(2) all_gtids->ensure_sidno(sidno)

all_gtids是gtid_set类型,可以理解为一个集合,ensure_sidno就是要确保这个集合至少可以容纳sidno个元素

(3) all_gtids->_add_gtid(sidno, gtid_ev->get_gno()

将该事件中记录的gtid加到all_gtids[sidno]中(最终调用Gtid_set::add_gno_interval,这里实际上是把(gno, gno+1)这样一个区间加入到其中,这里

面涉及到区间合并,交集等操作    )

当第一个文件中既没有PREVIOUS_GTIDS_LOG_EVENT, 也没有GTID_LOG_EVENT时,就继续读上一个文件

如果只存在PREVIOUS_GTIDS_LOG_EVENT事件,函数read_gtids_from_binlog返回GOT_PREVIOUS_GTIDS

如果还存在GTID_LOG_EVENT事件,返回GOT_GTIDS

这里很显然存在一个问题,即如果在重启前,我们并没有使用gtid_mode,并且产生了大量的binlog,在这次重启后,我们就可能需要扫描大量的binlog文件。这是一个非常明显的Bug, 后面再集中讨论。

3.如果第二部扫描,没有到达第一个文件,那么就从第一个文件开始扫描,和第2步流程类似,读取到第一个PREVIOUS_GTIDS_LOG_EVENT事件,并加入到lost_gtids中。

简单的讲,如果我们一直打开的gtid_mode,那么只需要读取第一个binlog文件和最后一个binlog文件,就可以确定logged_gtids和lost_gtids这两个GTID SET了。

二、备库上的GTID

a.如何保持主备GTID一致

由于在binlog中记录了每个事务的GTID,因此备库的复制线程可以通过设置线程级别GTID_NEXT来保证主库和备库的GTID一致。

默认情况下,主库上的thd->variables.gtid_next.type为AUTOMATIC_GROUP,而备库为GTID_GROUP

备库SQL线程gtid_next输出:

(gdb) p thd->variables.gtid_next$2 = {type = GTID_GROUP,gtid = {sidno = 2,gno = 1127,static MAX_TEXT_LENGTH = 56},static MAX_TEXT_LENGTH = 56}

C/C++ 中的 gdb 也是一个类似的命令行 debugger,只是用来调试 C/C++ 而已,使用的模式跟Python的pdb/ipdb相似,具体可参考 用GDB调试程序。

这些变量在执行Gtid_log_event时被赋值:Gtid_log_event::do_apply_event,大体流程为:

1.rpl_sidno sidno= get_sidno(true);  获取sidno

2.thd->variables.gtid_next.set(sidno, spec.gtid.gno);  设置gtid_next

3.gtid_acquire_ownership_single(thd);

|–>检查该gtid是否在logged_gtids集合中,如果在的话,则返回(gtid_pre_statement_checks会忽略该事务)

|–>如果该gtid已经被其他线程拥有,则等待(gtid_state->wait_for_gtid(thd, gtid_next)),否则将当前线程设置为owner(gtid_state->acquire_ownership(thd, gtid_next))

在上面提到,有可能当前事务的GTID已经在logged_gtids中,因此在执行Rows_log_event::do_apply_event或者mysql_execute_command函数中,都会去调用函数gtid_pre_statement_checks

该函数也会在每个SQL执行前,检查gtid是否合法,主要流程包括:

1.当打开选项enforce_gtid_consistency时,检查DDL是否被允许执行(thd->is_ddl_gtid_compatible()),若不允许,返回GTID_STATEMENT_CANCEL

2.检查当前SQL是否会产生隐式提交并且gtid_next被设置(gtid_next->type != AUTOMATIC_GROUP),如果是的话,则会抛出错误ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET 并返回GTID_STATEMENT_CANCEL,注意这里会导致bug#69045

3.对于BEGIN/COMMIT/ROLLBACK/(SET OPTION 或者 SELECT )且没有使用存储过程/ 这几种类型的SQL,总是允许执行,返回GTID_STATEMENT_EXECUTE

4.gtid_next->type为UNDEFINED_GROUP,抛出错误ER_GTID_NEXT_TYPE_UNDEFINED_GROUP,返回GTID_STATEMENT_CANCEL

5.gtid_next->type == GTID_GROUP且thd->owned_gtid.sidno == 0时, 返回GTID_STATEMENT_SKIP

其中第五步中处理了函数gtid_acquire_ownership_single的特殊情况

b.备库如何发起DUMP请求

引入GTID,最大的好处当然是我们可以随心所欲的切换主备拓扑结构了。在一个正常运行的复制结构中,我们可以在备库简单的执行如下SQL:

CHANGE MASTER TO MASTER_USER=’$USERNAME’, MASTER_HOST=’ ‘, MASTER_PORT=’ ‘, MASTER_AUTO_POSITION=1;

打开GTID后,我们就无需指定binlog文件或者位置,MySQL会自动为我们做这些事情。这里的关键就是MASTER_AUTO_POSITION。IO线程连接主库,可以大概分为以下几步:

1.IO线程在和主库建立TCP链接后,会去获取主库的uuid(get_master_uuid),然后在主库上设置一个用户变量@slave_uuid(io_thread_init_commands)

2.之后,在主库上注册SLAVE(register_slave_on_master)

在主库上调用register_slave来注册备库,将备库的host,user,password,port,server_id等信息记录到slave_list哈希中。

3.调用request_dump,开始向主库请求数据,这里分两种情况:

MASTER_AUTO_POSITION=0时,向主库发送命令的类型为COM_BINLOG_DUMP,这是传统的请求BINLOG的模式

MASTER_AUTO_POSITION=1时,命令类型为COM_BINLOG_DUMP_GTID,这是新的方式。

这里我们只讨论第二种。第二种情况下,会先去读取备库已经执行的gtid集合

quoted code in rpl_slave.cc :

2974   if (command == COM_BINLOG_DUMP_GTID)

2975   {

2976     // get set of GTIDs

2977     Sid_map sid_map(NULL/*no lock needed*/);

2978     Gtid_set gtid_executed(&sid_map);

2979     global_sid_lock->wrlock();

2980     gtid_state->dbug_print();

2981     if (gtid_executed.add_gtid_set(mi->rli->get_gtid_set()) != RETURN_STATUS_OK ||

2982         gtid_executed.add_gtid_set(gtid_state->get_logged_gtids()) !=

2983         RETURN_STATUS_OK)

构建完成发送包后,发送给主库。

在主库上接受到命令后,调用入口函数com_binlog_dump_gtid,流程如下:

1.slave_gtid_executed.add_gtid_encoding(packet_position, data_size) ;读取备库传来的GTID SET

2.读取备库的uuid(get_slave_uuid),被根据uuid来kill僵尸线程(kill_zombie_dump_threads)

这也是之前SLAVE IO线程执行SET @SLAVE_UUID的用处。

3.进入mysql_binlog_send函数:

|–>调用MYSQL_BIN_LOG::find_first_log_not_in_gtid_set,从最后一个Binlog开始扫描,获取文件头部的PREVIOUS_GTIDS_LOG_EVENT,如果它是slave_gtid_executed的子集,保存当前binlog文件名,否则继续向前扫描。

这一步的目的就是为了找出备库执行到的最后一个Binlog文件。

|–>从这个文件头部开始扫描,遇到GTID_EVENT时,会去判断该GTID是否包含在slave_gtid_executed中:

Gtid_log_event gtid_ev(packet->ptr() + ev_offset,

packet->length() – checksum_size,

p_fdle);

skip_group= slave_gtid_executed->contains_gtid(gtid_ev.get_sidno(sid_map),

gtid_ev.get_gno());

主库通过GTID决定是否可以忽略事务,从而决定执行开始的位置

注意,在使用MASTER_LOG_POSITION后,就不要指定binlog的位置,否则会报错。

三、运维操作

a.如何忽略复制错误

当备库复制出错时,传统的跳过错误的方法是设置sql_slave_skip_counter,然后再START SLAVE。

但如果打开了GTID,就会设置失败:

mysql> set global sql_slave_skip_counter = 1;

ERROR 1858 (HY000): sql_slave_skip_counter can not be set when the server is running with @@GLOBAL.GTID_MODE = ON. Instead, for each transaction that you want to skip, generate an empty transaction with the same GTID as the transaction

提示的错误信息告诉我们,可以通过生成一个空事务来跳过错误的事务。

我们手动产生一个备库复制错误:

Last_SQL_Error: Error ‘Unknown table ‘test.t1” on query. Default database: ‘test’. Query: ‘DROP TABLE `t1` /* generated by server */’

查看binlog中,该DDL对应的GTID为7a07cd08-ac1b-11e2-9fcf-0010184e9e08:1131

在备库上执行:

mysql> STOP SLAVE;

Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION GTID_NEXT = ’7a07cd08-ac1b-11e2-9fcf-0010184e9e08:1131′;

Query OK, 0 rows affected (0.00 sec)

mysql> BEGIN; COMMIT;

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

mysql> SET SESSION GTID_NEXT = AUTOMATIC;

Query OK, 0 rows affected (0.00 sec)

mysql> START SLAVE;

再查看show slave status,就会发现错误事务已经被跳过了。这种方法的原理很简单,空事务产生的GTID加入到GTID_EXECUTED中,这相当于告诉备库,这个GTID对应的事务已经执行了。

b.重指主库

使用change master to …. , MASTER_AUTO_POSITION=1;

注意在整个复制拓扑中,都需要打开gtid_mode

c.新的until条件

5.6提供了新的util condition,可以根据GTID来决定备库复制执行到的位置

SQL_BEFORE_GTIDS:在指定的GTID之前停止复制

SQL_AFTER_GTIDS :在指定的GTID之后停止复制

判断函数为Relay_log_info::is_until_satisfied

d.适当减小binlog文件的大小

如果开启GTID,理论上最好调小每个binlog文件的最大值,以缩小扫描文件的时间。

四、存在的bug

bug#69097, 即使关闭了gtid_mode,也会在启动时去扫描binlog文件。

当在重启前没有使用gtid_mode,重启后可能会去扫描所有的binlog文件,如果Binlog文件很多的话,这显然是不可接受的。

bug#69096,无法通过GTID_NEXT_LIST来跳过复制错误,因为默认编译下,GTID_NEXT_LIST未被编译进去。

TODO:GTID_NEXT_LIST的逻辑上面均未提到,有空再看。

bug#69095,将备库的复制模式设置为STATEMENT/MIXED。 主库设置为ROW模式,执行DML 会导致备库复制中断

Last_SQL_Error: Error executing row event: ‘Cannot execute statement: impossible to write to binary log since statement is in row format and BINLOG_FORMAT = STATEMENT.’

判断报错的backtrace:

handle_slave_worker->slave_worker_exec_job->Rows_log_event::do_apply_event->open_and_lock_tables->open_and_lock_tables->lock_tables->THD::decide_logging_format

解决办法:将备库的复制模式设置为’ROW’ ,保持主备一致

该bug和GTID无关

bug#69045, 当主库执行类似 FLUSH PRIVILEGES这样的动作时,如果主库和备库都开启了gtid_mode,会导致复制中断

Last_SQL_Error: Error ‘Cannot execute statements with implicit commit inside a transaction when @@SESSION.GTID_NEXT != AUTOMATIC or @@SESSION.GTID_NEXT_LIST != NULL.’ on query. Default database: ”. Query: ‘flush privileges’

也是一个很低级的bug,在MySQL5.6.11版本中,如果有可能导致隐式提交的事务, 则gtid_next必须等于AUTOMATIC,对备库复制线程而言,很容易就中断了,判断逻辑在函数gtid_pre_statement_checks中

参考文档

1.阿里长源的三篇博客(一, 二, 三)

2.MySQL5.6.11源代码

http://mysql.taobao.org/monthly/2020/05/09/

GTID的生成和使用由以下几步组成:

主服务器更新数据时,会在事务前产生GTID,一同记录到binlog日志中。

binlog传送到从服务器后,被写入到本地的relay log中。从服务器读取GTID,并将其设定为自己的GTID(GTID_NEXT系统)。

sql线程从relay log中获取GTID,然后对比从服务器端的binlog是否有记录。

如果有记录,说明该GTID的事务已经执行,从服务器会忽略。

如果没有记录,从服务器就会从relay log中执行该GTID的事务,并记录到binlog。

GTID_OWNED:

表示正在执行的事务的GTID以及其对应的线程ID。

Scope : Global, Session

Dynamic : No

Type : String

如果GDIT_OWNED是全局变量,它包含所有当前服务器上正在使用的GTIDs和使用它们的线程IDs。这个变量主要用于并行复制,从而可以查看一个事务是否已经被另一个线程处理。这个线程会拥有所处理事务的ownership。@@global.grid_owned会显示出GTID和它的owner。当事务处理完成,线程会释放ownership. 如果GDIT_OWNED是session变量,它包含一个seesion正在使用的GTID。这个变量对测试和debug会很有帮助

gtid在各个mysql节点的binlog里面都是全局唯一

f

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/487549.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

bzoj1018 [SHOI2008]堵塞的交通traffic

题目链接 分析&#xff1a; 这道题的题解很长&#xff0c;所以就不粘题面了&#xff0c;我们一点一点讲明白这道题 很荣幸&#xff0c;我看了题面之后 想到了这道题 可以很高兴的发现10w是线段树能够承受的范围 我们可以利用线段树维护连通性&#xff0c;每个节点内我们要维…

Science重磅!人类特有基因触发猴子长出更强大的大脑

本文系生物谷原创编译&#xff0c;欢迎分享&#xff0c;转载须授权&#xff01;人类大脑在进化过程中的扩张&#xff0c;特别是新大脑皮层的扩张&#xff0c;与诸如推理和语言等认知能力有关。有一种叫做ARHGAP11B的基因&#xff0c;只在人类身上表达&#xff0c;它能触发大脑干…

mysql不同的类的个数_Mysql数据库-SQL优化-统计某种类型的个数

有时我们想统计某种类型有多少个&#xff0c;会用这个SQL。全表扫描之余&#xff0c;还要filesort&#xff0c;耗时1.34秒。mysql> select country,count(*) from t1 group by country;-------------------| country | count(*) |-------------------| NULL | 32 || africa …

『实践』Matlab实现Flyod求最短距离及存储最优路径

Matlab实现Flyod求最短距离及存储最优路径 一、实际数据 已知图中所有节点的X、Y坐标。 图中的节点编号&#xff1a;矩阵中的编号 J01-J62:1-62; F01-F60:63-122; Z01-Z06:123-128; D01-D02:129-130. 二、Floyd求所有节点间的最小距离及通过矩阵存储最优路径的节点 1 function …

MIT Technology Review 2020年“十大突破性技术”解读 【中国科学基金】2020年第3期发布...

来源&#xff1a;国家自然科学基金委员会MIT Technology Review 2020年“十大突破性技术”解读&#xff3b;编者按&#xff3d; 2020年2月26日&#xff0c;MIT Technology Review一年一度的“十大突破性技术”榜单正式发布。自2001年起&#xff0c;该杂志每年都会评选出当年的…

动态代理Java实现

思考&#xff1a;在IBuyWatermelon添加一个方法selectWatermelon() 静态代理中需要在RealSubject中实现该方法&#xff0c;而且Proxy也要实现该方法调用RealSubject中的实现&#xff0c;如果再增加10个方法还是得这样操作&#xff0c;导致大量的代码重复。 现在来看动态代理&am…

mysql 非等值条件 索引_慢SQL简述与定位

慢SQL日志简述通过命令和查看日志文件的方式直接查看mysql服务器的慢sql参数配置参数作用slow_query_log是否启用slow_query_log_file日志文件long_query_time慢sql阈值log_slow_adimin_statements是否记录数据库管理相关的sqllong_queries_not_using_indexes是否记录未使用索引…

大数据是怎么知道你去过新发地的?

来源&#xff1a;科学加&#xff08;北京科技报记者&#xff1a;赵天宇&#xff09;“经过全市大数据分析&#xff0c;您可能在5月30日(含)以后去过新发地批发市场……”随着新发地市场新冠源头被锁定&#xff0c;近日来&#xff0c;大数据筛查&#xff0c;成为不少北京市民在朋…

[模板]洛谷T3379 最近公共祖先(LCA) 倍增+邻接表

一年前听说的这东西。。。现在终于会了。。。 1 #include<cstdio>2 #include<iostream>3 #include<cstring>4 #include<cmath>5 #include<ctime>6 #include<cstdlib>7 8 #include<string>9 #include<stack>10 #include<que…

mysql更新数据能回滚吗_MySQL数据回滚-误更新和删除时快速恢复

这世界上有后悔药– www.houhuiyao.cc 后悔药数据恢复 站长语前面的内容也提到过update或delete误更新删除了数据后如何恢复。实际生产环境中常常因各种不同场景导致一些办法有效一些办法无效&#xff0c;当然&#xff0c;最有效的办法依然是备份&#xff01;虽然啰嗦&#xff…

口罩巨头挑战“量子霸权”,3个月造出的『最强量子计算机』靠谱不?

摘要霍尼韦尔(Honeywell)最近可是非常忙活。作为全球数一数二的口罩厂商&#xff0c;疫情期间霍尼韦尔一直在努力生产口罩&#xff0c;大家对它的印象也多停留在口罩生产者的层面。但是事实上&#xff0c;人家是正经的多元化高科技制造企业&#xff0c;航空产品、汽车产品、涡轮…

三极管稳压管组成的线性电源关键理解

1. A点电压为20V.B点电压是稳压管电压5.6V.则A到B的电流是固定的。 2. 加电后在调整三极管&#xff0c;负载&#xff0c; 稳压管的电流回路如图。&#xff08;注意电流方向&#xff0c;a->b电流方向和大小都是基本不变的。&#xff09; 转载于:https://www.cnblogs.com/ture…

oci连接mysql_使用 OCILIB 连接并操作 Oracle 数据库

OCILIB是一个跨平台的Oracle驱动程序&#xff0c;可提供非常快速和可靠地访问Oracle数据库。它提供了一个丰富&#xff0c;功能齐全&#xff0c;并易于使用的APIOCILIB是一个跨平台的Oracle驱动程序&#xff0c;&#xff0c;可提供非常快速和可靠地访问Oracle数据库。它提供了一…

万字长文:2020智源大会总结-多角度看AI的未来

来源&#xff1a;混沌巡洋舰导读&#xff1a;智源大会2020 聚焦AI的未来&#xff0c;大家都知道&#xff0c; 2010是深度学习的时代&#xff0c; 我们经历了AI从巅峰进入到瓶颈的过程。那么什么是2020这个十年AI的中心&#xff1f;近一段大家逐步从各个不同的角度切入到这个主题…

python OCR 图形识别

1、pip install pyocr 2、pip install PIL 3、安装tesseract-ocr http://jaist.dl.sourceforge.net/project/tesseract-ocr-alt/tesseract-ocr-setup-3.02.02.exe exe文件&#xff0c;下载后直接安装&#xff0c;建议默认安装过程中的选项&#xff0c;安装目录默认C:\Program F…

python生成文件夹并向文件夹写文件_python - 文件练习生成100个MAC地址写入文件

需求&#xff1a;生成100个MAC地址并写入文件中&#xff0c;MAC地址前6位(16进制)为01-AF-3B解题思路&#xff1a;要求生成这样格式的mac地址&#xff1a;01-AF-3B-xx-xx-xx首先生成-xx格式&#xff0c;16进制组成的数,注意MAC英文字母为大写。拼接成为&#xff1a;01-AF-3B-xx…

智能发展与双重建构

来源&#xff1a; 人机与认知实验室本文摘自1983.5《国内哲学动态》皮亚杰有关同化和顺应及其平衡的图式学说是对智慧机制的一种结构主义的共时性分析,但是他更重视对智慧机制进行建构主义的历时性研究。主体关于客体的知识以及主体自身的认识图式都不仅仅是一种状态体系 , 而首…

2017.09.21学习总结

两天的时间主要学习了html的基本结构&#xff0c;标签&#xff0c;列表以及表格。 表格方面<td><tr>总是弄混&#xff1b; 合并用起来有些眼花&#xff0c;不过仔细的研究一下还是没什么问题。 加油&#xff01;努力&#xff01;目标&#xff1a;资深码农~~转载于:…

springboot2整合mysql5_SpringBoot2整合SSM框架详解

SpringBoot2整合SSM框架详解发布时间&#xff1a;2019-01-15 21:33,浏览次数&#xff1a;1218, 标签&#xff1a;SpringBootSSM<>开发环境* 开发工具&#xff1a;Eclipse STS插件* JDK版本&#xff1a;9.0.4* MySQL版本&#xff1a;8.0.12* Spring Boot版本&#xff1a;…

前途无量的MEMS传感器

来源&#xff1a;转载自「民生证券」&#xff0c;谢谢微机电系统&#xff08;Microelectromechanical Systems&#xff0c;简称 MEMS&#xff09;是将微电子技术与精密机械技术结合发展出来的工程技术&#xff0c;尺寸在 1 微米到 100 微米量级&#xff0c;涵盖机械&#xff08…