pg truncate

命令选项

TRUNCATE [ TABLE ] [ ONLY ] name [ * ] [, ... ]
    [ RESTART IDENTITY | CONTINUE IDENTITY ] [ CASCADE | RESTRICT ]

1.ONLY:只truncate指定的表。当表有继承子表或有子分区时,默认会一起truncate;only可只truncate继承父表。分区父表不能指定only

--不能truncate only分区父表
=> truncate only parttable;
ERROR:  42809: cannot truncate only a partitioned table
HINT:  Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.
LOCATION:  ExecuteTruncate, tablecmds.c:1655
--truncate only继承父表,只清理父表
=> truncate table only parenttable;
TRUNCATE TABLE
=> select tableoid::regclass,count(*) from parenttable group by tableoid::regclass ;
  tableoid  | count 
------------+-------
 childtable |     1
 
--直接truncate继承父表,子表也会被清理
=>  truncate table parenttable;
TRUNCATE TABLE
=> select tableoid::regclass,count(*) from parenttable group by tableoid::regclass ;
 tableoid | count 
----------+-------
(0 rows)

2.RESTART IDENTITY CONTINUE IDENTITY:列上的序列是否要重置,默认CONTINUE。

--bigserial 默认会创建列上的序列
=> create table tableserial (a  bigserial not null,b name);
CREATE TABLE
=> \d+ tableserial;
                                               Table "public.tableserial"
 Column |  Type  | Collation | Nullable |                Default                 | Storage | Stats target | Description 
--------+--------+-----------+----------+----------------------------------------+---------+--------------+-------------
 a      | bigint |           | not null | nextval('tableserial_a_seq'::regclass) | plain   |              | 
 b      | name   |           |          |                                        | plain   |              | 
 
=> insert into tableserial(b) select md5(random()::text) from generate_series(1,1000);
INSERT 0 1000 
--seq当前值为1000
=> select currval('tableserial_a_seq'::regclass);
 currval 
---------
    1000

--直接truncate默认不会重置序列
=>  truncate table tableserial;
TRUNCATE TABLE
=> select currval('tableserial_a_seq'::regclass) cur,nextval('tableserial_a_seq'::regclass);
 cur  | nextval 
------+---------
 1000 |    1001

--显示指定RESTART IDENTITY,重置序列
=>  truncate table tableserial RESTART IDENTITY;
TRUNCATE TABLE
--注意seq在nextval时重置了
=>  select currval('tableserial_a_seq'::regclass) cur,nextval('tableserial_a_seq'::regclass);
 cur  | nextval 
------+---------
 1001 |       1

3.CASCADE:清理表及其所有外键表的数据

--创建主表和外键表和数据
=>  create table pri_tab(id bigint primary key,name varchar(10));
CREATE TABLE

=> insert into pri_tab values (1,'abc'),(2,'abc'),(3,'abc');
INSERT 0 3

=>  create table frn_tab(id bigint,FOREIGN KEY (id) REFERENCES pri_tab(id));
CREATE TABLE

=> insert into frn_tab values (1),(2);
INSERT 0 2

=> select * from pri_tab;
 id | name 
----+------
  1 | abc
  2 | abc
  3 | abc
(3 rows)

--外键表frn_tab依赖主表pri_tab的数据
=> select * from frn_tab;
 id 
----
  1
  2
(2 rows)

--有主表外键reference时,外键表必须跟cascade,否则无法清理
=> truncate table pri_tab ;
ERROR:  0A000: cannot truncate a table referenced in a foreign key constraint
DETAIL:  Table "frn_tab" references "pri_tab".
HINT:  Truncate table "frn_tab" at the same time, or use TRUNCATE ... CASCADE.
LOCATION:  heap_truncate_check_FKs, heap.c:3427

--外键约束的表一起清空
=> truncate table pri_tab cascade;
NOTICE:  00000: truncate cascades to table "frn_tab"
LOCATION:  ExecuteTruncateGuts, tablecmds.c:1725
TRUNCATE TABLE
=> select * from pri_tab;
 id | name 
