当前位置: 首页 > news >正文

go atomic原子操作详细解读

文章目录

  • 概要
  • 1、基本知识
    • 1.1 原子操作是什么
    • 1.2 CPU怎么实现原子操作的?
  • 2、atomic包
    • 2.1、 Add函数
    • 2.2、CompareAndSwap函数
    • 2.3、Swap函数
    • 2.4、Load函数
    • 2.5、Store函数
  • 3、atomic.Value值

概要

atomic包是golang通过对底层系统支持的原子操作进行封装,而提供的原子操作包,用于实现无锁化并发安全的操作数据。支持加法(Add)、比较并交换(CompareAndSwap)、直接交换(Swap)、设置指针变量值(Store)、获取指针变量值(Load);还设置了atomic.Value结构,支持对象存储、获取、比较并交换、直接交换操作。

atmoic包怎么用?以加法为例,变量加法运算操作并不是原子性的,包括从内存中取值放入加法寄存器、寄存器运算得到结果、将结果写回内存,在并发环境下,一个协程的运算结果可能被另一个覆盖,从而导致丢失修改的问题,例如以下代码的最后打印结果不会是10000。

var wg sync.WaitGroup
var num = 0func Add() {num++wg.Done()
}
func main() {size := 10000wg.Add(size)for i := 0; i < size; i++ {go Add()}wg.Wait()fmt.Println(num)
}

而以下代码时10000:

var wg sync.WaitGroup
var num int32 = 0func Add() {atomic.AddInt32(&num, 1)wg.Done()
}
func main() {size := 10000wg.Add(size)for i := 0; i < size; i++ {go Add()}wg.Wait()fmt.Println(num)
}

1、基本知识

1.1 原子操作是什么

原子操作是指在执行过程中不会被中断的操作,要么全部执行完成,要么完全不执行,不存在部分执行的情况。原子操作可以看作是一个不可分割的单元,其他线程或进程无法在其中间进行干扰或插入。
原子操作通常用于对共享数据进行读取、写入和修改等操作,以保证数据的一致性和正确性。在多线程或多进程环境下,如果多个线程或进程同时访问和修改同一份共享数据,没有正确的同步机制或使用原子操作可能会导致数据竞争和不确定的行为。
常见的原子操作包括:

  • 原子读(Atomic Read):从内存中获取一个共享变量的值,并确保其他线程或进程不会在读取过程中修改该值。
  • 原子写(Atomic Write):将一个值写入到共享变量,并确保其他线程或进程不会在写入过程中读取或修改该值。
  • 原子加(Atomic Add):将一个特定的值与共享变量相加,并将结果存回共享变量中,确保其他线程或进程不会在加法过程中干扰。
  • 原子比较并交换(Atomic Compare-and-Swap):比较共享变量的当前值与预期值是否相等,如果相等,则将新值写入共享变量;如果不相等,则不做任何修改。该操作常用于实现同步机制和锁。

1.2 CPU怎么实现原子操作的?

