PostgreSQL中的行锁

news2024/11/24 3:52:45

行锁在PG中比较特殊,在9.4以前,只有两种类型的行锁, FOR UPDATE 和FOR SHARE,因为只有两种锁,粒度比较大,极大的影响了并发性。所以从9.4开始引入了FOR KEY SHARE和FOR NO KEY UPDATE这两种行锁。目前这四种行锁,其中两个是排他性锁,一次只能被一个事务获取;另外两个则是可由多个事务同时持有的共享锁。

有时候很多人使用SELECT…FOR UPDATE显式地锁定某些行以防止并发更新。但大多数时候,这种行为太过了。只有在打算删除整行或修改主键列或唯一键列时,才应该使用FOR UPDATE锁。否则,最好使用FOR NO KEY UPDATE锁,这样就不会阻塞对引用正在更新的表的表的插入。

PG中行锁保存在当前堆元组的头部,而且行锁实际上是堆页面的一个属性,不是真正意义上的锁。由于不在内存中,所以对内存也不会有影响。但是每个行锁都会修改表,修改后的块必须写回持久存储。所以行锁也有额外的I/O负载。

当我们获取行锁的时候会发生以下流程步骤:
  1. 首先检查是否有别的事物在该行上持有锁。
  2. 如果没有,第一个事物则在该行加上一个元组锁,元组锁是在内存中的,该锁的目的是如果多个会话想要锁定同一行,它们必须排队。这个队列由元组锁维护。该锁保护行免受并发修改,直到我们将锁写入行本身。
  3. 第二个事物检查到其他事务持有该行上的锁,则该事物持有元组锁并进入睡眠状态,等待第一个事务结束。一旦第一个事物结束,阻塞也就结束,我们就可以继续了。但首先,我们检查在等待的过程中是否存在对该行的并发修改。如果是,则该行为取决于当前事务隔离级别,如果是READ COMMITTED隔离级别,那么拿取已提交事物的最新行版本,如果是其它更高的隔离级别,那么会抛出一个序列化错误。
  4. 如果第一个事物没有提交,第三个事务(以及随后的任何事务)也看到该行被锁定。它阻塞并等待行上的元组锁。
  5. 以上事物一旦所有阻塞的行锁都消失了,那么我们得到了该行的最新版本,就可以继续了。最后修改行上的xmax和一些提示位,并释放元组锁。
模拟一下以上的流程,看下具体示例:
session 1: 创建测试表,并模拟一个我们正常的更新,一般来说for no key update和普通执行的update命令类似
postgres=# create table test (id int, info text);
CREATE TABLE
postgres=# insert into test values (1,'a'),(2,'b'); 
INSERT 0 2
postgres=# begin ;
BEGIN
postgres=*# select txid_current(), pg_backend_pid();
 txid_current | pg_backend_pid 
--------------+----------------
      2277673 |           1767
(1 row)

postgres=*#  select * from test where id=1 for no key update;
 id | info 
----+------
  1 | a
(1 row)

session 2:
postgres=# begin ;
BEGIN
postgres=*# select txid_current(), pg_backend_pid();
 txid_current | pg_backend_pid 
--------------+----------------
      2277674 |           2447
(1 row)
postgres=*# select * from test where id=1 for no key update;
此处夯住

session 3:
postgres=# begin;
BEGIN
postgres=*# select txid_current(), pg_backend_pid();
 txid_current | pg_backend_pid 
--------------+----------------
      2277675 |           4229
(1 row)

postgres=*#  select * from test where id=1 for no key update;
#等待持有tuple锁

如下图,session4分别查一下持有的锁,注意一下tuple锁,session2的granted为t,session3为f
在这里插入图片描述
一旦session1,pid为1767的事物提交了,那么就如下图,session2,pid为2447的会释放tuple锁,然后session3,pid为4229的获取tuple锁,granted变为t。
在这里插入图片描述
当然可以通过pgrowlocks插件查看锁内容,如下:

postgres=# SELECT * FROM pgrowlocks('test');
 locked_row | locker  | multi |   xids    |         modes         |  pids  
------------+---------+-------+-----------+-----------------------+--------
 (0,1)      | 2277674 | f     | {2277674} | {"For No Key Update"} | {2447}
(1 row)

以上介绍了行锁的一些概念和原理,下面我们看一下这四类锁的冲突矩阵,以及每种锁影响的操作。