----+------
(0 rows)

=>  select * from frn_tab;
 id 
----
(0 rows)

由于外键表依赖主表的数据,不能直接truncate主表,必须加cascade,此时外键表也跟随主表一起清空

4.RESTRICT
是否清理foreign key表。没什么用,default选项,加不加都是这样。清理附带的外键表应加CASCADE。

MVCC/transaction

pg官方文档有这么一段化

TRUNCATE is not MVCC-safe. After truncation, the table will appear empty to concurrent transactions, if they are using a snapshot taken before the truncation occurred.
TRUNCATE is transaction-safe with respect to the data in the tables: the truncation will be safely rolled back if the surrounding transaction does not commit.

transaction-safe意思是可以放在事务块里,可以回退
回滚truncate:

=> begin;
BEGIN
=> truncate t1;
TRUNCATE TABLE
=> rollback;
ROLLBACK
=> select count(*) from t1;
 count 
-------
   100

not MVCC-safe意思是一个会话在truncate前打了一个快照,快照期间如果发生truncate,这个快照是可以读到truncate清理后的结果的。这不符合MVCC。
不过这个问题不算太大,在会话场景下,因为truncate是8级锁,快照没有结束的话最低在表上有一个读共享锁,所以truncate不会执行。

This will only be an issue for a transaction that did not access the table in question before the DDL command started —any transaction that has done so would hold at least an ACCESS SHARE table lock, which would block the DDL command until that transaction completes

功能更新

在这里插入图片描述
truncate更新功能不多,只需要注意14的时候支持truncate foreign tables即可。truncate foreign tables前提是fdw得支持TRUNCATE API

Also it extends postgres_fdw so that it can issue TRUNCATE command to foreign servers, by adding new routine for that TRUNCATE API.

pg truncate和其他库的功能差异

在这里插入图片描述
在这里插入图片描述
truncate很快、8级锁等特性已经是人尽皆知的事情了,相对于其他数据库,pg还可以:选择是否重置序列RESTART IDENTITY CONTINUE IDENTITY)、回滚简单的授权

truncate做了什么

create table lzl(a int);
create index lzl_idx on lzl(a);
create sequence lzl_seq start with 1;
alter table lzl alter column a set default nextval('lzl_seq');
--select pg_relation_filepath('lzl');
--db路径
=> select oid from  pg_database where datname='lzldb';
  oid   
--------
 418679

--刚创建时候各个rel的oid=relfilenode
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';
 relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------
 lzl     | 428363 |      428363 | r
 lzl_idx | 428366 |      428366 | i
 lzl_seq | 428367 |      428367 | S
(3 rows)

=> truncate table lzl;
TRUNCATE TABLE
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';
 relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------
 lzl     | 428363 |      428370 | r
 lzl_idx | 428366 |      428371 | i
 lzl_seq | 428367 |      428367 | S
--truncate后,表和索引重建了,sequence却没有


M=> truncate table lzl RESTART IDENTITY;
TRUNCATE TABLE
=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';
 relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------
 lzl     | 428363 |      428372 | r
 lzl_idx | 428366 |      428373 | i
 lzl_seq | 428367 |      428367 | S
--显示restart,sequence还是没有重建


M=> alter sequence lzl_seq restart;
ALTER SEQUENCE
M=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';
 relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------
 lzl     | 428363 |      428372 | r
 lzl_idx | 428366 |      428373 | i
 lzl_seq | 428367 |      428374 | S
--显示restart sequence是会重建sequence的

truncate ···RESTART IDENTITY没有重建我们sequence,alter sequence lzl_seq restart重建了sequence。应该是RESTART IDENTITY没有理解对。在看下官方文档对RESTART IDENTITY的解释

Automatically restart sequences owned by columns of the truncated table(s).