现在的CPU一般为多核处理器,它底层实现原子操作的方式有多种,下面介绍几种常见的方法:

  1. 总线锁定:在多核处理器中,可以使用总线锁定机制来确保原子操作。当一个处理器需要执行原子操作时,它会向总线发送一个请求锁定的信号。其他处理器在接收到该信号后,将暂停对总线的访问,以防止干扰正在进行的原子操作。只有当原子操作完成并释放锁定时,其他处理器才能继续对总线进行访问。

  2. 缓存一致性协议:多核处理器中的每个核心通常都具有自己的缓存,这就需要确保各个核心之间的缓存数据的一致性。缓存一致性协议可以通过在多个核心之间共享和更新处理器**缓存行(缓存在cpu核心本地缓存中的一条数据)**的状态信息来实现原子操作。常见的缓存一致性协议有MESI,多个核心更新数据时需要遵循以下原则:

    • Modified(修改)状态:当一个核心修改了一个缓存行中的数据时,该缓存行将被标记为“修改”状态。此时,其他核心的缓存中对应的缓存行就被视为无效(Invalid),即无效数据。

    • Exclusive(独占)状态:当一个核心从内存中加载了一个缓存行到自己的缓存中,并且该缓存行在其他核心的缓存中没有副本时,该缓存行处于“独占”状态。这时,其他核心需要读取或写入该数据时,必须通过该核心进行访问,独占状态的数据,可以直接进行修改。

    • Shared(共享)状态:当多个核心都拥有同一个缓存行的副本时,该缓存行处于“共享”状态。此时,对于读操作,其他核心可以直接从自己的缓存中读取数据。对于写操作,需要通过总线发出命令让其他缓存的状态更新为“失效”,该缓存中数据更新为独占,才能修改。

    • Invalid(无效)状态:当一个核心修改了一个缓存行的数据时,该缓存行在其他核心的数据状态会被标记为“无效”状态。这意味着其他核心需要重新从内存中加载最新的数据。

      总的逻辑是,当一个核心读取内存中数据在自己的本地缓存中并且只有该核心的缓存拥有该缓存行,这个缓存行的状态为独占状态,此时可直接进行读写操作,其它缓存读取该缓存行需要通过总线想该核心发送请求进行读取,此时缓存行状态变为共享。核心可以读取缓存行数据,但修改需要先通过总线发起修改请求,将其它核心的该缓存行数据置为无效,再修改缓存行中数据(看起来效率很低?其实cpu会马上修改该缓存行,修改完后缓存不会马上生效,而是放入store buffer中,再反送失效请求给所有核心,由store buffer等待全部核心响应成功,再生效缓存行的修改,这段期间核心可以继续做其它事情)。修改完成后,缓存行为修改状态,其他核心的该缓存行为无效状态,此时若有核心读取该缓存行,就将缓存行写入内存,该核心上的缓存行状态更新为共享;若是修改请求,就将核心缓存行状态更新为无效。

  3. 原子指令:现代的多核处理器通常会提供一些原子指令,例如比较并交换(compare and swap)、加载和存储条件(load-linked/store-conditional)等。这些指令能够以硬件级别的原子方式执行,从而保证对共享数据的访问是原子的。通过使用这些原子指令,可以避免锁的开销,并提供更高效的原子操作。

2、atomic包

Golang的atomic包的原子操作是通过CPU指令实现的。在大多数CPU架构中,原子操作的实现都是基于32位或64位的寄存器。Golang的atomic包的原子操作函数会将变量的地址转换为指针型的变量,并使用CPU指令对这个指针型的变量进行操作。
Golang的atomic包提供了一组原子操作函数,包括Add、CompareAndSwap、Load、Store、Swap等函数。这些函数的具体作用如下:

  • Add函数:用于对一个整数型的变量进行加法操作,并返回新的值。
  • CompareAndSwap函数:用于比较并交换一个指针型的变量的值。如果变量的值等于旧值,就将变量的值设置为新值,并返回true;否则,不修改变量的值,并返回false。
  • Load函数:用于获取一个指针型的变量的值。
  • Store函数:用于设置一个指针型的变量的值。
  • Swap函数:用于交换一个指针型的变量的值,并返回旧值。
    让我们更具体地来看一下Golang的atomic包的原子操作:

2.1、 Add函数

Add函数用于对一个整数型的变量进行加法操作,并返回新的值。Add函数的定义如下:

func AddInt32(addr *int32, delta int32) (new int32)
func AddInt64(addr *int64, delta int64) (new int64)
func AddUint32(addr *uint32, delta uint32) (new uint32)
func AddUint64(addr *uint64, delta uint64) (new uint64)
func AddUintptr(addr *uintptr, delta uintptr) (new uintptr)

其中,addr表示要进行加法操作的变量的地址,delta表示要加上的值。Add函数会将变量的值加上delta,并返回新的值。

2.2、CompareAndSwap函数

CompareAndSwap函数用于比较并交换一个指针型的变量的值。如果变量的值等于旧值,就将变量的值设置为新值,并返回true;否则,不修改变量的值,并返回false。CompareAndSwap函数的定义如下:

func CompareAndSwapInt32(addr *int32, old, new int32) (swapped bool)
func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool)
func CompareAndSwapUint32(addr *uint32, old, new uint32) (swapped bool)
func CompareAndSwapUint64(addr *uint64, old, new uint64) (swapped bool)
func CompareAndSwapUintptr(addr *uintptr, old, new uintptr) (swapped bool)
func CompareAndSwapPointer(addr *unsafe.Pointer, old, new unsafe.Pointer) (swapped bool)