在这里插入图片描述

FOR UPDATE:该模式允许修改任何元组字段,甚至删除整个元组

FOR NO KEY UPDATE:对除主(唯一)键外的字段更新,此更改也不影响外键

FOR SHARE:当我们需要读取一行,但不允许其他事务更改它时,使用该模式

FOR KEY SHARE:读该行的键值,但只允许对除键外的其他字段更新。在检查外键约束时会自动使用该锁。

排它模式的两个锁:FOR UPDATE 和 FOR NO KEY UPDATE
#创建测试表
CREATE TABLE accounts(
  acc_no integer PRIMARY KEY,
  amount numeric
);
INSERT INTO accounts VALUES (1, 100.00), (2, 200.00), (3, 300.00);

#创建插件
CREATE EXTENSION pageinspect;
CREATE EXTENSION pgrowlocks;

#更新表,一个是key update一个是no key update
BEGIN;
UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 1;  --no key update
UPDATE accounts SET acc_no = 20 WHERE acc_no = 2; --key update = for update

#查看标志位
SELECT '(0,'||lp||')' AS ctid,
       t_xmax as xmax,
       CASE WHEN (t_infomask & 128) > 0   THEN 't' END AS lock_only,
       CASE WHEN (t_infomask & 4096) > 0  THEN 't' END AS is_multi,
       CASE WHEN (t_infomask2 & 8192) > 0 THEN 't' END AS keys_upd,
       CASE WHEN (t_infomask & 16) > 0 THEN 't' END AS keyshr_lock,
       CASE WHEN (t_infomask & 16+64) = 16+64 THEN 't' END AS shr_lock
FROM heap_page_items(get_raw_page('accounts',0))
ORDER BY lp;

 ctid  |  xmax   | lock_only | is_multi | keys_upd | keyshr_lock | shr_lock 
-------+---------+-----------+----------+----------+-------------+----------
 (0,1) | 2277687 |           |          |          |             | 
 (0,2) | 2277687 |           |          | t        |             | 
 (0,3) |       0 |           |          |          |             | 
 (0,4) |       0 |           |          |          |             | 
 (0,5) |       0 |           |          |          |             | 
(5 rows)

#查看锁模式
SELECT * FROM pgrowlocks('accounts');
 locked_row | locker  | multi |   xids    |       modes       |  pids   
------------+---------+-------+-----------+-------------------+---------
 (0,1)      | 2277687 | f     | {2277687} | {"No Key Update"} | {27249}
 (0,2)      | 2277687 | f     | {2277687} | {Update}          | {27249}
(2 rows)

#再看一下select ... for和上面的不同
postgres=# begin;
BEGIN
postgres=*# SELECT * FROM accounts WHERE acc_no = 1 FOR NO KEY UPDATE;
 acc_no | amount 
--------+--------
      1 | 100.00
(1 row)

postgres=*# SELECT * FROM accounts WHERE acc_no = 2 FOR UPDATE;
 acc_no | amount 
--------+--------
      2 | 200.00
(1 row)

#标志位lock_only为true,和前面的实验在标志位设置还是有区别的,说明行只是被锁定,并没有被删除,行还是活动的
postgres=# SELECT '(0,'||lp||')' AS ctid,
       t_xmax as xmax,
       CASE WHEN (t_infomask & 128) > 0   THEN 't' END AS lock_only,
       CASE WHEN (t_infomask & 4096) > 0  THEN 't' END AS is_multi,
       CASE WHEN (t_infomask2 & 8192) > 0 THEN 't' END AS keys_upd,
       CASE WHEN (t_infomask & 16) > 0 THEN 't' END AS keyshr_lock,
       CASE WHEN (t_infomask & 16+64) = 16+64 THEN 't' END AS shr_lock
FROM heap_page_items(get_raw_page('accounts',0))
ORDER BY lp;
 ctid  |  xmax   | lock_only | is_multi | keys_upd | keyshr_lock | shr_lock 
-------+---------+-----------+----------+----------+-------------+----------
 (0,1) | 2277689 | t         |          |          |             | 
 (0,2) | 2277689 | t         |          | t        |             | 
 (0,3) |       0 |           |          |          |             | 
 (0,4) |       0 |           |          |          |             | 
 (0,5) |       0 |           |          |          |             | 
(5 rows)

