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

内存飙升,罪魁祸首竟是死锁,这样检测和处理减少一半加班时间

时间:2023-03-07 13:45:18  来源:今日头条  作者:

你是否因为写出死锁导致半夜加班,扣绩效?你是否为小白程序员,还没有接触过并发编程不知道什么死锁,你是否希望通过并发编程这块突破自己的瓶颈,在新的一年挑战高薪?那么JAVA并发编程中的死锁是你避不开的。

在通过redis或者zookeeper实现分布式锁时也可能出现死锁,本篇文章从Java线程入手,解密以下几点:

  • 什么是死锁,死锁如何产生
  • 通过有趣的案例实现死锁,并分析原因
  • 分析死锁产生的四个必要条件,并且解决死锁
  • 通过Java自带工具检测和定位死锁位置
  • 通过银行家算法,规避死锁问题

什么是死锁

死锁是进程死锁的简称,是由Dijkstra于1965年研究银行家算法时首先提出来的。它是计算机操作系统乃至并发程序设计中最难处理的问题之一。实际上,死锁问题不仅在计算机系统中存在,在我们日常生活中它也广泛存在。

我们来看一个死锁例子:

公司需要有工作经验的员工,而刚毕业的小伙伴需要工作来获得工作经验,这样企业和应届生之间就产生了死锁现象

这样的例子还有很多,比如:两辆车过桥

电影中的经典情节:我要的货呢,你带钱没有,最后一手交钱一手交货

所谓的死锁其实是一种现象,就是两个或两个以上线程的多线程情况下,多个线程同时被阻塞,它们中的一个或全部都在等待某一锁资源的释放,由于线程被无期限的阻塞,因此程序不会继续执行,表现为卡住不动。

如:线程1和线程2的运行都需要A资源和B资源,此时线程1获取了A资源,线程2获取到了B锁,此时线程1获取不到B锁和线程2获取不到A锁,导致两个线程彼此僵持!

多把锁场景

之前文章中的案例都是使用一把锁,死锁是线程需要多把锁才会出现,那么什么场景下需要多把锁呢?

案例

家中住着张三和翠花夫妻二人,家庭条件一般,只有一个厨房,希望实现翠花做饭和张三洗菜互不相干

一把锁解决

分析:

  • 定义一个厨房类,两个功能,分别为洗菜和做饭【煮粥不是炒菜】
  • 定义一把锁,直接将厨房锁上
  • 假设洗菜需要1秒,做饭需要2秒
  • 洗菜和做饭时使用唯一的一把厨房锁,将整个厨房锁上,实现互不打扰

厨房类:

package com.tianzhen.thread;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class KitchenRoom {

    // 锁对象
    public Object lock = new Object();

    // 洗菜
    public void washing() {
        // 锁住房间
        synchronized (lock) {
            // 输出开始时间 + 操作
            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss SSS")) + ":洗菜");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    // 做饭【煮粥】
    public void cook() {
        // 锁上房间
        synchronized (lock) {
            // 输出开始时间 + 操作
            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss SSS")) + ":做饭");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

运行结果:

 

 

使用一把锁的时候性能较低,因为锁的范围太大了,直接将厨房锁住,只需将房间内的每一个功能单独锁起来即可,比如:单独将洗菜,做饭,使用冰箱锁住,不能多人同时使用,应该将锁细化,这样同一个房间就可以同时做很多工作,厨房的利用率就会上来,洗菜和做饭可以同步进行,这样是不就可以早点吃上美味了呢!

厨房改造:

package com.tianzhen.thread;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class KitchenRoom {

    // 洗菜锁
    public Object washLock = new Object();

    // 做饭锁
    public Object cookLock = new Object();

    // 洗菜
    public void washing() {
        // 使用洗菜锁
        synchronized (washLock) {
            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss SSS")) + ":洗菜");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
    // 做饭
    public void cook() {
        // 使用做饭锁
        synchronized (cookLock) {
            System.out.println(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss SSS")) + ":做饭");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

运行结果:

发现做饭【煮粥】和洗菜是同时开始的,通过细化锁,可以提升程序性能,必须要保障两个操作没有关联性,比如煮粥不需要等菜洗好,如果是炒菜,就需要等待菜洗好才可以进行。

 

 

锁细粒度化的好处是:提高程序等性能,弊端在于:如果一个线程同时需要多把锁,就可能产生死锁

死锁现象

以上边的企业和面试者为例演示死锁,企业招工需要有工作经验的程序员,但是添甄刚毕业,没有工作经验,需要有工作才能获取工作经验,这样就导致企业招不到人,面试者找不到工作的尴尬境地!

分析

  • 这里有两个条件,一个是工作经验,一个是工作
  • 企业先验证面试者是否有工作经验,才会给工作机会
  • 面试者需要先获取工作,才能还有工作经验
  • 两者如果僵持不下,就会产生死锁

代码实现

package com.tianzhen.thread;

public class Deadhread {

    // 工作锁
    private static Object work = new Object();
    // 工作经验锁
    private static Object workExperience = new Object();
    
    public static void mAIn(String[] args) {
        // 企业线程
        new Thread(() -> {
            // 先锁定工作经验
            synchronized (workExperience) {
                System.out.println(Thread.currentThread().getName() + "我们需要有工作经验的");
                // 给工作机会
                synchronized (work) {
                    System.out.println(Thread.currentThread().getName() + "恭喜你通过面试加入我们");
                }
            }
        },"企业线程:").start();
        // 员工线程
        new Thread(() -> {
            // 先获取工作机会
            synchronized (work) {
                System.out.println(Thread.currentThread().getName() + "我需要工作才能有工作经验");
                // 有工作经验
                synchronized (workExperience) {
                    System.out.println(Thread.currentThread().getName() + "通过面试获得了工作经验");
                }
            }
        },"面试者线程:").start();
    }
}

运行结果:发现程序再企业和面试者各输出一句之后卡死不动

 

 

原因

 

 

图解

 

 

简单的说就是:我需要的东西你占着,你需要的东西我占着,而且我们都不会脑筋急转弯,就傻傻的等着对方让步,拿到自己需要的东西之后继续玩,但是大家都这么想那就谁也别玩了。

死锁产生的四个必要条件

  • 互斥使用:即当资源被一个线程使用(占有)时,别的线程不能使用
  • 不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放
  • 请求和保持:即当资源请求者在请求其他的资源的同时保持对原有资源的占有
  • 循环等待:即存在一个等待队列:企业线程占有workExperience锁资源,面试者线程占有 work锁 资源,面试者线程需要workExperience锁资源,企业线程需要work锁资源,彼此等待对方释放资源。这样就形成了一个等待环路

当上述四个条件都成立的时候,便形成死锁。当然,死锁的情况下如果打破上述任何一个条件,便可让死锁消失。

死锁检测

死锁检测其实非常简单,这里介绍两种方式监测死锁,如果你有更好的办法或工具记得在评论区分享哦!

方式1:命令检测

  • 死锁就会导致程序卡死不动,它的特点就是占用内存比较多,首先找到占用内存多的Java进程
  • 其次通过jps命令找到对应的java进程号
  • 通过jstack 进程号得到进程信息
  • 通过进程信息查看是否是死锁,发生在什么地方

1、window下通过任务管理器查看进程内存占用情况,linux下通过 top 命令查看,这里以window为例

 

 

2、通过jps命令获取该进程的进程号也就是PID

 

 

3、通过 jstack PID 查看进程信息

 

 

接下来的信息:在jstack输出的信息中最后出现了死锁提示,提示显示在DeadThread.java文件的第39行和23行,那你去排查代码就可以啦

 

 

方式2:通过jconsole工具

这个工具在查看JVM内存时也是可以使用的,它是JDK中携带官方提供的工具,无需下载第三方插件即可使用

1、打开 jconsole 工具,在命令行输入jconsole即可开启,箭头右侧就是该工具启动页

 

 

2、选择对应的Java进程查看信息,双击选中PID为128的Java进程

 

 

3、选中线程,点击下方检查死锁按钮

 

 

4、死锁检测结果,也会将死锁的信息展示出开【右侧信息需要双击左侧线程名才会展示出来】

 

 

如何避免死锁

这里说的避免死锁,其实是在生产环境中也就是项目上线运行不要出现死锁,不然又要被喊过去加班了,上边说了死锁产生的四个条件,只要我们将这四个条件中的任意一个破坏就不会产生死锁。

  • 禁止一个线程同时持有多把锁
  • 具备相同的加锁顺序
  • 设置锁超时
  • 死锁检测

方案1:具备相同加锁顺序

比如,企业和面试者的案例,调换两者加锁顺序一致即可解决死锁问题

package com.tianzhen.thread;

public class DeadThread {

    // 工作锁
    private static Object work = new Object();
    // 工作经验锁
    private static Object workExperience = new Object();
    
    public static void main(String[] args) {
        // 企业线程
        new Thread(() -> {
            // 工作
            synchronized (work) {
                System.out.println(Thread.currentThread().getName() + "来吧!加入我们,有无经验都可");
                // 工作经验
                synchronized (workExperience) {
                    System.out.println(Thread.currentThread().getName() + "感谢你的加入,为我们注入新鲜血液");
                }
            }
        },"企业线程:").start();
        // 员工线程
        new Thread(() -> {
            // 先获取工作机会
            synchronized (work) {
                System.out.println(Thread.currentThread().getName() + "我没有工作经验");
                // 有工作经验
                synchronized (workExperience) {
                    System.out.println(Thread.currentThread().getName() + "通过工作获得了工作经验");
                }
            }
        },"面试者线程:").start();

    }
}

运行结果:

 

 

此时就不会出现死锁,当企业线程运行占用work锁,这是如果发生线程切换,面试者也是要获取work锁,此时发现获取不到,就会进入阻塞,CPU放弃执行转而执行企业线程,此时企业线程获取workExperience锁,因为加锁顺序相同,此锁必然没有被比别的线程占用可以获得,继续执行,但是此时就无法实现交替执行,如果需要交替执行则需要使用线程通信实现,后边会安排此部分内容

方案2:设置超时

因为 synchronized 不会自动释放,无法设置超时时间,此方案需要通过Lock接口实现,改接口在Java并发编程合集的《Java线程安全问题和解决方案》一文中有详细介绍

  • 通过tryLock尝试获取锁,如果获取不到就立即失败,不进入阻塞,你也可以调用tryLock(long time, TimeUnit unit)方法,设置获取所得超时时间,如果指定的时间没有获取到则继续运行
  • 在finally中记得通过unlock方法释放锁,如果不释放锁,就会一直持有,陷入死锁
package com.tianzhen.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class DeadThread {

    // 工作锁
    private static Lock work = new ReentrantLock();
    // 工作经验锁
    private static Lock workExperience = new ReentrantLock();

    public static void main(String[] args) {
        // 企业线程
        new Thread(() -> {
            // 工作

            if (workExperience.tryLock()) {
                try {
                    System.out.println(Thread.currentThread().getName() + "没有工作经验,立即失败!");
                    // 工作锁
                    if (work.tryLock()) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "技术不行,立即失败!");
                        } finally {
                            work.unlock();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "有工作经验,通过面试,欢迎加入我们!");
                } finally {
                    workExperience.unlock();
                }
            }
        }, "企业线程:").start();
        // 员工线程
        new Thread(() -> {

            if (work.tryLock()) {
                try {
                    System.out.println(Thread.currentThread().getName() + "我需要工作,才能有工作经验!");
                    // 工作锁

                    if (workExperience.tryLock()) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "没有工作经验,告辞告辞!");
                        } finally {
                            workExperience.unlock();
                        }
                    }
                    System.out.println(Thread.currentThread().getName() + "有工作经验,获取工作!");
                } finally {
                    work.unlock();
                }
            }
        }, "面试者线程:").start();

    }
}

此方法一定要记得调用unlock释放锁,同样可以解决死锁问题,因为不会像 synchronized 一样无脑等待,而是非常机智,如果拿不到就不要了,就好比追一个女孩子,追了三年还不行就放弃吧,而synchronized就是永不言弃,等到天荒地老,非常痴情。倾我半世阳光,许你天荒地老

你是不有更好的解决方案,赶紧掏出来唠唠吧!

银行家算法中避免死锁思维

银行家算法是一种最有代表性的避免死锁的算法。又被称为资源分配拒绝法。 在避免死锁方法中允许进程动态地申请资源,但系统在进行资源分配之前,应先计算此次分配资源的安全性,若此次分配不会导致系统进入不安全状态,则将资源分配给线程,否则进程等待

银行家算法中的数据结构

1、可利用资源向量Available 是个含有m个元素的数组,其中的每一个元素代表一类可利用的资源数目。如果Available[j]=K,则表示系统中现有Rj类资源K个。

2、最大需求矩阵Max 这是一个n×m的矩阵,它定义了系统中n个进程中的每一个进程对m类资源的最大需求。如果Max[i,j]=K,则表示进程i需要Rj类资源的最大数目为K。

3、分配矩阵Allocation 这也是一个n×m的矩阵,它定义了系统中每一类资源当前已分配给每一进程的资源数。如果Allocation[i,j]=K,则表示进程i当前已分得Rj类资源的数目为K。

4、需求矩阵Need 这也是一个n×m的矩阵,用以表示每一个进程尚需的各类资源数。如果Need[i,j]=K,则表示进程i还需要Rj类资源K个,方能完成其任务。 Need[i,j]=Max[i,j]-Allocation[i,j]

操作系统的两种状态

安全序列:是指一个进程序列{P1,…,Pn}是安全的,即对于每一个进程Pi(1≤i≤n),它以后尚需要的资源量不超过系统当前剩余资源量与所有进程Pj (j < i )当前占有资源量之和。

1、安全状态:如果存在一个由系统中所有进程构成的安全序列P1,…,Pn,则系统处于安全状态。安全状态一定是没有死锁发生。

2、不安全状态:不存在一个安全序列。不安全状态不一定导致死锁。

示例

 

 

首先判断一下当前的安全序列: 当前状态,可利用资源向量有 1 6 2 2

1、P0: 已分配 0 0 3 2, 还需要 0 0 1 2,当前可利用资源 1 6 2 2足够分配给P0; Process Allocation Need Available(Available-Need) Available+Allocation P0 0 0 4 4 0 0 0 0 1 6 1 0 1 6 5 4 P0分配成功:进入安全序列,分配完成后,将资源还给可利用资源

2、P1:已分配1 0 0 0, 还需要 1 7 5 0,当前可利用资源 1 6 5 4不够分配给P1; P1分配失败

3、P2: 已分配1 3 5 4, 还需要 2 3 5 6,当前可利用资源 1 6 5 4不够分配给P2; P2分配失败

4、P3: 已分配 0 3 3 2, 还需要 0 6 5 2,当前可利用资源 1 6 5 4足够分配给P3; Process Allocation Need Available(Available-Need) Available+Allocation P3 0 9 8 4 0 0 0 0 1 0 0 2 1 9 8 6 P3分配成功,进入安全序列,分配完成后,将资源还给可利用资源

5、P4: 已分配 0 0 1 4, 还需要 0 6 5 6,当前可利用资源 1 9 8 6足够分配给P4; Process Allocation Need Available(Available-Need) Available+Allocation P4 0 6 6 10 0 0 0 0 1 3 3 0 1 9 9 10 P4分配成功,进入安全序列,分配完成后,将资源还给可利用资源

6、P1: 已分配 1 0 0 0, 还需要 1 7 5 0,当前可利用资源 1 9 9 10足够分配给P1; Process Allocation Need Available(Available-Need) Available+Allocation P1 2 7 5 0 0 0 0 0 0 2 4 10 2 9 9 10 P1分配成功,进入安全序列,分配完成后,将资源还给可利用资源

7、P2: 已分配 1 3 5 4, 还需要 2 3 5 6,当前可利用资源 2 9 9 10足够分配给P2; Process Allocation Need Available(Available-Need) Available+Allocation P2 3 6 10 10 0 0 0 0 0 6 4 4 3 12 14 14 P4分配成功,进入安全序列,分配完成后,将资源还给可利用资源

所以:当前的安全序列为: p0-p3-p4-p1-p2

如果在未分配的时候:p2请求 1 2 2 2 ,从资源池里给他分配,请问可以分配吗?

答: 如果满足了P2的请求1 2 2 2 的话,要从可利用资源Available 1 6 2 2 中减去1 2 2 2,此时可利用资源为0 4 0 0 , 纵观全局,如果满足了P2的请求,那么别的进程的需求都不能满足,导致资源不够分配,所以P2的请求不可以分配

Java代码实现银行家算法

import java.util.Scanner;

public class Banker {
    int available[] = new int[]{3,3,2};//可利用的资源
    int max[][] = new int[][]{{7,5,3},{3,2,2},{9,0,2},{2,2,2},{4,3,3}};//每个进程最大资源数
    int allocation[][] = new int[][]{{0,1,0},{2,0,0},{3,0,2},{2,1,1},{0,0,2}};//每个进程目前拥有的资源数
    int need[][] = new int[][]{{7,4,3},{1,2,2},{6,0,0},{0,1,1},{4,3,1}};//每个进程需要的资源数
    void showData() {
    //展示数据输出每个进程的相关数
        System.out.println("进程号   Max            All            Need   ");
        System.out.println("      A  B  C       A  B  C      A  B  C");
        for(int i = 0;i<5;i++){
            System.out.print(i+"    ");
            for(int m = 0;m<3;m++) System.out.print(max[i][m]+"  ");
            for(int m = 0;m<3;m++) System.out.print(allocation[i][m]+"  ");
            for(int m = 0;m<3;m++) System.out.print(need[i][m]+"  ");
            System.out.println();
        }
    }

    boolean change(int inRequestNum,int inRequest[])//分配数据
    {
        int requestNum = inRequestNum;
        int request[] = inRequest;
        // for(int i=0;i<3;i++)System.out.println("修改前available"+available[i]);
        if(!(request[0]<=need[requestNum][0]&&request[1]<=need[requestNum][1]&&request[2]<=need[requestNum][2]))
        {
            //request[0]<=need[requestNum][0]
            //request[1]<=need[requestNum][1]
            //request[2]<=need[requestNum][2]
            //每一类请求资源小于当前线程need的资源数
            System.out.println("请求的资源数超过了所需要的最大值,分配错误");
            return false;
        }
        if((request[0]<=available[0]&&request[1]<=available[1]&&request[2]<=available[2])==false)
        {
            //当前线程的每一类请求资源小于等于资源池对应资源的数量
            System.out.println("尚无足够资源分配,必须等待");
            return false;
        }

        for(int i = 0;i<3;i++)//试分配数据给请求的线程
        {
            available[i] = available[i]-request[i];
            //资源池的每类资源减去每类请求资源数量
            allocation[requestNum][i] = allocation[requestNum][i] + request[i];
            //当前线程allocation中每类资源加上每类资源请求数量
            need[requestNum][i] = need[requestNum][i] - request[i];
            //当前线程need中每类资源数量减去每类资源的请求数量
        }
// for(int i=0;i<3;i++)System.out.println("修改后available"+available[i]);
        boolean flag = checkSafe(available[0],available[1],available[2]);//进行安全性检查并返回是否安全
// System.out.println("安全性检查后"+flag);
        if(flag==true)
        {
            System.out.println("能够安全分配");
            return true;
        }
        else//不能通过安全性检查 恢复到未分配前的数据
        {
            System.out.println("不能够安全分配");
            for(int i = 0;i<3;i++)
            {
                available[i] = available[i]+request[i];
                allocation[requestNum][i] = allocation[requestNum][i] - request[i];
                need[requestNum][i] = need[requestNum][i] + request[i];
            }
            return false;
        }
    }
    boolean checkSafe(int a,int b,int c)//安全性检查
    {
        int work[] = new int[3];
        work[0] = a;
        work[1] = b;
        work[2] = c;
        int i=0;
        boolean finish[] = new boolean[5];
        while(i<5)//寻找一个能够满足的认为完成后才去执行下一进程
        {
            if(finish[i]==false&&need[i][0]<=work[0]&&need[i][1]<=work[1]&&need[i][2]<=work[2])
            {//找到满足的修改work值,然后i=0,重新从开始的为分配的中寻找
                System.out.println("分配成功的是"+i);
                for(int m = 0;m<3;m++)
                    work[m] =work[m] + allocation[i][m];
                finish[i] = true;
                i=0;
            }
            else//如果没有找到直接i++
                i++;
        }
        for(i=0;i<5;i++)//通过finish数组判断是否都可以分配
        {
            if(finish[i]==false)
                return false;
        }
        return true;
    }
    public static void main(String[] args)
    {
        Banker bank = new Banker();
        bank.showData();
        //请求线程资源存放的数组
        int request[] =new int[3];
        int requestNum;
        String source[] = new String[]{"A","B","C"};
        Scanner s = new Scanner(System.in);
        String choice = new String();
        while(true)//循环进行分配
        {
            System.out.println("请输入要请求的进程号(0--4):");
            requestNum = s.nextInt();
            System.out.print("请输入请求的资源数目");
            for(int i = 0;i<3;i++)
            {
                System.out.println(source[i]+"资源的数目:");
                request[i] = s.nextInt();
            }
            bank.change(requestNum, request);
            System.out.println("是否再请求分配(y/n)");
            choice = s.next();
            if(choice.equals("n"))
                break;
        }
    }
}

运行结果:

 

 

总结

  • 掌握死锁是什么,怎么产生
  • 可以写出死锁代码证明对死锁的理解
  • 可以通过工具检测死锁和解决死锁问题
  • 掌握死锁思维,在编程时避免死锁


Tags:死锁   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  Search: 死锁  点击:(49)  评论:(0)  加入收藏
MySQL事务中遇到死锁问题该如何解决?
在并发访问下,MySQL事务中的死锁问题是一种常见的情况。当多个事务同时请求和持有相互依赖的资源时,可能会出现死锁现象,导致事务无法继续执行,严重影响系统的性能和可用性。死...【详细内容】
2024-01-10  Search: 死锁  点击:(104)  评论:(0)  加入收藏
多个线程或进程竞争共享资源而导致的死锁问题
死锁是多线程或多进程并发编程中常见的问题之一,它会导致程序无法继续执行下去,造成系统资源的浪费和性能下降。在Java项目中,当多个线程或进程竞争共享资源时,如果不恰当地处理...【详细内容】
2023-12-07  Search: 死锁  点击:(150)  评论:(0)  加入收藏
解锁多线程死锁之谜:深入探讨使用GDB调试的技巧
多线程编程是现代软件开发中的一项重要技术,但随之而来的挑战之一是多线程死锁。多线程死锁是程序中的一种常见问题,它会导致线程相互等待,陷入无法继续执行的状态。这里,我们将...【详细内容】
2023-11-23  Search: 死锁  点击:(170)  评论:(0)  加入收藏
MySQL 事务死锁问题排查
一、背景 在预发环境中,由消息驱动最终触发执行事务来写库存,但是导致 MySQL 发生死锁,写库存失败。com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: rpc...【详细内容】
2023-09-27  Search: 死锁  点击:(376)  评论:(0)  加入收藏
从一个死锁问题分析优化器特性
作者:李锡超,一个爱笑的江苏苏宁银行 数据库工程师,主要负责数据库日常运维、自动化建设、DMP 平台运维。擅长 MySQL、Python、Oracle,爱好骑行、研究技术。爱可生开源社区出品...【详细内容】
2023-09-22  Search: 死锁  点击:(179)  评论:(0)  加入收藏
一个 MySQL 数据库死锁的案例和解决方案
本文介绍了一个 MySQL 数据库死锁的案例和解决方案。场景生产环境出了一个偶现的数据库死锁问题,导致少部分业务处理失败。分析特征之后,发现是多个线程并发执行同一个方法,更...【详细内容】
2023-09-01  Search: 死锁  点击:(286)  评论:(0)  加入收藏
Mybatis-Plus可能会导致数据库死锁
一、场景还原1.版本信息MySQL版本:5.6.36-82.1-logMybatis-Plus的starter版本:3.3.2存储引擎:InnoDB2.死锁现象A同学在生产环境使用了Mybatis-Plus提供的com.baomidou.mybatisp...【详细内容】
2023-08-14  Search: 死锁  点击:(171)  评论:(0)  加入收藏
Oracle 死锁与慢查询总结
查看死锁SELECT s.sid "会话ID",s.lockwait "等待锁",s.event "等待的资源/事件", -- 最近等待或正在等待的资源/事件DECODE(lo.locked_mode, 0, &#39;尚未获得锁&#39;, 1,...【详细内容】
2023-05-22  Search: 死锁  点击:(108)  评论:(0)  加入收藏
在 Linux 内核中调试 FUSE 死锁
Netflix 的计算团队负责管理 Netflix 的所有 AWS 和容器化工作负载,包括自动缩放、容器部署、问题修复等。作为该团队的一员,我致力于修复用户报告的奇怪问题。这个特殊问题...【详细内容】
2023-05-21  Search: 死锁  点击:(423)  评论:(0)  加入收藏
▌简易百科推荐
向量数据库落地实践
本文基于京东内部向量数据库vearch进行实践。Vearch 是对大规模深度学习向量进行高性能相似搜索的弹性分布式系统。详见: https://github.com/vearch/zh_docs/blob/v3.3.X/do...【详细内容】
2024-04-03  京东云开发者    Tags:向量数据库   点击:(5)  评论:(0)  加入收藏
原来 SQL 函数是可以内联的!
介绍在某些情况下,SQL 函数(即指定LANGUAGE SQL)会将其函数体内联到调用它的查询中,而不是直接调用。这可以带来显著的性能提升,因为函数体可以暴露给调用查询的规划器,从而规划器...【详细内容】
2024-04-03  红石PG  微信公众号  Tags:SQL 函数   点击:(5)  评论:(0)  加入收藏
如何正确选择NoSQL数据库
译者 | 陈峻审校 | 重楼Allied Market Research最近发布的一份报告指出,业界对于NoSQL数据库的需求正在持续上升。2022年,全球NoSQL市场的销售额已达73亿美元,预计到2032年将达...【详细内容】
2024-03-28    51CTO  Tags:NoSQL   点击:(14)  评论:(0)  加入收藏
为什么数据库连接池不采用 IO 多路复用?
这是一个非常好的问题。IO多路复用被视为是非常好的性能助力器。但是一般我们在使用DB时,还是经常性采用c3p0,tomcat connection pool等技术来与DB连接,哪怕整个程序已经变成以...【详细内容】
2024-03-27  dbaplus社群    Tags:数据库连接池   点击:(13)  评论:(0)  加入收藏
八个常见的数据可视化错误以及如何避免它们
在当今以数据驱动为主导的世界里,清晰且具有洞察力的数据可视化至关重要。然而,在创建数据可视化时很容易犯错误,这可能导致对数据的错误解读。本文将探讨一些常见的糟糕数据可...【详细内容】
2024-03-26  DeepHub IMBA  微信公众号  Tags:数据可视化   点击:(7)  评论:(0)  加入收藏
到底有没有必要分库分表,如何考量的
关于是否需要进行分库分表,可以根据以下考量因素来决定: 数据量和负载:如果数据量巨大且负载压力较大,单一库单一表可能无法满足性能需求,考虑分库分表。 数据增长:预估数据增长...【详细内容】
2024-03-20  码上遇见你  微信公众号  Tags:分库分表   点击:(15)  评论:(0)  加入收藏
在 SQL 中写了 in 和 not in,技术总监说要炒了我……
WHY?IN 和 NOT IN 是比较常用的关键字,为什么要尽量避免呢?1、效率低项目中遇到这么个情况:t1表 和 t2表 都是150w条数据,600M的样子,都不算大。但是这样一句查询 &darr;select *...【详细内容】
2024-03-18  dbaplus社群    Tags:SQL   点击:(6)  评论:(0)  加入收藏
应对慢SQL的致胜法宝:7大实例剖析+优化原则
大促备战,最大的隐患项之一就是慢SQL,对于服务平稳运行带来的破坏性最大,也是日常工作中经常带来整个应用抖动的最大隐患,在日常开发中如何避免出现慢SQL,出现了慢SQL应该按照什...【详细内容】
2024-03-14  京东云开发者    Tags:慢SQL   点击:(5)  评论:(0)  加入收藏
过去一年,我看到了数据库领域的十大发展趋势
作者 | 朱洁策划 | 李冬梅过去一年,行业信心跌至冰点2022 年中,红衫的一篇《适应与忍耐》的报告,对公司经营提出了预警,让各个公司保持现金流,重整团队,想办法增加盈利。这篇报告...【详细内容】
2024-03-12    InfoQ  Tags:数据库   点击:(27)  评论:(0)  加入收藏
SQL优化的七个方法,你会哪个?
一、插入数据优化 普通插入:在平时我们执行insert语句的时候,可能都是一条一条数据插入进去的,就像下面这样。INSERT INTO `department` VALUES(1, &#39;研发部(RD)&#39;, &#39...【详细内容】
2024-03-07  程序员恰恰  微信公众号  Tags:SQL优化   点击:(20)  评论:(0)  加入收藏
站内最新
站内热门
站内头条