其中,addr表示要进行比较和交换的变量的地址,old表示旧值,new表示新值。如果变量的值等于旧值,就将变量的值设置为新值,并返回true;否则,不修改变量的值,并返回false。

2.3、Swap函数

Swap函数用于交换一个指针型的变量的值,并返回旧值。Swap函数的定义如下:

func SwapInt32(addr *int32, new int32) (old int32)
func SwapInt64(addr *int64, new int64) (old int64)
func SwapUint32(addr *uint32, new uint32) (old uint32)
func SwapUint64(addr *uint64, new uint64) (old uint64)
func SwapUintptr(addr *uintptr, new uintptr) (old uintptr)
func SwapPointer(addr *unsafe.Pointer, new unsafe.Pointer) (old unsafe.Pointer)

其中,addr表示要交换的变量的地址,new表示新值。Swap函数会将变量的值设置为new,并返回旧值。

2.4、Load函数

Load函数用于获取一个指针型的变量的值。Load函数的定义如下

func LoadInt32(addr *int32) (val int32)
func LoadInt64(addr *int64) (val int64)
func LoadUint32(addr *uint32) (val uint32)
func LoadUint64(addr *uint64) (val uint64)
func LoadUintptr(addr *uintptr) (val uintptr)
func LoadPointer(addr *unsafe.Pointer) (val unsafe.Pointer)

2.5、Store函数

Store函数用于设置一个指针型的变量的值。Store函数的定义如下:

func StoreInt32(addr *int32, val int32)
func StoreInt64(addr *int64, val int64)
func StoreUint32(addr *uint32, val uint32)
func StoreUint64(addr *uint64, val uint64)
func StoreUintptr(addr *uintptr, val uintptr)
func StorePointer(addr *unsafe.Pointer, val unsafe.Pointer)

其中,addr表示要设置的变量的地址,val表示要设置的值。Store函数会将变量的值设置为val。

3、atomic.Value值

可以用来存储any类型对象的结构体,存取操作均是原子操作,具体方法如下:

type Value struct {v any
}
// 加载Value中存的值
func (v *Value) Load() (val any) {}
// 存储对象放入Value中
func (v *Value) Store(val any) {}
// 交换Value中存的数据
func (v *Value) Swap(new any) (old any) {)
// 比较并存储(值必须铜类型且可比较采用使用该方法,否则panic);传入old、new对象,如果old对象等于Value内存储的对象,就将新的对象存入,返回treu。否则false
func (v *Value) CompareAndSwap(old, new any) (swapped bool) {}

实践一下:

package mainimport ("fmt""sync/atomic"
)type Config struct {Addr string
}var config atomic.Valuefunc main() {conf1 := Config{Addr: "1.1.1.1",}conf2 := Config{Addr: "2.2.2.2",}config.Store(conf1)oldData := config.Swap(conf2)newData := config.Load()fmt.Println(oldData, newData)conf3 := Config{Addr: "3.3.3.3",}ok := config.CompareAndSwap(conf1, conf3)fmt.Println(ok, config.Load())ok = config.CompareAndSwap(conf2, conf3)fmt.Println(ok, config.Load())
}

输出:

{1.1.1.1} {2.2.2.2}
false {2.2.2.2}
true {3.3.3.3}

相关文章:

go atomic原子操作详细解读

文章目录 概要1、基本知识1.1 原子操作是什么1.2 CPU怎么实现原子操作的&#xff1f; 2、atomic包2.1、 Add函数2.2、CompareAndSwap函数2.3、Swap函数2.4、Load函数2.5、Store函数 3、atomic.Value值 概要 atomic包是golang通过对底层系统支持的原子操作进行封装&#xff0c;…...

Vue用JSEncrypt对长文本json加密以及发现解密失败

哈喽 大家好啊&#xff0c;最近发现进行加密后 超长文本后端解密失败&#xff0c;经过看其他博主修改 JSEncrypt原生代码如下&#xff1a; // 分段加密&#xff0c;支持中文JSEncrypt.prototype.encryptUnicodeLong function (string) {var k this.getKey();//根据key所能编…...

Excel/PowerPoint折线图从Y轴开始(两侧不留空隙)

默认Excel/PowerPoint折线图是这个样子的&#xff1a; 左右两侧都留了大块空白&#xff0c;很难看 解决方案 点击横坐标&#xff0c;双击&#xff0c;然后按下图顺序点击 效果...

