深入解读MySQL8.0 新特性 :Crash Safe DDL

前言

在MySQL8.0之前的版本中,由于架构的原因,mysql在server层使用统一的frm文件来存储表元数据信息,这个信息能够被不同的存储引擎识别。而实际上innodb本身也存储有元数据信息。这给ddl带来了一定的挑战,因为这种架构无法做到ddl的原子化,我们在线上经常能够看到数据目录下遗留的临时文件,或者类似server层和innodb层列个数不一致之类的错误。甚至某些ddl可能还遗留元数据在innodb内,而丢失了frm,导致无法重建表…..(我们为了解决这个问题,实现了一个叫drop table force的功能,去强制做清理….)

(以下所有的讨论都假定使用InnoDB存储引擎)

到了8.0版本,我们知道所有的元数据已经统一用InnoDB来进行管理,这就给实现原子ddl带来了可能,几乎所有的对innodb表,存储过程,触发器,视图或者UDF的操作,都能做到原子化:

- 元数据修改,binlog以及innodb的操作都放在一个事务中
- 增加了一个内部隐藏的系统表`mysql.innodb_ddl_log`,ddl操作被记录到这个表中,注意对该表的操作产生的redo会fsync到磁盘上,而不会考虑innodb_flush_log_at_trx_commit的配置。当崩溃重启时,会根据事务是否提交来决定通过这张表的记录去回滚或者执行ddl操作
- 增加了一个post-ddl的阶段,这也是ddl的最后一个阶段,会去:1. 真正的物理删除或重命名文件; 2. 删除innodb_ddl_log中的记录项; 3.对于一些ddl操作还会去更新其动态元数据信息(存储在`mysql.innodb_dynamic_metadata`,例如corrupt  flag, auto_inc值等)
- 一个正常运行的ddl结束后,其ddl log也应该被清理,如果这中间崩溃了,重启时会去尝试重放:1.如果已经走到最后一个ddl阶段的(commit之后),就replay ddl log,把ddl完成掉;2. 如果处于某个中间态,则回滚ddl

由于引入了atomic ddl, 有些ddl操作的行为也发生了变化:

- DROP TABLE: 在之前的版本中,一个drop table语句中如果要删多个表,比如t1,t2, t2不存在时,t1会被删除。但在8.0中,t1和t2都不会被删除,而是抛出错误。因此要注意5.7->8.0的复制问题 (DROP VIEW, CREATE USER也有类似的问题)
- DROP DATABASE: 修改元数据和ddl_log先提交事务,而真正的物理删除数据文件放在最后,因此如果在删除文件时崩溃,重启时会根据ddl_log继续执行drop database

测试:

MySQL很贴心的加了一个选项innodb_print_ddl_logs,打开后我们可以从错误日志看到对应的ddl log,下面我们通过这个来看下一些典型ddl的过程

root@(none) 11:12:19>SET GLOBAL innodb_print_ddl_logs = 1;                                                                                                                                    
Query OK, 0 rows affected (0.00 sec)root@(none) 11:12:22>SET GLOBAL log_error_verbosity = 3;                                                                                                                                                       
Query OK, 0 rows affected (0.00 sec)

CREATE DATABASE

mysql> CREATE DATABASE test;
Query OK, 1 row affected (0.02 sec)

创建数据库语句没有写log_ddl,可能觉得这不是高频操作,如果创建database的过程中失败了,重启后可能需要手动删除目录。

CREATE TABLE

mysql> USE test;
Database changed
mysql> CREATE TABLE t1 (a INT PRIMARY KEY, b INT);
Query OK, 0 rows affected (0.06 sec)[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=428, thread_id=7, space_id=76, old_file_path=./test/t1.ibd]
[InnoDB] DDL log delete : by id 428
[InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=429, thread_id=7, table_id=1102, new_file_path=test/t1]
[InnoDB] DDL log delete : by id 429
[InnoDB] DDL log insert : [DDL record: FREE, id=430, thread_id=7, space_id=76, index_id=190, page_no=4]
[InnoDB] DDL log delete : by id 430
[InnoDB] DDL log post ddl : begin for thread id : 7
InnoDB] DDL log post ddl : end for thread id : 7