postgres=# SELECT * FROM pgrowlocks('accounts');
 locked_row | locker  | multi |   xids    |         modes         |  pids   
------------+---------+-------+-----------+-----------------------+---------
 (0,1)      | 2277689 | f     | {2277689} | {"For No Key Update"} | {27249}
 (0,2)      | 2277689 | f     | {2277689} | {"For Update"}        | {27249}
(2 rows)

共享模式的两个锁FOR SHARE 和 FOR KEY SHARE
#select ... for语句加锁
postgres=# begin;
BEGIN
postgres=*# SELECT * FROM accounts WHERE acc_no = 1 FOR KEY SHARE;
 acc_no | amount 
--------+--------
      1 | 100.00
(1 row)

postgres=*# SELECT * FROM accounts WHERE acc_no = 2 FOR SHARE;
 acc_no | amount 
--------+--------
      2 | 200.00
(1 row)

#查看标志位,在这两种情况下,都设置了keyshr_lock标志位,并且SHARE模式通过多设置一个标志位来表示。
postgres=# SELECT '(0,'||lp||')' AS ctid,
       t_xmax as xmax,
       CASE WHEN (t_infomask & 128) > 0   THEN 't' END AS lock_only,
       CASE WHEN (t_infomask & 4096) > 0  THEN 't' END AS is_multi,
       CASE WHEN (t_infomask2 & 8192) > 0 THEN 't' END AS keys_upd,
       CASE WHEN (t_infomask & 16) > 0 THEN 't' END AS keyshr_lock,
       CASE WHEN (t_infomask & 16+64) = 16+64 THEN 't' END AS shr_lock
FROM heap_page_items(get_raw_page('accounts',0))
ORDER BY lp;
 ctid  |  xmax   | lock_only | is_multi | keys_upd | keyshr_lock | shr_lock 
-------+---------+-----------+----------+----------+-------------+----------
 (0,1) | 2277690 | t         |          |          | t           | 
 (0,2) | 2277690 | t         |          |          | t           | t
 (0,3) |       0 |           |          |          |             | 
 (0,4) |       0 |           |          |          |             | 
 (0,5) |       0 |           |          |          |             | 
(5 rows)

#查看行锁
postgres=# SELECT * FROM pgrowlocks('accounts');
 locked_row | locker  | multi |   xids    |       modes       |  pids   
------------+---------+-------+-----------+-------------------+---------
 (0,1)      | 2277690 | f     | {2277690} | {"For Key Share"} | {27249}
 (0,2)      | 2277690 | f     | {2277690} | {"For Share"}     | {27249}
(2 rows)

#因为For Key Share和For no key Update是兼容不冲突的,如下:
postgres=# begin;
BEGIN
postgres=*# UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 1;
UPDATE 1

#查看标志位,is_multi设置为true
postgres=# SELECT '(0,'||lp||')' AS ctid,
       t_xmax as xmax,
       CASE WHEN (t_infomask & 128) > 0   THEN 't' END AS lock_only,
       CASE WHEN (t_infomask & 4096) > 0  THEN 't' END AS is_multi,
       CASE WHEN (t_infomask2 & 8192) > 0 THEN 't' END AS keys_upd,
       CASE WHEN (t_infomask & 16) > 0 THEN 't' END AS keyshr_lock,
       CASE WHEN (t_infomask & 16+64) = 16+64 THEN 't' END AS shr_lock
FROM heap_page_items(get_raw_page('accounts',0))
ORDER BY lp;
 ctid  |  xmax   | lock_only | is_multi | keys_upd | keyshr_lock | shr_lock 
-------+---------+-----------+----------+----------+-------------+----------
 (0,1) |      12 |           | t        |          |             | 
 (0,2) | 2277690 | t         |          |          | t           | t
 (0,3) |       0 |           |          |          |             | 
 (0,4) |       0 |           |          |          |             | 
 (0,5) |       0 |           |          |          |             | 
 (0,6) | 2277690 | t         |          |          | t           | 

#可以看到同一行,持有2个行锁"Key Share"和"No Key Update"
postgres=#  SELECT * FROM pgrowlocks('accounts');
 locked_row | locker  | multi |       xids        |             modes             |     pids      
------------+---------+-------+-------------------+-------------------------------+---------------
 (0,1)      |      12 | t     | {2277690,2277691} | {"Key Share","No Key Update"} | {27249,30530}
 (0,2)      | 2277690 | f     | {2277690}         | {"For Share"}                 | {27249}