C++的类成员对齐

这是个小语法点&#xff0c;之前我们的对齐方式都是使用#pragma pack&#xff0c;这个方式实际是依赖编译器&#xff0c;且粒度粗(如果#pragma pack(1)之后没有#pragma pack(),那就作用整个进程了)。在C11之后引入关键字alignas&#xff0c;以此来实现对齐更加便利&#xff0c;…...

敏感挂载userhelper容器逃逸复现

目录 前言 分析 实验 前言 分析 实验 # Creates a payload cat "#!/bin/sh" > /evil-helper cat "ps > /output" >> /evil-helper chmod x /evil-helper # Finds path of OverlayFS mount for container # Unless the configuration ex…...

深度解读Promise.prototype.finally

由一个问题引发的血案&#xff1a; 手写源码实现Promise.prototype.finally。 我们知道&#xff0c;对于promise来讲&#xff0c;当状态敲定&#xff0c;无论状态兑现或拒绝时都需要调用的函数&#xff0c;可以使用Promise.prototype.finally的回调来实现。那么如何手写实现Pro…...

如何实现24/7客户服务自动化?建设智能客服知识库

客户自助服务是指用户通过企业或者第三方建立的网络平台或者终端&#xff0c;实现相关的自定义处理。实现客户服务自动化&#xff0c;对提高客户满意度、维持客户关系至关重要。客户服务自动化可以帮助企业以更快的速度和更高的效率来满足客户的售后服务要求&#xff0c;以进一…...

和鲸 ModelWhale 与中科可控多款服务器完成适配认证,赋能中国云生态

当前世界正处于新一轮技术革命及传统产业数字化转型的关键期&#xff0c;云计算作为重要的技术底座&#xff0c;其产业发展与产业规模对我国数字经济的高质量运行有着不可取代的推动作用。而随着我国数字上云、企业上云加快进入常规化阶段&#xff0c;云计算承载的业务应用越来…...

selenium +Jmeter 的性能测试

通过Jmeter快速将已有的Selenium 代码以性能测试的方式组织起来&#xff0c;并使用JMeter 丰富的报表展示测试结果 from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By driver …...

探索高效的HTTP异步接口测试方法:从轮询等待到自动化方案

本文将深入探讨HTTP异步接口测试的多个方面&#xff0c;包括轮询等待、性能测试以及自动化方案。通过详细的解释和实际案例&#xff0c;帮助您了解如何有效地测试异步接口&#xff0c;确保系统的稳定性和性能。 在现代软件开发中&#xff0c;HTTP异步接口扮演着至关重要的角色&…...

Android资深工程书之LiveData核心组件原理剖析

LiveData是Android架构组件库中的一个类&#xff0c;用于在应用程序组件之间共享数据。它是一种可观察的数据持有者&#xff0c;可以感知应用程序组件的生命周期&#xff0c;并在数据发生变化时通知观察者。 使用LiveData 在Android应用程序中使用LiveData&#xff0c;你可以…...

Vue的五种方法实现加减乘除运算

五种方法的详细说明&#xff1a; 计算属性&#xff08;Computed Properties&#xff09;&#xff1a; 计算属性是Vue.js提供的一种便捷的属性&#xff0c;它根据依赖的数据动态计算出一个新的值。计算属性的值会被缓存&#xff0c;只有当依赖的数据发生变化时&#xff0c;才会…...

C++(1)Linux基础知识

经济下行&#xff0c;计算机就业形势严峻&#xff0c;为了勉励自己继续进步&#xff0c;继续学习代码提高核心竞争力。 安装QT Creator 首先&#xff0c;安装QT开发工具QT Creator 参考&#xff1a;2021最新Qt6开发环境&#xff08;Qt Creator&#xff09;安装以及卸载记录_q…...

接口自动化yaml文件读取与写入

前言 在走进yaml文件之前大家应该都很想知道他是用来干嘛的&#xff1f; 是的是的&#xff0c;他是用来做接口自动化测试的。 我们一起来学习他吧&#xff01;——&#xff08;一定要收藏带走哦❤&#xff09; 1、yaml文件有什么作用呢&#xff1f; ①可作为配置文件使用—…...

Java Map、JSONObject、实体类互转