sequence必须owned by表上的列,注意不是owner to。虽然\d可以看到表上的sequence,但是它可能不属于表

 \d+ lzl;
                                              Table "public.lzl"
 Column |  Type   | Collation | Nullable |           Default            | Storage | Stats target | Description 
--------+---------+-----------+----------+------------------------------+---------+--------------+-------------
 a      | integer |           |          | nextval('lzl_seq'::regclass) | plain   |              | 

owned by修改sequence的所属表

=> ALTER SEQUENCE lzl_seq OWNED BY lzl.a;
ALTER SEQUENCE

--查看序列的所有者信息
SELECT s.relname AS seq, n.nspname AS sch, t.relname AS tab, a.attname AS col  
FROM pg_class s  
JOIN pg_depend d ON d.objid=s.oid AND d.classid='pg_class'::regclass AND d.refclassid='pg_class'::regclass  
JOIN pg_class t ON t.oid=d.refobjid  
JOIN pg_namespace n ON n.oid=t.relnamespace  
JOIN pg_attribute a ON a.attrelid=t.oid AND a.attnum=d.refobjsubid  
WHERE s.relkind='S' AND d.deptype='a';
        seq        |  sch   |     tab     | col 
-------------------+--------+-------------+-----
 tableserial_a_seq | public | tableserial | a
 lzl_seq           | public | lzl         | a

=> truncate table lzl RESTART IDENTITY;
TRUNCATE TABLE
M=> select relname,oid,relfilenode,relkind from pg_class where  relname like 'lzl%';
 relname |  oid   | relfilenode | relkind 
---------+--------+-------------+---------
 lzl     | 428363 |      428375 | r
 lzl_idx | 428366 |      428376 | i
 lzl_seq | 428367 |      428377 | S

sequence owned by表上的列时,truncate显示带RESTART IDENTITY就会restart这个sequence,也就重建了sequence。默认以serial/bigserial方式创建的序列是被表拥有的,随表drop而删除;那些不被表拥有的序列,drop不会删除
truncate重建特性汇总:

  • 直接truncate table会重建表和索引
  • truncate table+RESTART IDENTITY,会重建(也就是retart)属于这个表的sequence。只要不属于这个表的sequence,哪怕列上关联了seq的默认值,也不会重建这个seq

源码分析

truncate也是utility命令,很快就可以找到入口函数
src/backend/commands/tablecmds.c中的ExecuteTruncate为入口函数,注释其实已经说明truncate要获得exclusive lock,并检查权限和relation是否ok,递归检查所有需要truncate的表

void

ExecuteTruncate(TruncateStmt *stmt)