(2 rows)

1 单事务锁行时, 各种行锁模式和infomask标志位的关系 :

1.1select … for key share

t_infomask 设置 HEAP_XMAX_LOCK_ONLY 以及 HEAP_XMAX_KEYSHR_LOCK

1.2 select … for share

t_infomask 设置 HEAP_XMAX_LOCK_ONLY 以及 HEAP_XMAX_SHR_LOCK [也就是HEAP_XMAX_KEYSHR_LOCK和HEAP_XMAX_EXCL_LOCK]

1.3 select … for no key update

t_infomask 设置 HEAP_XMAX_LOCK_ONLY 以及 HEAP_XMAX_EXCL_LOCK

1.4 select … for update [也就是key update]

t_infomask 设置 HEAP_XMAX_LOCK_ONLY 以及 HEAP_XMAX_EXCL_LOCK 同时 t_infomask2 设置 HEAP_KEYS_UPDATED

1.5 update命令不更新主键

如:[UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 1;]
t_infomask没有设置相关标志位

1.6 update命令更新主键

如:[UPDATE accounts SET acc_no = 20 WHERE acc_no = 2;]
t_infomask2设置了HEAP_KEYS_UPDATED

标志位见源码src/include/access/htup_details.h
/*
 * information stored in t_infomask:
 */
#define HEAP_HASNULL			0x0001	/* has null attribute(s) */
#define HEAP_HASVARWIDTH		0x0002	/* has variable-width attribute(s) */
#define HEAP_HASEXTERNAL		0x0004	/* has external stored attribute(s) */
#define HEAP_HASOID_OLD			0x0008	/* has an object-id field */
#define HEAP_XMAX_KEYSHR_LOCK	0x0010	/* xmax is a key-shared locker */
#define HEAP_COMBOCID			0x0020	/* t_cid is a combo CID */
#define HEAP_XMAX_EXCL_LOCK		0x0040	/* xmax is exclusive locker */
#define HEAP_XMAX_LOCK_ONLY		0x0080	/* xmax, if valid, is only a locker */

 /* xmax is a shared locker */
#define HEAP_XMAX_SHR_LOCK	(HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)

#define HEAP_LOCK_MASK	(HEAP_XMAX_SHR_LOCK | HEAP_XMAX_EXCL_LOCK | \
						 HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMIN_COMMITTED		0x0100	/* t_xmin committed */
#define HEAP_XMIN_INVALID		0x0200	/* t_xmin invalid/aborted */
#define HEAP_XMIN_FROZEN		(HEAP_XMIN_COMMITTED|HEAP_XMIN_INVALID)
#define HEAP_XMAX_COMMITTED		0x0400	/* t_xmax committed */
#define HEAP_XMAX_INVALID		0x0800	/* t_xmax invalid/aborted */
#define HEAP_XMAX_IS_MULTI		0x1000	/* t_xmax is a MultiXactId */
#define HEAP_UPDATED			0x2000	/* this is UPDATEd version of row */
#define HEAP_MOVED_OFF			0x4000	/* moved to another place by pre-9.0
										 * VACUUM FULL; kept for binary
										 * upgrade support */
#define HEAP_MOVED_IN			0x8000	/* moved from another place by pre-9.0
										 * VACUUM FULL; kept for binary
										 * upgrade support */
#define HEAP_MOVED (HEAP_MOVED_OFF | HEAP_MOVED_IN)

#define HEAP_XACT_MASK			0xFFF0	/* visibility-related bits */


*
 * information stored in t_infomask2:
 */
#define HEAP_NATTS_MASK			0x07FF	/* 11 bits for number of attributes */
/* bits 0x1800 are available */
#define HEAP_KEYS_UPDATED		0x2000	/* tuple was updated and key cols
										 * modified, or tuple deleted */
#define HEAP_HOT_UPDATED		0x4000	/* tuple was HOT-updated */
#define HEAP_ONLY_TUPLE			0x8000	/* this is heap-only tuple */