文章目录 前言Map、JSONObject、实体类互转 前言 使用库 com.alibaba.fastjson2&#xff0c;可完成大部分JSON转换操作。 详情参考文章: Java FASTJSON2 一个性能极致并且简单易用的JSON库 Map、JSONObject、实体类互转 import com.alibaba.fastjson2.JSON; import com.alib…...

在Hive/Spark上执行TPC-DS基准测试 (PARQUET格式)

在上一篇文章:《在Hive/Spark上运行执行TPC-DS基准测试 (ORC和TEXT格式)》中,我们介绍了如何使用 hive-testbench 在Hive/Spark上执行TPC-DS基准测试,同时也指出了该项目不支持parquet格式。 如果我们想要生成parquet格式的测试数据,就需要使用其他工具了。本文选择使用另…...

基于CentOS搭建私有仓库harbor

环境&#xff1a; 操作系统&#xff1a;CentOS Linux 7 (Core) 内核&#xff1a; Linux 3.10.0-1160.el7.x86_64 目录 安装搭建harbor &#xff08;1&#xff09;安装docker编排工具docker compose &#xff08;2&#xff09;下载Harbor 安装包 &#xff08;3&…...

PDF怎么转Word?8 个最佳 PDF 转 Word 转换器

PDF 转 Word 转换工具只是一个特殊程序&#xff0c;可以将 PDF&#xff08;本机和/或扫描&#xff09;转换为 Microsoft Office Word 格式。将 PDF 导出到 Word 的主要原因之一是满足可编辑文档的需求&#xff0c;尽管还有其他原因。 由于缺少 PDF 阅读器&#xff0c;您可以选…...

老板都爱看的财务数据分析报表,全在这了

老板们都爱看哪些财务数据分析报表&#xff1f;自然是可以帮助他们更好地了解公司的财务状况和经营绩效的那一类财务数据分析报表&#xff0c;比如利润表、资产负债表、现金流量表、应收账款分析报表、应付账款分析报表、库存分析报表等。奥威BI数据可视化工具有一套标准化财务…...

ZooKeeper(zk)与 Eureka 的区别及集群模式比较分析

​ 作者&#xff1a;zhaokk 推荐阅读 AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI绘画、AI讲话、翻译,GPU点亮AI想象空间 资源分享 「java、python面试题」来自UC网盘app分享&#xff0c;打开手机app&#xff…...

搜狗拼音占用了VSCode及微信小程序开发者工具快捷键Ctrl + Shit + K 搜狗拼音截图快捷键

修改搜狗拼音的快捷键 右键--更多设置--属性设置--按键--系统功能快捷键--系统功能快捷键设置--取消Ctrl Shit K的勾选--勾选截屏并设置为Ctrl Shit A 微信开发者工具设置快捷键 右键--Command Palette--删除行 微信开发者工具快捷键 删除行&#xff1a;Ctrl Shit K 或…...

PMI-ACP值得考吗?在中国的前景如何?

相信很多小伙伴都听过PMP证书吧&#xff0c;但是对于PMI-ACP则知之甚少。那么同为项目管理证书&#xff0c;PMI-ACP认证的含金量怎么样呢&#xff1f;今天咱们就来聊一聊PMI-ACP敏捷项目管理证书。 PMI-ACP是由PMI&#xff08;美国项目管理协会&#xff09;颁发的针对敏捷项目…...

centos 安装防火墙,并开启对应端口号

1.查看防火墙状态&#xff1a; 命令&#xff1a;systemctl status firewalld.service 开启防火墙时&#xff0c;提示没有安装防火墙 [rootlocalhost ~]# systemctl start firewalld.service Failed to start firewalld.service: Unit not found.2.安装防火墙 [rootlocalhost …...

学习微信小程序时间延迟setTimeout和setInterval的使用方法

学习微信小程序时间延迟setTimeout和setInterval的使用方法 setTimeout()setInterval() setTimeout() setTimeout在使用的时候可以实现代码块延迟执行的效果&#xff0c;并且可以设置延迟执行的具体时间。请见如下代码&#xff1a; setTimeout(function() {//要实现延迟执行效…...

Vite好用的前端构建工具

是什么 Vite是Vue的作者尤雨溪开发的 一种新型前端构建工具。 Vite在大型项目开发模式下&#xff0c;打包速度远高于webpack。 Vite 为什么这么快 1. 快速冷启动 Vite只启动一台静态页面的服务器&#xff0c;不会打包全部项目文件代码&#xff0c;服务器根据客户端的请求加…...