{

...

/*

* Open, exclusive-lock, and check all the explicitly-specified relations

*/

foreach(cell, stmt->relations)

{

...
LOCKMODE lockmode = AccessExclusiveLock;  //8级锁
...

rel = table_open(myrelid, NoLock); //打开表


void
ExecuteTruncate(TruncateStmt *stmt)
{
...
	foreach(cell, stmt->relations)
	{
...
		LOCKMODE	lockmode = AccessExclusiveLock; //8级锁
...
		/* open the relation, we already hold a lock on it */
		rel = table_open(myrelid, NoLock); //打开表
...
		truncate_check_activity(rel);  //虽然已经有锁了,但是还是要验证是否在使用
...
		if (recurse) //递归执行
		{
...
			children = find_all_inheritors(myrelid, lockmode, NULL); //找到所有继承子表

			foreach(child, children)
			{
...
				 //上面只检查了父表,递归要检查子表
				truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
				truncate_check_activity(rel);

				rels = lappend(rels, rel);  //加入到待truncate的rel队列中
				relids = lappend_oid(relids, childrelid);
...
			}
		}
		//递归结束
		//发现truncate only分区父表,直接报错
		else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("cannot truncate only a partitioned table"),
					 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
	}
		
	//主体函数	
	ExecuteTruncateGuts(rels, relids, relids_logged,
						stmt->behavior, stmt->restart_seqs);

	/* And close the rels */
	foreach(cell, rels)
	{
		Relation	rel = (Relation) lfirst(cell);

		table_close(rel, NoLock);
	}
}

ExecuteTruncateGuts函数不仅被truncate命令调用,还被订阅端调用(发布订阅可以同步truncate)。

void
ExecuteTruncateGuts(List *explicit_rels,
					List *relids,
					List *relids_logged,
					DropBehavior behavior, bool restart_seqs)
{
...
	rels = list_copy(explicit_rels);
	if (behavior == DROP_CASCADE)  //如果指定了cascade选项,提取所有reference的relation
	{
		for (;;)
		{
...
			newrelids = heap_truncate_find_FKs(relids); //找到fk
			if (newrelids == NIL)
				break;			/* nothing else to add */ //没有rel直接退出

			foreach(cell, newrelids)
			{
...
				rel = table_open(relid, AccessExclusiveLock); //所有rel获得AccessExclusiveLock
				ereport(NOTICE,
						(errmsg("truncate cascades to table \"%s\"",
								RelationGetRelationName(rel))));
				truncate_check_rel(relid, rel->rd_rel);  //检查是否是可以truncate的对象,得是存储数据的表
				truncate_check_perms(relid, rel->rd_rel);  //检查是否有权限
				truncate_check_activity(rel);  //检查是否在使用
...
			}
		}
	}

...
	if (restart_seqs) //restart seq的处理
	{
		foreach(cell, rels)
		{
			Relation	rel = (Relation) lfirst(cell);
			List	   *seqlist = getOwnedSequences(RelationGetRelid(rel));
...
			//只是做sequence的权限检查
				if (!pg_class_ownercheck(seq_relid, GetUserId()))
					aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
								   RelationGetRelationName(seq_rel));
...
		}
	}

...

	//执行所有before truncate触发器
	foreach(cell, rels)
	{
		ExecBSTruncateTriggers(estate, resultRelInfo);
		resultRelInfo++;
	}

//正式开始truncate
	foreach(cell, rels)
	{
...
		//如果是分区父表,啥都不做
		if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
			continue;

		//如果是foreign table的处理
		if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
		{
		...
		}

...
		 //如果是同一事务,因为可能会回退,直接执行heap_truncate_one_rel函数,不创建新的relfilenode
		if (rel->rd_createSubid == mySubid ||
			rel->rd_newRelfilenodeSubid == mySubid)
		{
			/* Immediate, non-rollbackable truncation is OK */
			heap_truncate_one_rel(rel);
		}
		else
		{
...
			//设置NewRelfilenode
			RelationSetNewRelfilenode(rel, rel->rd_rel->relpersistence);

			heap_relid = RelationGetRelid(rel);

			 //toast同理
			toast_relid = rel->rd_rel->reltoastrelid;
			if (OidIsValid(toast_relid))
			{
				Relation	toastrel = relation_open(toast_relid,
													 AccessExclusiveLock);

				RelationSetNewRelfilenode(toastrel,
										  toastrel->rd_rel->relpersistence);
				table_close(toastrel, NoLock);
			}

...
			 //重建索引
			reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
							 &reindex_params);
		}

		pgstat_count_truncate(rel); //更新pgstat的truncate计算
	}

...
	//重置sequence 
	foreach(cell, seq_relids)
	{
		Oid			seq_relid = lfirst_oid(cell);

		ResetSequence(seq_relid);
	}

	//写wal
	if (list_length(relids_logged) > 0)
	{
	...
	}


	//触发AFTER TRUNCATE triggers
	resultRelInfo = resultRelInfos;
	foreach(cell, rels)
	{
		ExecASTruncateTriggers(estate, resultRelInfo);
		resultRelInfo++;
	}
...
}	

ExecuteTruncateGuts函数根据truncate选项进行处理,处理过程如下:

  1. 根据cascade选项找到所有reference的外键表
  2. 触发before truncate触发器
  3. 执行truncate
  • 如果是同一事务,不立即生成NewRelfilenode,直接调用函数heap_truncate_one_rel进行truncate
  • 如果不是同一事务,调用RelationSetNewRelfilenode新建NewRelfilenode
  1. reindex_relation函数重建索引
  2. 根据restart identity重置sequence
  3. 写wal日志
  4. 触发after truncate触发器