#define HEAP2_XACT_MASK			0xE000	/* visibility-related bits */
查看标志位SQL
#可以根据需求调整t_infomask或者t_infomask2值,比如(t_infomask & 128) > 0   THEN 't' END AS lock_only
#比如宏定义#define HEAP_XMAX_LOCK_ONLY		0x0080,0x0080为16进制转为10进制为128,t_infomask按位和128求与大于0,则为真
SELECT '(0,'||lp||')' AS ctid,
       t_xmax as xmax,
       CASE WHEN (t_infomask & 128) > 0   THEN 't' END AS lock_only,
       CASE WHEN (t_infomask & 4096) > 0  THEN 't' END AS is_multi,
       CASE WHEN (t_infomask2 & 8192) > 0 THEN 't' END AS keys_upd,
       CASE WHEN (t_infomask & 16) > 0 THEN 't' END AS keyshr_lock,
       CASE WHEN (t_infomask & 16+64) = 16+64 THEN 't' END AS shr_lock
FROM heap_page_items(get_raw_page('accounts',0))
ORDER BY lp;


SELECT '(0,'||lp||')' AS ctid,
       CASE lp_flags
         WHEN 0 THEN 'unused'
         WHEN 1 THEN 'normal'
         WHEN 2 THEN 'redirect to '||lp_off
         WHEN 3 THEN 'dead'
       END AS state,
       t_xmin as xmin,
       t_xmax as xmax,
       (t_infomask & 64) > 0  AS HEAP_XMAX_EXCL_LOCK,
       (t_infomask & 128) > 0  AS HEAP_XMAX_LOCK_ONLY,
       (t_infomask & 256) > 0  AS xmin_commited,
       (t_infomask & 512) > 0  AS xmin_aborted,
       (t_infomask & 1024) > 0 AS xmax_commited,
       (t_infomask & 2048) > 0 AS xmax_aborted,
       t_ctid
FROM heap_page_items(get_raw_page('accounts',0));

参考:
https://www.cybertec-postgresql.com/en/row-locks-in-postgresql/
https://www.cybertec-postgresql.com/en/whats-in-an-xmax/
https://habr.com/en/companies/postgrespro/articles/503008/

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

防火墙——SNAT和DNAT策略的原理及应用、防火墙规则的备份、还原和抓包

防火墙—SNAT和DNAT策略的原理及应用、防火墙规则的备份、还原和抓包 一、SNAT策略概述1、SNAT应用环境2、SNAT原理3、SNAT转换的前提条件 二、SNAT策略的应用1、临时打开2、永久打开3、SNAT转换1:固定的公网IP地址4、SNAT转换2:非固定的公网IP地址&…

一文搞懂,PO设计模式详解

PO模式: 全称:page objece,分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复⽤性。 PO模式的优势: 1)效率⾼ :同理,PO模式的逻辑层⽅法有具体定…

Flink第六章:多流操作

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作. Flink第三章:基本操作(二) Flink第四章:水位线和窗口 Flink第五章:处理函数 Flink第六章:多流操作 文章目录 系列文章目录前言一、分流1.侧输出流(process function) 二、合流1. 联合(Union)2…

CVE-2018-2894WebLogic未授权任意文件上传

CVE-2018-2894WebLogic未授权任意文件上传 这个洞的限制就比较多了 限制版本 Oracle WebLogic Server版本 10.3.6.0 12.1.3.0 12.2.1.2 12.2.1.3 限制配置 该漏洞的影响模块为web服务测试页,在默认情况下不启用。 /ws_utc/config.do /ws_utc/begin.do 默认情况下不…

在职字节6年,一个29岁女软件测试工程师的心声

简单的先说一下,坐标杭州,14届本科毕业,算上年前在字节跳动的面试,一共有面试了有6家公司(因为不想请假,因此只是每个晚上去其他公司面试,所以面试的公司比较少) 其中成功的有4家&a…

Linux防火墙----firewalld

文章目录 一、firewalld概述二、firewalld 与 iptables 的区别三、firewalld 区域的概念四、firewalld数据处理流程五、firewalld防火墙的配置方法5.1 使用firewall-config 图形工具5.2 编写/etc/firewalld/中的配置文件5.3使用firewall-cmd 命令行工具 一、firewalld概述 fir…

AI:帮助你更好地发声!

正文共 978 字,阅读大约需要 3 分钟 公务员必备技巧,您将在3分钟后获得以下超能力: 快速生成倡议书 Beezy评级 :B级 *经过简单的寻找, 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图片由Lex…

当你知道前后端分离与不分离的6个特点,你就不该再当点工了