Agile Iteration Velocity

【agile iteration velocity】敏捷速度指的平均速度 第四次迭代结束速度&#xff1a; 76 / 4 19 第五次迭代结束速度&#xff1a; &#xff08;76 24 &#xff09; / 5 100 / 5 20...

HarmonyOS/OpenHarmony应用开发-ArkTS语言渲染控制LazyForEach数据懒加载

LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当LazyForEach在滚动容器中使用了&#xff0c;框架会根据滚动容器可视区域按需创建组件&#xff0c;当组件划出可视区域外时&#xff0c;框架会进行组件销毁回收以降低内存占用。一、接…...

04_15页表缓存(TLB)和巨型页

前言 linux里面每个物理内存(RAM)页的一般大小都是4kb(32位就是4kb),为了使管理虚拟地址数变少 加快从虚拟地址到物理地址的映射 建议配值并使用HugePage巨型页特性 cpu和mmu和页表缓存(TLB)和cache和ram的关系 CPU看到的都是虚拟地址&#xff0c;需要经过MMU的转化&#xf…...

ResourceBundle类:读取配置文件

ResourceBundle类是java自带的类&#xff0c;类路径&#xff1a;java.util.ResourceBundle&#xff0c;用来读取项目中后缀为properties的配置文件。 下面简单举例说明一下用法&#xff1a; 数据准备 1&#xff09;配置文件名称&#xff1a;application.properties&#xff…...

数学建模的三大模型和十大常用算法

一、三大模型 预测模型 神经网络预测、灰色预测、拟合插值预测(线性回归)、时间序列预测、马尔科夫链预测、微分方程预测、Logistic模型等等。 应用领域&#xff1a;人口预测、水资源污染增长预测、病毒蔓延预测、竞赛获胜概率预测、月收入预测、销量预测、经济发展情况预测等在…...

广元市城乡规划建设监察大队网站/新媒体销售好做吗

分享一下我老师大神的人工智能教程&#xff01;零基础&#xff0c;通俗易懂&#xff01;http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识&#xff0c;造福人民&#xff0c;实现我们中华民族伟大复兴&#xff01;实验软件环境&#xff1a;虚拟机Vmware Work…...

asp net4.0网站开发/深圳企业网站制作公司

1、安装VMware Workstation。 2、下载MAC OS镜像&#xff08;.cdr文件&#xff09; 3、下载unlocker206 注&#xff1a;以上资源可百度、谷歌搜索进行下载。(如果未找到资源&#xff0c;请下面评论&#xff0c;谢谢合作) 4、确保VMware虚拟机完全关闭的情况下&#xff0c;以管理…...

python3网站开发/网站seo推广计划

在虚拟机中配置数据库的远程访问与本机配置类似&#xff0c;需要注意的是虚拟机网络设置 选择虚拟机设置--硬件--网络适配器&#xff0c;选择桥接模式&#xff1a;直接连接物理网络&#xff0c;不可选用主机模式&#xff08;与主机共享专用网络&#xff09;&#xff0c;其他模式…...

长春集团网站建设/北京seo相关

基于注意力&#xff08;Attention&#xff09;机制的端到端系统&#xff0c;又被称为LAS端到端构架。 [6] W. Chan, N. Jaitly, Q. Le, O. Vinyals. Listen, Attend and Spell: A Neural Network for Large Vocabulary Conversational Speech Recognition. ICASSP 2016. 来自 &…...

做网站用什么/企业邮箱怎么开通注册

MySQL数据字典相关的sql查询和navicat的一个导出ER图功能##mysql 查看表结构SELECTCOLUMN_NAME Field,COLUMN_TYPE Type,IS_NULLABLE Null,COLUMN_KEY Key,COLUMN_DEFAULT Default,COLUMN_COMMENT CommentFROMINFORMATION_SCHEMA.COLUMNSWHEREtable_schema dbname ##数据库名A…...

备案密码如何登录添加网站/深圳搜索引擎

点击 基本形状->软件和数据库->软件->企业应用 &#xff0c;企业应用有管与数据库的形状是圆柱体&#xff0c;具体如下图&#xff1a; 转载于:https://www.cnblogs.com/zhuangliu/p/6418573.html...