后面大概追了下函数,套娃比较多
RelationSetNewRelfilenode
table_relation_set_new_filenode
relation_set_new_filenode在这里插入代码片
heapam_relation_set_new_filenode
RelationCreateStorage
然后到src/backend/storage/smgr/smgr.c中的smgrcreatesmgr_create。后面就没看太懂了(一到函数指针就有点追不到的感觉,先这样吧~)···
对于smgr.c有这样的注释:

public interface routines to storage manager switch
All file system operations in POSTGRES dispatch through these routines.

任何文件系统操作都会经过smgr(storage manager);到这里就是文件系统操作了。

reference

https://www.postgresql.org/docs/15/sql-truncate.html
https://www.postgresql.org/docs/current/mvcc-caveats.html
https://pgpedia.info/t/truncate.html
https://www.orafaq.com/wiki/SQL_FAQ
https://learnsql.com/blog/difference-between-truncate-delete-and-drop-table-in-sql/

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

负索引和负方向

在python里有序集合的index位置信息可正可负,方向可以从左向右或从右向左。以“python”字符串通过list函数转化生成的列表为例,其正负位置信息index值如下所示: 0 1 2 3 4 5 p y t h o n -6 -5 -4 -3 -2 -1 故,切片的start、end、…

Vue框架学习笔记——绑定class样式和绑定style样式

文章目录 前文提要class样式的三种绑定方法(图片来自参考链接)style样式(内联形式)总结 前文提要 本人仅做个人学习记录,如有错误,请多包涵 主要学习链接:尚硅谷Vue2.0Vue3.0全套教程丨vuejs从…

KepServer配置OPC UA

KepServer在进行OPC UA配置时需要一个身份验证,密码是安装KepServer时输入的密码,但是这个用户名有些人可能不清楚,KepServer默认的管理员用户名为:administrator。 登录进去之后可以进行OPC UA服务配置。 如果想添加自定义的用户…

虚幻学习笔记2—点击场景3D物体的两种处理方式

一、前言 本文使用的虚幻引擎为5.3.2,两种方式分别为:点击根物体和精准点击目标物体。 二、实现 2.1、玩家控制器中勾选鼠标点击事件:这一步很重要,如图2.1.1所示:在自定义玩家控制器中勾 图2.1.1 选该项&#xff0c…

SpringBoot : ch09 整合Redis

前言 当你的应用程序需要一个快速、可扩展的内存数据库时,Redis是一个非常流行的选择。通过将Redis与Spring Boot集成,你可以轻松地利用Redis的功能,例如缓存、会话存储和消息队列等,从而提升应用程序的性能和可伸缩性。 在本教…

OpenCvSharp从入门到实践-(04)色彩空间

目录 1、GRAY色彩空间 2、从BGR色彩空间转换到GRAY色彩空间 2.1色彩空间转换码 2.2实例 BGR色彩空间转换到GRAY色彩空间 3、HSV色彩空间 4、从BGR色彩空间转换到HSV色彩空间 4.1色彩空间转换码 4.2实例 BGR色彩空间转换到HSV色彩空间 1、GRAY色彩空间 GRAY色彩空间通常…

echarts案例网站

一、ppchart 网站:https://ppchart.com/#/ 二、echarts官网示例 网站:https://echarts.apache.org/examples/zh/index.html

Linux:通过VMWare,定制化Linux系统

一、原理图 二、新增磁盘(对应上图sdb盘) 三、挂载磁盘 主要是四步:查看磁盘,分区磁盘,格式化磁盘,挂载磁盘 1、查看磁盘 2、分区磁盘 3、格式化磁盘 4、挂载磁盘 创建两个备用目录,用于磁盘…

初刷leetcode题目(8)——数据结构与算法

😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️Take your time ! 😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️…

12 网关实战:Spring Cloud Gateway基础理论

为什么需要网关? 传统的单体架构中只有一个服务开放给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,那么作为客户端如何去调用这些微服务呢?如果没有网关的存在,只能在本地记录每个微服务的调用地址。 无网关的微服务架构往往存在以下问题: 客户端多次请求…

没有哈希时间锁定合约的跨链原子交换

在上一篇文章中,我们介绍了使用哈希时间锁定合约(HTLC)的跨链原子交换实现。 今天,我们介绍一种无需 HTLC 即可实现的替代方法。 这将原子交换扩展到缺乏哈希锁和时间锁的区块链。 使用 SPV 证明交易已被挖掘 让我们按照商定的价…

监控大屏 | 拐角OLED柔性屏:实现拐角处连惯拼接显示

监控大屏 | 拐角OLED柔性屏 产品:20块55寸OLED柔性屏 项目时间:2023年10月 项目地点:贵州 应用场景:在监控大厅三面墙都要装显示屏,利用OLED柔性屏可弯曲的特性,在两个捌角处进行拼接安装。 在2023年10…

力扣 hot100 最小覆盖子串 滑动窗口 字符计数

&#x1f468;‍&#x1f3eb; 题目地址 &#x1f37b; AC code class Solution {public String minWindow(String s, String t){int n s.length();int m t.length();if (n < m)return "";char[] tt t.toCharArray();int[] cnt new int[128];// 字符计数数组…

Docker 容器日志查看和清理

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

【Android】Android Framework系列--Launcher3各启动场景源码分析

Android Framework系列–Launcher3各启动场景源码分析 Launcher3启动场景 Launcher3是Android系统提供的默认桌面应用(Launcher)&#xff0c;它的源码路径在“packages/apps/Launcher3/”。 Launcher3的启动场景主要包括&#xff1a; 开机后启动&#xff1a;开机时&#xff…

[原创][1]探究C#多线程开发细节-“Thread类的简单使用“

[简介] 常用网名: 猪头三 出生日期: 1981.XX.XXQQ: 643439947 个人网站: 80x86汇编小站 https://www.x86asm.org 编程生涯: 2001年~至今[共22年] 职业生涯: 20年 开发语言: C/C、80x86ASM、PHP、Perl、Objective-C、Object Pascal、C#、Python 开发工具: Visual Studio、Delphi…

spring-boot集成mybatis-generator

通用 Mapper 在 1.0.0 版本的时候增加了 MyBatis Generator (以下简称 MBG) 插件&#xff0c;使用该插件可以很方便的生成实体类、Mapper 接口以及对应的 XML 文件。 下面介绍了 mybatis-generator 在 spring-boot 中的使用过程 一、引入pom依赖 <dependencies><de…

双通道 12V 直流电机驱动芯片GC8548,12V,大电流,具有短地短电源保护功能,可替代LV8548/LV8549/ONSEMI

GC8548 是一款双通道 12V 直流电机驱动芯 片&#xff0c;为摄像机、消费类产品、玩具和其他低压或 者电池供电的运动控制类应用提供了集成的电机 驱动解决方案。芯片一般用来驱动两个直流电机 或者驱动一个步进电机。 可以工作在 3.8~12V 的电源电压上&#xff0c; 每通道能提供…

事件机制?

事件流&#xff1a; 描述的页面接收事件的顺序。先进行事件捕获 到达目标元素 在进行事件冒泡 分为事件捕获和事件冒泡 事件冒泡&#xff1a;从具体元素从内向外依次触发事件 从下面这个小案例可以清楚了解什么是事件冒泡 <!DOCTYPE html> <html lang"en"…

Mysql之局域网内不同ip互登陆mysql

1 navicat修改mysql表中user> host改为% 2 重新加载mysql服务 3登陆mysql -h 192.168.x.xxx&#xff08;计算机ip&#xff09; -P 3306 -uroot -p123456&#xff08;密码&#xff09;