猴子都能看懂的A星算法原理

article/2025/10/2 21:45:46

文章目录

    • A星算法基本原理
      • 什么是寻路算法
      • 算法的思路
    • 算法实现
      • 脚本1————cconst.cs
      • 脚本2————AStar.cs
    • Unity演示
      • 演示样例一
      • 演示样例二
      • 演示样例三
      • 演示样例四

俗话说,好记性不如烂笔头,对于有了解过寻路算法的同学,对于A星算法应该不陌生;为了巩固下这个算法的理解,所以利用Unity演示了算法的过程;本文的基本构成分为 基本原理+ 算法实现+ Unity演示三个步骤。

A星算法基本原理

什么是寻路算法

寻路算法是在指定地图中,NPC可以根据起始点和目标点,计算出一条比较合理的链接路线(通常需要最短路径);在地图中,路点可以分为两种,一种是普通路点,一种是障碍路点(墙、水、坑等),算法的目的就是要绕过障碍物,使得NPC尽可能的走最短路线到达目标点(targetPoint)。

对于最基本的A星寻路算法,是把一张地图分为一个二维数组,每一个格子代表一个路点;或者如下图如所示,将一张地图分为一个一个的格子,格子为正方形,下方的绿色格子周围有8个相邻的格子;

在这里插入图片描述

算法的思路

对于上述九宫格图,假设最小方格的边长是10,那么从绿色格子出发,到达红色格子的距离为 200 \sqrt{200} 200 ,这里可以简化为14,因为这样可以简化算法的计算难度(浮点数比整数的计算复杂);到达黄色格子的距离为10;
每一个路点都有一个评估值F,其中 F = G + H F = G + H F=G+H,G值是起点移动到指定方格的移动代价,H是指定的方格移动到终点的估算成本,H值的计算方式有如下两种:

方式一:计算横向和纵向移动的距离,不能斜着走;
在这里插入图片描述
方式二:比如该点和目标点为对角顶点组成的举行的边长是a 和 b,那么H值是 m i n ( a , b ) × 14 + a b s ( a − b ) × 10 min(a, b) \times 14 + abs(a - b) \times 10 min(a,b)×14+abs(ab)×10


在这里插入图片描述
本文算法选择的是方式二,H值的计算需要注意的地方是,不需要考虑障碍物,只需考虑起始点和目标点即可。对于F、G、H三个评估值的什么这么命名,我也不清楚,猜测是就和我们平时用来替代临时变量用的i、j、k一样,仅仅是26英文字母中连续的三个而已,方便记忆😎;

这个算法需要两个数组,openList和closeList,openList里面存放的是当前状态可以到达的路点,closeList存放不能到达的路点和已经经过判断的路点;具体步骤如下:

  1. 从当前选中路点a出发,总共有8个路点可能到达,假设可到达的节点为b,如果b是障碍点,则直接加入closeList;如果b在openList中,当且仅当b的G值小于点a的G值时,更新b的G值,并且将路点b的父节点设置为当前a;如果b在closeList中,则跳过该点的判断。否者初始化G值和H值,并将该节点b加入到openlist列表中;最后将初始路点a移出openlist。
  2. 如果目标点进入了openList,则遍历停止,找到可到达路径,由于每个节点都保存着到达自己的父节点,所以拿到目标点,一直拿到它的父节点就可以找到到达路径;如果openList为空时还没找都目标路点,则不可到达,算法结束;否则进入下一个步骤;
  3. 遍历openList的路点,选出F值最小的那个点,如果有多个最小值,可以选择第一个最小值,也可以选择多个最小值中的最后一个,最后的表现就是到达目标点的最短路径可能有多条;然后把这个点当做选中路点,进行步骤1;

算法实现

这里列出算法用的大部分代码,其中最关键的就是RefreshFind函数和AddOpenlistNode函数,执行的就是算法的关键部分----遍历openList然后不断的进行判断,直到找到目标路点或者openList为空,如果有不理解的地方,请留言。