Web 应用的开发主要有两种模式: 前后端不分离 前后端分离 理解它们的区别有助于我们进行对应产品的测试工作。 前后端不分离 在早期,Web 应用开发主要采用前后端不分离的方式,它是以后端直接渲染模板完成响应为主的一种开发模式。以前后端不…

linux存储技术学习资料

参考 https://www.cnblogs.com/pengdonglin137/p/16525428.html Linux I/O栈 Linux内核的I/O栈大图知乎Linux I/O专栏1Linux 块设备之Block Layer层架构演变Linux VFS机制简析(一)Linux VFS机制简析(二)Linux Kernel文件系统写I…

keycloak入门

realm:领域,指的是在某一个软件业务领域中所涉及的用户认证授权管理相关的对象,在这个realm中有用户、角色、会话session等等用于认证授权管理的对象。 假设一个公司A使用一个erp系统,那么就可以给这个公司A设置一个realm&#xf…

微信小程序nodejs+vue高校食堂餐厅点餐订餐系统ja221

本文以实际运用为开发背景,运用软件工程原理和开发方法,它主要是采用 语言 node.js 框架:Express 前端:Vue.js 数据库:mysql 数据库工具:Navicat 开发软件:VScode 前端vueelementui, (1) vue引入elementu…

NFC入门介绍

缩写词 NFCNear Field Communication近场通信OEMOriginal Equipment Manufacturer原始设备制造商HWHardware硬件OMAPIOpen Mobile Application Programming Interface开发移动应用程序编程接口eSEEmbedded Secure Element嵌入式安全元件SEMSSecure Element Management Service…

5月22日比特币披萨日,今天你吃披萨了吗?

比特币披萨日 1. Laszlo Hanyecz2. 最贵披萨诞生记3. 梭哈买披萨4. 未完待续 2010年5月22日,美国佛罗里达州的程序员Laszlo Hanyecz(拉兹洛哈涅克斯)用10000个比特币购买了棒约翰(Papa Johns)比萨店一个价值25美元的奶…

Three.js--》实现3d水晶小熊模型搭建

目录 项目搭建 初始化three.js基础代码 加载背景纹理 加载小熊模型 今天简单实现一个three.js的小Demo,加强自己对three知识的掌握与学习,只有在项目中才能灵活将所学知识运用起来,话不多说直接开始。 项目搭建 本案例还是借助框架书写…

vTESTstudio概述

vTESTstudio支持的测试用例编写方式 项目层级结构 从用例编写到测试执行及生成报告的整个流程 vTESTsutido 开发,CANoe执行测试 界面简介 CANoe 创建的测试用例用Test Modules执行,vTESTstudio 创建的测试用例用Test Units执行 先在vTESTstudio里创建pr…

Quard Bayer(COMS SENSOR)

手机越做越紧凑需要模组和芯片尺寸越做越小,在尺寸一定的基础上,高像素和大像素,对于手机摄像头来说,一直是一对矛盾的存在。然而,高像素所带来的高分辨率画质,和大像素带给暗态高感度低噪声的画质&#xf…

Idea使用详解

01.idea简介 (1)idea介绍 IDEA 全称IntelliJ IDEA,是用于java语言开发的集成环境(也可用于其他语言),IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、…

鸿蒙Hi3861学习十七-Huawei LiteOS-M(MQTT)

一、简介 有关MQTT的相关概念介绍,请看之前的文章,这里不做过多的介绍:MQTT学习总结_t_guest的博客-CSDN博客 本章节需要使用如下软件: Mosquitto(MQTT消息代理工具) Eclipsse paho MQTT工具 二、操作说明…

十款优质企业级Java微服务开源项目(开源框架,用于学习、毕设、公司项目、私活等,减少开发工作,让您只关注业务!)

Java微服务开源项目 前言一、pig二、zheng三、SpringBlade四、SOP五、matecloud六、mall七、jeecg-boot八、Cloud-Platform九、microservices-platform十、RuoYi-Cloud 前言 这篇文章为大家推荐几款优质的 Java 开源项目框架,可以用于学习,毕业设计&…

【腾讯云FinOps Crane 集训营】 Crane入门

前言 随着云计算的快速发展和云原生应用的兴起,容器技术成为了现代化应用部署和管理的重要工具。 越来越多的公司正在选择将应用运行在云上或者自建的 Kubernetes 集群上,但是许多机构的调研 发现,绝大多数的用户集群资源利用率并不高&…