从日志来看有三类操作,实际上描述了如果操作失败需要进行的三项逆向操作:删除数据文件,释放内存中的数据词典信息,删除索引btree。在创建表之前,这些数据被写入到ddl_log中,在创建完表并commit后,再从ddl log中删除这些记录。
另外上述日志中还有DDL log delete日志,其实在每次写入ddl log时是单独事务提交的,但在提交之后,会使用当前事务执行一条delete操作,直到操作结束了才会提交。

加列(instant)

mysql> ALTER TABLE t1 ADD COLUMN c INT;
Query OK, 0 rows affected (0.08 sec)
Records: 0  Duplicates: 0  Warnings: 0[InnoDB] DDL log post ddl : begin for thread id : 7
[InnoDB] DDL log post ddl : end for thread id : 7

注意这里执行的是Instant ddl, 这是8.0.13新支持的特性,加列操作可以只修改元数据,因此从ddl log中无需记录数据

删列

mysql> ALTER  TABLE t1 DROP COLUMN c;
Query OK, 0 rows affected (2.77 sec)
Records: 0  Duplicates: 0  Warnings: 0[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=487, thread_id=7, space_id=83, old_file_path=./test/#sql-ib1108-1917598001.ibd]
[InnoDB] DDL log delete : by id 487
[InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=488, thread_id=7, table_id=1109, new_file_path=test/#sql-ib1108-1917598001]
[InnoDB] DDL log delete : by id 488
[InnoDB] DDL log insert : [DDL record: FREE, id=489, thread_id=7, space_id=83, index_id=200, page_no=4]
[InnoDB] DDL log delete : by id 489[InnoDB] DDL log insert : [DDL record: DROP, id=490, thread_id=7, table_id=1108]
[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=491, thread_id=7, space_id=82, old_file_path=./test/#sql-ib1109-1917598002.ibd, new_file_path=./test/t1.ibd]
[InnoDB] DDL log delete : by id 491
[InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=492, thread_id=7, table_id=1108, old_file_path=test/#sql-ib1109-1917598002, new_file_path=test/t1]
[InnoDB] DDL log delete : by id 492
[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=493, thread_id=7, space_id=83, old_file_path=./test/t1.ibd, new_file_path=./test/#sql-ib1108-1917598001.ibd]
[InnoDB] DDL log delete : by id 493
[InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=494, thread_id=7, table_id=1109, old_file_path=test/t1, new_file_path=test/#sql-ib1108-1917598001]
[InnoDB] DDL log delete : by id 494
[InnoDB] DDL log insert : [DDL record: DROP, id=495, thread_id=7, table_id=1108]
[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=496, thread_id=7, space_id=82, old_file_path=./test/#sql-ib1109-1917598002.ibd][InnoDB] DDL log post ddl : begin for thread id : 7
[InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=496, thread_id=7, space_id=82, old_file_path=./test/#sql-ib1109-1917598002.ibd]
[InnoDB] DDL log replay : [DDL record: DROP, id=495, thread_id=7, table_id=1108]
[InnoDB] DDL log replay : [DDL record: DROP, id=490, thread_id=7, table_id=1108]
[InnoDB] DDL log post ddl : end for thread id : 7

这是个典型的三阶段ddl的过程:分为prepare, perform 以及commit三个阶段:

  • Prepare: 这个阶段会修改元数据,创建临时ibd文件#sql-ib1108-1917598001.ibd, 如果发生异常崩溃,我们需要能把这个临时文件删除掉, 因此和create table类似,也为这个idb写了三条日志:delete space, remove cache,以及free btree
  • Perform: 执行操作,将数据拷贝到上述ibd文件中,(同时处理online dmllog), 这部分不涉及log ddl操作
  • Commit: 更新数据词典信息并提交事务, 这里会写几条日志:

    • DROP : table_id=1108
    • RENAME SPACE: #sql-ib1109-1917598002.ibd文件被rename成t1.ibd
    • RENAME TABLE: #sql-ib1109-1917598002被rename成t1
    • RENAME SPACE: t1.ibd 被rename成#sql-ib1108-1917598001.ibd
    • RENAME TABLE: t1表被rename成#sql-ib1108-1917598001
    • DROP TABLE: table_id=1108
    • DELETE SPACE: 删除#sql-ib1109-1917598002.ibd

实际上这一步写的ddl log描述了commit阶段操作的逆向过程:将t1.ibd rename成#sql-ib1109-1917598002, 并将sql-ib1108-1917598001 rename成t1表,最后删除旧表。其中删除旧表的操作这里不执行,而是到post-ddl阶段执行

  • Post-ddl: 在事务提交后,执行最后的操作:replay ddl log, 删除旧文件,清理mysql.innodb_dynamic_metadata中相关信息

    • DELETE SPACE: #sql-ib1109-1917598002.ibd
    • DROP: table_id=1108
    • DROP: table_id=1108

加索引

mysql> ALTER TABLE t1 ADD KEY(b);
Query OK, 0 rows affected (0.14 sec)
Records: 0  Duplicates: 0  Warnings: 0[InnoDB] DDL log insert : [DDL record: FREE, id=431, thread_id=7, space_id=76, index_id=191, page_no=5]
[InnoDB] DDL log delete : by id 431[InnoDB] DDL log post ddl : begin for thread id : 7
[InnoDB] DDL log post ddl : end for thread id : 7

创建索引采用inplace创建的方式,没有临时文件,但如果异常发生的话,依然需要在发生异常时清理临时索引, 因此增加了一条FREE log,用于异常发生时能够删除临时索引.

TRUNCATE TABLE

mysql> TRUNCATE TABLE t1;
Query OK, 0 rows affected (0.13 sec)[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=439, thread_id=7, space_id=77, old_file_path=./test/#sql-ib1103-1917597994.ibd, new_file_path=./test/t1.ibd]
[InnoDB] DDL log delete : by id 439
[InnoDB] DDL log insert : [DDL record: DROP, id=440, thread_id=7, table_id=1103]
[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=441, thread_id=7, space_id=77, old_file_path=./test/#sql-ib1103-1917597994.ibd]
[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=442, thread_id=7, space_id=78, old_file_path=./test/t1.ibd]
[InnoDB] DDL log delete : by id 442
[InnoDB] DDL log insert : [DDL record: REMOVE CACHE, id=443, thread_id=7, table_id=1104, new_file_path=test/t1]
[InnoDB] DDL log delete : by id 443
[InnoDB] DDL log insert : [DDL record: FREE, id=444, thread_id=7, space_id=78, index_id=194, page_no=4]
[InnoDB] DDL log delete : by id 444
[InnoDB] DDL log insert : [DDL record: FREE, id=445, thread_id=7, space_id=78, index_id=195, page_no=5]
[InnoDB] DDL log delete : by id 445[InnoDB] DDL log post ddl : begin for thread id : 7
[InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=441, thread_id=7, space_id=77, old_file_path=./test/#sql-ib1103-1917597994.ibd]
[InnoDB] DDL log replay : [DDL record: DROP, id=440, thread_id=7, table_id=1103]
[InnoDB] DDL log post ddl : end for thread id : 7

Truncate table是个比较有意思的话题,在早期5.6及之前的版本中, 是通过删除旧表创建新表的方式来进行的,5.7之后为了保证原子性,改成了原地truncate文件,同时增加了一个truncate log文件,如果在truncate过程中崩溃,可以通过这个文件在崩溃恢复时重新truncate。到了8.0版本,又恢复成了删除旧表,创建新表的方式,与之前不同的是,8.0版本在崩溃时可以回滚到旧数据,而不是再次执行。以上述为例,主要包括几个步骤:

  • 将表t1.ibd rename成#sql-ib1103-1917597994.ibd
  • 创建新文件t1.ibd
  • post-ddl: 将老文件#sql-ib1103-1917597994.ibd删除

RENAME TABLE

mysql> RENAME TABLE t1 TO t2;
Query OK, 0 rows affected (0.06 sec)

DDL LOG:

[InnoDB] DDL log insert : [DDL record: RENAME SPACE, id=450, thread_id=7, space_id=78, old_file_path=./test/t2.ibd, new_file_path=./test/t1.ibd]
[InnoDB] DDL log delete : by id 450
[InnoDB] DDL log insert : [DDL record: RENAME TABLE, id=451, thread_id=7, table_id=1104, old_file_path=test/t2, new_file_path=test/t1]
[InnoDB] DDL log delete : by id 451[InnoDB] DDL log post ddl : begin for thread id : 7
[InnoDB] DDL log post ddl : end for thread id : 7

这个就比较简单了,只需要记录rename space 和rename table的逆操作即可. post-ddl不需要做实际的操作

DROP TABLE

DROP TABLE t2
[InnoDB] DDL log insert : [DDL record: DROP, id=595, thread_id=7, table_id=1119]
[InnoDB] DDL log insert : [DDL record: DELETE SPACE, id=596, thread_id=7, space_id=93, old_file_path=./test/t2.ibd][InnoDB] DDL log post ddl : begin for thread id : 7
[InnoDB] DDL log replay : [DDL record: DELETE SPACE, id=596, thread_id=7, space_id=93, old_file_path=./test/t2.ibd]
[InnoDB] DDL log replay : [DDL record: DROP, id=595, thread_id=7, table_id=1119]
[InnoDB] DDL log post ddl : end for thread id : 7

先在ddl log中记录下需要删除的数据,再提交后,再最后post-ddl阶段执行真正的删除表对象和文件操作

代码实现:

主要实现代码集中在文件storage/innobase/log/log0ddl.cc中,包含了向log_ddl表中插入记录以及replay的逻辑。

隐藏的innodb_log_ddl表结构如下

  def->add_field(0, "id", "id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT");def->add_field(1, "thread_id", "thread_id BIGINT UNSIGNED NOT NULL");def->add_field(2, "type", "type INT UNSIGNED NOT NULL");def->add_field(3, "space_id", "space_id INT UNSIGNED");def->add_field(4, "page_no", "page_no INT UNSIGNED");def->add_field(5, "index_id", "index_id BIGINT UNSIGNED");def->add_field(6, "table_id", "table_id BIGINT UNSIGNED");def->add_field(7, "old_file_path","old_file_path VARCHAR(512) COLLATE UTF8_BIN");def->add_field(8, "new_file_path","new_file_path VARCHAR(512) COLLATE UTF8_BIN");def->add_index(0, "index_pk", "PRIMARY KEY(id)");def->add_index(1, "index_k_thread_id", "KEY(thread_id)");

记录类型

根据不同的操作类型,可以分为如下几类:

  1. FREE_TREE_LOG
    目的是释放索引btree,入口函数log_DDL::write_free_tree_log,在创建索引和删除表时会调用到

对于drop table中涉及的删索引操作,log ddl的插入操作放到父事务中,一起要么提交要么回滚
对于创建索引的case, log ddl就需要单独提交,父事务将记录标记删除,这样后面如果ddl回滚了,也能将残留的index删掉。

  1. DELETE_SPACE_LOG

入口函数:Log_DDL::write_delete_space_log

用于记录删除tablespace操作,同样分为两种情况:

  1. drop table/tablespace, 写入的记录随父事务一起提交,并在post-ddl阶段replay
  2. 创建tablespace, 写入的记录单独提交,并被父事务标记删除,如果父事务回滚,就通过replay删除参与的tablespace
  3. RENAME_SPACE_LOG

入口函数:Log_DDL::write_rename_space_log

用于记录rename操作,例如如果我们把表t1 rename成t2,在其中就记录了逆向操作t2 rename to t1.
在函数Fil_shard::space_rename()中,总是先写ddl log, 再做真正的rename操作. 写日志的过程同样是独立事务提交,父事务做未提交的删除操作

  1. DROP_LOG

入口函数: Log_DDL::write_drop_log

用于记录删除表对象操作,这里不涉及文件层操作,写ddl log在父事务中执行

  1. RENAME_TABLE_LOG

入口函数: Log_DDL::write_rename_table_log

用于记录rename table对象的逆操作,和rename space类似,也是独立事务提交ddl log, 父事务标记删除

  1. REMOVE_CACHE_LOG

入口函数: Log_DDL::write_remove_cache_log

用于处理内存表对象的清理,独立事务提交,父事务标记删除

  1. ALTER_ENCRYPT_TABLESPACE_LOG

入口函数: Log_DDL::write_alter_encrypt_space_log

用于记录对tablespace加密属性的修改,独立事务提交. 在写完ddl log后修改tablespace page0 中的加密标记

综上,在ddl的过程中可能会提交多次事务,大概分为三类:

  • 独立事务写ddl log并提交,父事务标记删除, 如果父事务提交了,ddl log也被顺便删除了,如果父事务回滚了,那就要根据ddl log做逆操作来回滚ddl
  • 独立事务写ddl log 并提交, (目前只有ALTER_ENCRYPT_TABLESPACE_LOG)
  • 使用父事务写ddl log,在ddl结束时提交。需要在post-ddl阶段处理

post_ddl

如上所述,有些ddl log是随着父事务一起提交的,有些则在post-ddl阶段再执行, post_ddl发生在父事提交或回滚之后: 若事务回滚,根据ddl log做逆操作,若事务提交,在post-ddl阶段做最后真正不可逆操作(例如删除文件)

入口函数: Log_DDL::post_ddl -->Log_DDL::replay_by_thread_id

根据执行ddl的线程thread id通过innodb_log_ddl表上的二级索引,找到log id,再到聚集索引上找到其对应的记录项,然后再replay这些操作,完成ddl后,清理对应记录

崩溃恢复

在崩溃恢复结束后,会调用ha_post_recover接口函数,进而调用innodb内的函数Log_DDL::recover(), 同样的replay其中的记录,并在结束后删除记录。但ALTER_ENCRYPT_TABLESPACE_LOG类型并不是在这一步删除,而是加入到一个数组ts_encrypt_ddl_records中,在之后调用resume_alter_encrypt_tablespace来恢复操作,


#阿里云开年Hi购季#幸运抽好礼!
点此抽奖:https://www.aliyun.com/acts/product-section-2019/yq-lottery?utm_content=g_1000042901

原文链接
本文为云栖社区原创内容,未经允许不得转载。

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

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

相关文章

mysql查询包含字符串(模糊查询)

mysql查询包含字符串更高效率的方法一、LOCATE语句SELECT column from table where locate(‘keyword’, condition)>0二、或是 locate 的別名 positionSELECT column from table where position(‘keyword’ IN condition)三、INSTR语句SELECT column from table where ins…

ant编译web项目

文章目录1.下载ant2. 解压ant3. 配置an环境变量4. 验证二、编译项目2.1. 新建一个build.xml2.2. 编译项目测试1.下载ant 官网链接: https://ant.apache.org/srcdownload.cgi 2. 解压ant 3. 配置an环境变量 4. 验证 ant -v二、编译项目 2.1. 新建一个build.xml…

Spark in action on Kubernetes - Playground搭建与架构浅析

前言 Spark是非常流行的大数据处理引擎,数据科学家们使用Spark以及相关生态的大数据套件完成了大量又丰富场景的数据分析与挖掘。Spark目前已经逐渐成为了业界在数据处理领域的行业标准。但是Spark本身的设计更偏向使用静态的资源管理,虽然Spark也支持了…

阿里云发布时间序列数据库TSDB,关于时序你了解多少?

概要介绍 时间序列数据是一种表示物理设备,系统、应用过程或行为随时间变化的数据,广泛应用于物联网,工业物联网,基础运维系统等场景。阿里云TSDB 时间序列数据库可以解决大规模时序数据的可靠写入,降低数据存储成本&…

VMware宣布完成27亿美元收购Pivotal;日本成功研发出6G芯片:单载波速度高达100Gbps;联想手机再换新掌门……...

关注并标星星CSDN云计算 速递、最新、绝对有料。这里有企业新动、这里有业界要闻,打起十二分精神,紧跟fashion你可以的!每周两次,打卡即read更快、更全了解泛云圈精彩newsgo go go【1月1日 星期三】云の声音5G医疗爆发箭在弦上&am…

mysql自定义排序以及优化like模糊查询

**1. 自定义排序函数FIELD()**SELECT id,username,city FROM sy_user order byFIELD(city,郑州, 开封, 平顶山,洛阳, 商丘, 安阳, 新乡, 许昌, 鹤壁, 焦作, 濮阳, 漯河, 三门峡, 周口,驻马店, 南阳, 信阳, 济源,省本部,河南) **2.使用 case when**SELECT id,username,city FR…

使用Logtail采集Kubernetes上挂载的NAS日志

采集k8s挂载Nas后的日志 该文档主要介绍使用logtail以两种不同的方式进行k8s挂载Nas后的日志采集。两种采集方式的实现原理是一样的,都是通过将Logtail和业务容器挂载到相同的NAS上,使Logtail和业务容器的日志数据共享,以此实现日志采集。下…

linux目录挂载

挂载前声明: 执行挂载后,源本地目录下的文件会不显示。 挂载前:需要提前将原目录下面的日志文件备份转移,挂载成功后,在转移到挂载的本地目录下面即可。操作流程如下: 1. 将/app/fis/xml中148G多的日志文件…

深度揭秘“蚂蚁双链通”

今年年初,蚂蚁金服ATEC城市峰会在上海举行。在ATEC区块链行业研讨会分论坛上,蚂蚁金服区块链高级产品专家杨俊带来了主题为《供应链金融,不止于金融:蚂蚁双链通——基于区块链的供应链协作网络》的精彩分享。 蚂蚁金服区块链高级产…

@程序员,不要瞎努力!比起熬夜更可怕的是“熬日”!

最近,笔者在经常后台看到小伙伴留言在问,想学Python,但不知道如何入门?其实对于这个问题,真是仁者见仁智者见智。有句老话说的好“一千个读者,就有一千个哈姆雷特”不过对于此疑惑,笔者就想直接…

配置nginx作为静态资源服务器 css,js,image等资源直接访问

1.传统的web项目,一般都将静态资源存放在 webroot 的目录下,这样做很方便获取静态资源,但是如果说web项目很大,用户很多,静态资源也很多时,服务器的性能 或许就会很低下了。这种情况下一般都会需要一个静态…

分布式事务中间件 Fescar - 全局写排它锁解读

前言 一般,数据库事务的隔离级别会被设置成 读已提交,已满足业务需求,这样对应在Fescar中的分支(本地)事务的隔离级别就是 读已提交,那么Fescar中对于全局事务的隔离级别又是什么呢?如果认真阅…

使用xfire webservice接口开发,obj与xml相互转换好用工具类,不需要写大量的转换代码,亲测可用

webservice接口开发&#xff0c;旧工程中存在使用xfire开发的接口&#xff0c;对象转换为xml和xml转换为对象的时候需要些大量的代码&#xff0c;工作量很大。现在提供一个比较好的对象转换为xml的工具。 <!-- https://mvnrepository.com/artifact/commons-betwixt/commons…

Spring Cloud Alibaba迁移指南(二):零代码替换 Eureka

自 Spring Cloud 官方宣布 Spring Cloud Netflix 进入维护状态后&#xff0c;我们开始制作《Spring Cloud Alibaba迁移指南》系列文章&#xff0c;向开发者提供更多的技术选型方案&#xff0c;并降低迁移过程中的技术难度。 第二篇&#xff0c;Spring Cloud Alibaba 实现了 Sp…

云+X案例展 | 民生类:京东云突破数据中心光互联瓶颈

本案例由京东云投递并参与评选&#xff0c;CSDN云计算独家全网首发&#xff1b;更多关于【云X 案例征集】的相关信息&#xff0c;点击了解详情丨挖掘展现更多优秀案例&#xff0c;为不同行业领域带来启迪&#xff0c;进而推动整个“云行业”的健康发展。随着数字化的进程&#…

Linux查看文件夹大小

Linux查看文件夹大小 du -sh 查看当前文件夹大小 du -sh * | sort -n 统计当前文件夹(目录)大小&#xff0c;并按文件大小排序 du -sk filename 查看指定文件大小

UI2Code智能生成Flutter代码--整体设计篇

背景: 随着移动互联网时代的到来&#xff0c;人类的科学技术突飞猛进。然而软件工程师们依旧需要花费大量精力在重复的还原UI视觉稿的工作。 UI视觉研发拥有明显的特征&#xff1a;组件&#xff0c;位置和布局&#xff0c;符合机器学习处理范畴。能否通过机器视觉和深度学习等手…

如何成为优秀的技术主管?你要做到这三点

阿里妹导读&#xff1a;技术主管&#xff0c;又叫「技术经理」&#xff0c;英文一般是 Tech Leader &#xff0c;简称 TL。随着工作经验的不断积累&#xff0c;能力的不断提升&#xff0c;每个人都有机会成为Team Leader。然而在机会到来前&#xff0c;我们必须提前做好准备&am…

达摩院2020十大科技趋势发布:云成IT技术创新中心

2020年第一个工作日&#xff0c;“达摩院2020十大科技趋势”发布。这是继2019年之后&#xff0c;阿里巴巴达摩院第二次预测年度科技趋势。 回望2019年的科技领域&#xff0c;静水流深之下仍有暗潮涌动。AI芯片崛起、智能城市诞生、5G催生全新应用场景……达摩院去年预测的科技…

SpringBoot Mybatisplus 多数据源使用

文章目录一、mybatisplus3.x1. 依赖2. 启动类添加注解3. 添加多数据源注解4. yml5. 测试类6. 源码地址为了适配新的需求&#xff0c;需要同时支持mysql和oracle数据库操作多数据源&#xff0c;因此项目中集成dynamic-datasource-spring-boot-starter,支持很多场景。 例如&#…