脚本1————cconst.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;namespace AStar
{public static class cconst{public static int NormalTile = 0;public static int WaterTile = 1;public static int DestinationTile = 2;public static int MarkTile = 3;public static int FindTile = 4;public static int StartTile = 5;public static int RowLineNum = 16;  // 行数public static int ColLineNum = 32;  // 列数public static Vector2 startPos = new Vector2(-617, 298);public static float itemRate = 1.0f;public static float width = 40 * itemRate;public static float height = 40 * itemRate;public static int SlideDis = 14;public static int UnSlideDis = 10;public static int slideValNum = 4;public static List<Vector2> UnSlideDisVal = new List<Vector2> { new Vector2(-1, 0), new Vector2(0, 1), new Vector2(1, 0), new Vector2(0, -1) };public static List<Vector2> SlideDisVal = new List<Vector2> { new Vector2(-1, -1), new Vector2(-1, 1), new Vector2(1, 1), new Vector2(1, -1) };//障碍点类型public static int BattleNum = 5;public static int BattleTypeNum = 3;public static Dictionary<int, List<List<int>>> BattleTypeDic = new Dictionary<int, List<List<int>>>{{ 0, new List<List<int>>{ new List<int> {1,0,0 }, new List<int> { 1,1,0 }, new List<int> { 1,1,1 },new List<int> { 1,1,1 }, new List<int> { 1,1,0 }, new List<int> { 1,0,0 } } },{ 1, new List<List<int>>{ new List<int> {1,1,1,1}, new List<int> {1,1,1 }, new List<int> { 1, 1, 1,1,1 } } },{ 2, new List<List<int>>{ new List<int> {1,0, 0, 0, }, new List<int> {1,1,1,1 }, new List<int> {0,0,0,1 }, new List<int> { 0, 0, 0, 1 } } },};}
}

脚本2————AStar.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;namespace AStar
{public class GObject{public GameObject gameobject;public int objType;GameObject fValText;GameObject gValText;GameObject hValText;GameObject textPanel;public GObject(GameObject _gameobject, int _type) {gameobject = _gameobject; objType = _type;textPanel = _gameobject.transform.Find("Panel").gameObject;fValText = textPanel.transform.Find("FText").gameObject;gValText = textPanel.transform.Find("GText").gameObject;hValText = textPanel.transform.Find("HText").gameObject;textPanel.SetActive(false);}public bool IsObstacle{get { return objType == cconst.WaterTile;  }}public void UpdateDis(int g, int h){textPanel.SetActive(true);gValText.GetComponent<Text>().text = g.ToString();hValText.GetComponent<Text>().text = h.ToString();int f = g + h;fValText.GetComponent<Text>().text = f.ToString();}public bool Visible{set { gameobject.SetActive(value);if(!value){textPanel.SetActive(false);}}get { return gameobject.activeSelf; }}public void SetParent(Transform transform){gameobject.transform.SetParent(transform);}public Vector2 localPosition{set { gameobject.transform.localPosition = value; }get { return gameobject.transform.localPosition;  }}}public class Node{public Node parent;public GObject gameobject;int idx = 0;public Node(GObject obj, int _idx) { gameobject = obj; idx = _idx; }int g = 0;int h = 0;public int Idx{get { return idx; }}public int F{get { return G + H; }}public int G{get { return g; }set { g = value; }}public int H{get { return h; }set { h = value;}}public int xIdx{get { return idx / cconst.ColLineNum; }}public int yIdx{get { return idx % cconst.ColLineNum; }}public void UpdateGVal(int gVal){if(gVal < G){G = gVal;}}public void UpdateLocalPosition(){int rowIdx = xIdx;int colIdx = yIdx;float posX = cconst.startPos.x + colIdx * cconst.width;float posY = cconst.startPos.y - rowIdx * cconst.height;gameobject.localPosition = new Vector2(posX, posY);}public void UpdateDescDis(){gameobject.UpdateDis(G, H);}public void UpdateDescDis(Node node){G = node.G;H = node.H;gameobject.UpdateDis(G, H);}public void SetParrent(Node node){parent = node;}public bool Visible{set { gameobject.gameobject.SetActive(value); }get { return gameobject.gameobject.activeSelf; }}}public class AStarTest : MonoBehaviour{// Start is called before the first frame updatepublic List<int> map;public Dictionary<int, string> mapTypeDic;public Dictionary<int, GObject> mapTypeObjDic;public Dictionary<int, GObject> idxObjDic;GameObject normalTile;GameObject waterTile;GameObject destinationTile;GameObject markTile;GameObject findTile;GameObject startTile;public int startIdx;public int targetIdx;public List<int> battleList;public List<Node> openList;public Dictionary<int, Node> openListIdxDic;public List<Node> closeList;public Dictionary<int, Node> closeListIdxDic;public List<GObject> totalObjList;public Dictionary<int, Stack<GObject>> nodeDicPool;Node targetNode;Node markTargetNode;public List<Node> markNodeList;public Coroutine curCoroutine;void Start(){idxObjDic = new Dictionary<int, GObject>();nodeDicPool = new Dictionary<int, Stack<GObject>>();mapTypeDic = new Dictionary<int, string> {{0, "normalTile" },{1, "waterTile" },{2, "targetTile" },{3, "markTile" },{4, "findTile" },{5, "startTile" },};normalTile = Resources.Load<GameObject>("Prefab/" + mapTypeDic[cconst.NormalTile]);waterTile = Resources.Load<GameObject>("Prefab/" + mapTypeDic[cconst.WaterTile]);destinationTile = Resources.Load<GameObject>("Prefab/" + mapTypeDic[cconst.DestinationTile]);markTile = Resources.Load<GameObject>("Prefab/" + mapTypeDic[cconst.MarkTile]);findTile = Resources.Load<GameObject>("Prefab/" + mapTypeDic[cconst.FindTile]);startTile = Resources.Load<GameObject>("Prefab/" + mapTypeDic[cconst.StartTile]);mapTypeObjDic = new Dictionary<int, GObject>{{cconst.NormalTile,  new GObject(normalTile,cconst.NormalTile)},{cconst.WaterTile,  new GObject(waterTile,cconst.WaterTile)},{cconst.DestinationTile,  new GObject(destinationTile,cconst.DestinationTile)},{cconst.MarkTile,  new GObject(markTile,cconst.MarkTile)},{cconst.FindTile,  new GObject(findTile,cconst.FindTile)},{cconst.StartTile,  new GObject(startTile,cconst.StartTile)},};InitData();}public void InitData(){transform.DetachChildren();int _totalIdx = cconst.RowLineNum * cconst.ColLineNum;//初始化乜有障碍物的地图map = new List<int>();for (int i = 0; i < cconst.RowLineNum; ++i)for (int j = 0; j < cconst.ColLineNum; ++j){map.Add(cconst.NormalTile);}//初始化地图障碍物battleList = new List<int>();for (int i = 0; i < cconst.BattleNum; i++){int curBattleType = Random.Range(0, 100) % cconst.BattleTypeNum;int curBattleStartIdx = Random.Range(0, _totalIdx);List<List<int>> curBattleList = cconst.BattleTypeDic[curBattleType];int curBattleRow = curBattleStartIdx / cconst.ColLineNum;int curBattleCol = curBattleStartIdx % cconst.ColLineNum;curBattleRow = Mathf.Min(curBattleRow, cconst.RowLineNum - curBattleList.Count - 1);curBattleCol = Mathf.Min(curBattleCol, cconst.ColLineNum - curBattleList[0].Count - 1);for(int j = 0; j< curBattleList.Count; ++j)for(int k=0; k<curBattleList[j].Count; ++k){if(curBattleList[j][k] > 0){int curCalIdx = (curBattleRow + j) * cconst.ColLineNum + (curBattleCol + k);map[curCalIdx] = cconst.WaterTile;battleList.Add(curCalIdx);}}}//初始化起始点startIdx = Random.Range(0, _totalIdx / 2);while (battleList.Contains(startIdx)){startIdx = Random.Range(0, _totalIdx / 2);}//初始化目标点targetIdx = Random.Range(_totalIdx / 2, _totalIdx);while (battleList.Contains(targetIdx) || startIdx == targetIdx){targetIdx = Random.Range(_totalIdx / 2, _totalIdx);}totalObjList = new List<GObject>();map[startIdx] = cconst.StartTile;map[targetIdx] = cconst.DestinationTile;InitMapRes();// ---------------- A*openList = new List<Node>();openListIdxDic = new Dictionary<int, Node>();closeList = new List<Node>();closeListIdxDic = new Dictionary<int, Node>();markNodeList = new List<Node>();targetNode = new Node(idxObjDic[targetIdx], targetIdx);markTargetNode = null;InitAStarStart();curCoroutine = StartCoroutine(FindNextPoint());}public IEnumerator FindNextPoint(){RefreshFind();yield return new WaitForSeconds(0.05f);if (!openListIdxDic.ContainsKey(targetNode.Idx)){curCoroutine = StartCoroutine(FindNextPoint());  //目标点没在dic里面,继续执行}else{markTargetNode = openListIdxDic[targetNode.Idx];curCoroutine = StartCoroutine(ShowFindPath(markTargetNode));Debug.Log("find the way to the target !!!");}}void InitMapRes(){int _totalIdx = cconst.RowLineNum * cconst.ColLineNum;for (int i=0; i< _totalIdx; ++i){int rowIdx = i / cconst.ColLineNum;int colIdx = i % cconst.ColLineNum;var obj = GetTypeObject(map[i]);idxObjDic[i] = obj;float posX = cconst.startPos.x + colIdx * cconst.width;float posY = cconst.startPos.y - rowIdx * cconst.height;obj.localPosition = new Vector2(posX, posY);}}void InitAStarStart(){GObject startObj = idxObjDic[startIdx];Node startNode = new Node(startObj, startIdx);AddOpenlistNode(startNode);}void AddOpenlistNode(Node node)  //出发点是node,遍历其周围8个点,更细其F、G、H值{if(openListIdxDic.ContainsKey(targetNode.Idx)){Debug.Log("find the way to the target !!!");return;}int xIdx = node.xIdx;int yIdx = node.yIdx;List<Vector2> curDisVal;int curValDis;for (int i =0; i<2; i++){if(i == 0){curDisVal = cconst.UnSlideDisVal;curValDis = cconst.UnSlideDis;}else{curDisVal = cconst.SlideDisVal;curValDis = cconst.SlideDis;}for (int j = 0; j < cconst.slideValNum; ++j){var addVal = curDisVal[j];var curXIdx = xIdx + (int)addVal.x;var curYIdx = yIdx + (int)addVal.y;if (curXIdx < 0 || curXIdx >= cconst.RowLineNum || curYIdx < 0 || curYIdx >= cconst.ColLineNum) continue;int cur_Idx = curXIdx * cconst.ColLineNum + curYIdx;if (closeListIdxDic.ContainsKey(cur_Idx)) continue;Node curNode = null;if (openListIdxDic.ContainsKey(cur_Idx)){curNode = openListIdxDic[cur_Idx];int cur_G = node.G + curValDis;if (curNode.G > cur_G){curNode.SetParrent(node);curNode.UpdateGVal(cur_G);}}else{GObject gobject = GetTypeObject(cconst.FindTile);float posX = cconst.startPos.x + curYIdx * cconst.width;float posY = cconst.startPos.y - curXIdx * cconst.height;gobject.localPosition = new Vector2(posX, posY);curNode = new Node(gobject, cur_Idx);if (idxObjDic[curXIdx * cconst.ColLineNum + curYIdx].IsObstacle){closeListIdxDic[cur_Idx] = curNode;curNode.gameobject.gameobject.SetActive(false);return;}curNode.SetParrent(node);openList.Add(curNode);openListIdxDic[cur_Idx] = curNode;curNode.G = node.G + curValDis;SetHVal(curNode);}curNode.UpdateDescDis();}}closeListIdxDic[node.Idx] = node;closeList.Add(node);}// Update is called once per framevoid Update(){if(Input.GetKeyDown(KeyCode.A)){//dosomething}}void DoNextOperation(){StopCoroutine(curCoroutine);foreach (var obj in totalObjList){obj.Visible = false;Stack<GObject> tmpList;if (nodeDicPool.ContainsKey(obj.objType)){tmpList = nodeDicPool[obj.objType];tmpList.Push(obj);}else{tmpList = new Stack<GObject>();tmpList.Push(obj);}nodeDicPool[obj.objType] = tmpList;}InitData();}void RefreshFind(){Node findNode = null;int tmpMax = int.MaxValue;if(openList.Count == 0){Debug.Log("can not find the way to the target !!!");DoNextOperation();return;}foreach(var node in openList){if (node.F < tmpMax){tmpMax = node.F;findNode = node;}}if (findNode == null) return;GObject gobject = GetTypeObject(cconst.MarkTile);Node curNode = new Node(gobject, findNode.Idx);curNode.UpdateLocalPosition();curNode.UpdateDescDis(findNode);markNodeList.Add(curNode);openList.Remove(findNode);AddOpenlistNode(findNode);}IEnumerator ShowFindPath(Node targetNode){foreach (var _node in markNodeList){_node.Visible = false;}Node temp_node = targetNode;while(temp_node != null){int curTile = cconst.MarkTile;if(temp_node.Idx == startIdx){curTile = cconst.StartTile;}else if(temp_node.Idx == targetIdx){curTile = cconst.DestinationTile;}GObject gobject = GetTypeObject(curTile);Node curNode = new Node(gobject, temp_node.Idx);curNode.Visible = true;curNode.UpdateLocalPosition();temp_node = temp_node.parent;yield return new WaitForSeconds(0.2f);}yield return new WaitForSeconds(0.5f);DoNextOperation();}    void SetHVal(Node node){int xDelVal = Mathf.Abs(node.xIdx - targetNode.xIdx);int yDelVal = Mathf.Abs(node.yIdx - targetNode.yIdx);node.H =  Mathf.Min(xDelVal, yDelVal) * cconst.SlideDis + Mathf.Abs(xDelVal - yDelVal) * cconst.UnSlideDis;}GObject GetTypeObject(int objType){if(nodeDicPool.ContainsKey(objType)){var typeStack = nodeDicPool[objType];if(typeStack.Count > 0){var obj = typeStack.Pop();obj.SetParent(transform);nodeDicPool[objType] = typeStack;obj.Visible = true;totalObjList.Add(obj);return obj;}else{GameObject gameObject = Instantiate(mapTypeObjDic[objType].gameobject);gameObject.transform.SetParent(transform);GObject obj = new GObject(gameObject, objType);totalObjList.Add(obj);return obj;}}else{GameObject gameObject = Instantiate(mapTypeObjDic[objType].gameobject);gameObject.transform.SetParent(transform);GObject obj = new GObject(gameObject, objType);obj.Visible = true;totalObjList.Add(obj);return obj;}}}}

Unity演示

这里的演示样例,都是跑的同一份代码,每次循环都是随机出发点、目的点、障碍物。

演示样例一

在这里插入图片描述

演示样例二

在这里插入图片描述

演示样例三

在这里插入图片描述

演示样例四

在这里插入图片描述


http://chatgpt.dhexx.cn/article/1pYOeP3b.shtml

相关文章

A星寻路算法详解(C++实现 完整代码+图片演示 )

文章目录 三种寻路算法 A星寻路算法A星寻路算法思想A星寻路准备A星寻路过程&#xff08;图例&#xff09;A星寻路代码&#xff08;完整&#xff09; 三种寻路算法 深度寻路算法&#xff1a;不一定能找到最佳路径&#xff0c;但是寻路快速&#xff0c;只能走直线。广度寻路算法…

A星(A*、A Star)路径规划算法详解(附MATLAB代码)

首先看看运行效果&#xff0c;分别有三种模式&#xff0c;代码运行前需要通过鼠标点击设置起点和终点。 第一种模式直接输出最短路径 第二种模式输出最短路径的生成过程 第三种模式输出最短路径的生成过程和详细探索的过程 代码获取 gitee链接&#xff1a;https://gitee.c…

什么是脏读?不可重复读?幻读?如何解决?

什么是脏读&#xff1f;不可重复读&#xff1f;幻读&#xff1f;如何解决&#xff1f; 朋友最近面试美团&#xff0c;被面试官问到数据库的幻读问题&#xff0c;自己正好最近复习到这&#xff0c;做个笔记整理一下数据库的三大特征以及隔离级别。 一.先来回顾一下什么是事务&…

MySQL理论:脏读、不可重复读、幻读

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;MySQL理论&#xff1a;脏读、不可重复读、幻读 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xf…

数据库事务隔离级别(脏读、幻读、不可重复读)

一、脏读、幻读和不可重复读 一、脏读、不可重复读、幻读 1、脏读&#xff1a;脏读就是指当一个事务正在访问数据&#xff0c;并且对数据进行了修改&#xff0c;而这种修改还没有提交到数据库中&#xff0c;这时&#xff0c;另外一个事务也访问这个数据&#xff0c;然后使用了…

快速理解脏读,不可重复读,幻读

介绍 要聊事务&#xff0c;不可避免的要提到数据库事务的四大特性&#xff1a;ACID atomicconsistenceisolationdurability 先放一个表格&#xff0c;看看4个隔离级别会出现的各种问题&#xff0c;网上的解释一大堆。看完后还是一脸懵逼&#xff0c;感觉懂了&#xff0c;又好…

MySQL之脏写、脏读、不可重复读、幻读

脏写和脏读都是在多个事务同时修改或读取同一行数据的情况下产生的问题。比如现在有事务1和事务2同时对一行数据进行修改&#xff0c;事务1将值改成1&#xff0c;而事务2将值改成了2&#xff0c;这时的值应该是2&#xff0c;但是就在这时&#xff0c;事务1发生了回滚&#xff0…

数据库必备知识:脏读和幻读的定义及应对策略

随着数据库应用的广泛使用&#xff0c;数据库并发性和一致性的问题成为了引起重视的问题之一。其中&#xff0c;脏读&#xff08;Dirty Read&#xff09;和幻读&#xff08;Phantom Read&#xff09;是常见的并发访问问题&#xff0c;本文将对脏读、幻读进行详细介绍&#xff0…

Seata AT模式怎样防止脏写和脏读

前言 Seata AT 模式是一种非侵入式的分布式事务解决方案&#xff0c;Seata 在内部做了对数据库操作的代理层&#xff0c;我们使用 Seata AT 模式时&#xff0c;实际上用的是 Seata 自带的数据源代理 DataSourceProxy&#xff0c;Seata 在这层代理中加入了很多逻辑&#xff0c;…

mysql 脏数据是什么_什么是脏读?

什么是脏读&#xff1f; 脏读又称无效数据的读出&#xff0c;是指在数据库访问中&#xff0c;事务T1将某一值修改&#xff0c;然后事务T2读取该值&#xff0c;此后T1因为某种原因撤销对该值的修改&#xff0c;这就导致了T2所读取到的数据是无效的&#xff0c;值得注意的是&…

[Database] 脏读、幻读这些都是什么?事务隔离级别又是什么?MySQL数据库的事务隔离级别都有哪些?

文章目录 前言事务隔离级别三种数据不一致问题1. 脏读2. 不可重复读3. 幻读不可重复读 vs 幻读 四种事务隔离级别1. READ UNCOMMITTED2. READ COMMITTED3. REPEATABLE READ4. SERIALIZABLE 不同事务隔离级别会面临的问题不同隔离事务级别的使用率排名 实战查看事务隔离级别更改…

Mysql-详解脏读、不可重复读、幻读

Mysql的事务隔离级别 Mysql有四种事务隔离级别&#xff0c;这四种隔离级别代表当存在多个事务并发冲突时&#xff0c;可能出现的脏读、不可重复读、幻读的问题。 脏读 大家看一下&#xff0c;我们有两个事务&#xff0c;一个是 Transaction A&#xff0c;一个是 Transaction B…

MySQL的事务,脏读,不可重复读,幻读

一、什么是事务 在MySQL中&#xff0c;事务是一种机制、一个操作序列&#xff0c;是访问和更新数据库的程序执行单元。事务中包含一个或多个数据库操作命令&#xff0c;会把所有的命令作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么都执行&#…

脏读、幻读、不可重复读,傻傻分不清楚

脏读 &#xff08;针对未提交数据) 脏读又称无效数据读出&#xff08;读出了脏数据&#xff09;。一个事务读取另外一个事务还没有提交的数据叫脏读。 例如&#xff1a;事务T1修改了某个表中的一行数据&#xff0c;但是还没有提交&#xff0c;这时候事务T2读取了被事务T1修改…

【MySQL理论】脏读、不可重复读、幻读

文章目录 1. 脏读(dirty read)脏读是指事务读取到其他事务未提交的数据 2. 不可重复读(non-repeatable read)不可重复读是指在同一次事务中前后查询不一致的问题 3. 幻读(phantom read)幻读是一次事务中前后数据量发生变化&#xff0c;用户产生不可预料的问题 4. 不可重复读和幻…

脏读、不可重复读、幻读、丢失更新

根儿上来说&#xff0c;为什么需要事务和锁&#xff1f; 如果所有的操作都是依次进行的&#xff0c;或者说mysql的server是单线程的&#xff0c;就不会有并发问题&#xff0c;也就不需要事务和锁了。然而事实上&#xff0c;是多客户端&#xff0c;多线程的&#xff0c;所有必须…

一文搞懂MySQL脏读,幻读和不可重复读

目录 MySQL 中事务的隔离 1.READ UNCOMMITTED2.READ COMMITTED3.REPEATABLE READ4.SERIALIZABLE前置知识 1.事务相关的常用命令2.MySQL 8 之前查询事务的隔离级别3.MySQL 8 之后查询事务的隔离级别4.查看连接的客户端详情5.查询连接客户端的数量6.设置客户端的事务隔离级别7.新…

脏读、幻读和不可重复读

一、引言 脏读、不可重复读和幻读是数据库中由于并发访问导致的数据读取问题。当多个事务同时进行时可以通过修改数据库事务的隔离级别来处理这三个问题。 二、问题解释 1、脏读&#xff08;读取未提交的数据&#xff09; 脏读又称无效数据的读出&#xff0c;是指在数据库访…

一文详解脏读、不可重复读、幻读

MySQL 是支持多事务并发执行的。否则来一个事务处理一个请求&#xff0c;处理一个人请求的时候&#xff0c;其它事务都等着&#xff0c;那估计都没人敢用MySQL作为数据库,因为用户体验太差&#xff0c;估计都要砸键盘了。 既然事务可以并发操作,这里就有一些问题&#xff1a;一…

快速理解脏读、不可重复读和幻读

MySQL的InnoDB引擎是支持事务的&#xff0c;但是并发事务的处理又会带来以下问题&#xff1a; 脏读不可重复读幻读 一、脏读 脏读指事务A读取到了事务B更新了但是未提交的数据&#xff0c;然后事务B由于某种错误发生回滚&#xff0c;那么事务A读取到的就是脏数据。 具体的说…