主頁 > 後端開發 > 不要再問我 in,exists 走不走索引了

不要再問我 in,exists 走不走索引了

2020-09-14 09:51:24 後端開發

微信搜『煙雨星空』,獲取最新好文,

前言

最近,有一個業務需求,給我一份資料 A ,把它在資料庫 B 中存在,而又比 A 多出的部分算出來,由于資料比較雜亂,我這里簡化模型,

然后就會發現,我去,這不就是 not in ,not exists 嘛,

那么問題來了,in, not in , exists , not exists 它們有什么區別,效率如何?

曾經從網上聽說,in 和 exists 不會走索引,那么事實真的是這樣嗎?

帶著疑問,我們研究下去,

注意: 在說這個問題時,不說明 MySQL 版本的都是耍流氓,我這里用的是 5.7.18 ,

用法講解

為了方便,我們創建兩張表 t1 和 t2 ,并分別加入一些資料,(id為主鍵,name為普通索引)

-- t1
DROP TABLE IF EXISTS `t1`;
CREATE TABLE `t1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_t1_name` (`name`(191)) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1009 DEFAULT CHARSET=utf8mb4;

INSERT INTO `t1` VALUES ('1001', '張三', '北京'), ('1002', '李四', '天津'), ('1003', '王五', '北京'), ('1004', '趙六', '河北'), ('1005', '杰克', '河南'), ('1006', '湯姆', '河南'), ('1007', '貝爾', '上海'), ('1008', '孫琪', '北京');

-- t2
DROP TABLE IF EXISTS `t2`;
CREATE TABLE `t2`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_t2_name`(`name`(191)) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1014 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

INSERT INTO `t2` VALUES (1001, '張三', '北京');
INSERT INTO `t2` VALUES (1004, '趙六', '河北');
INSERT INTO `t2` VALUES (1005, '杰克', '河南');
INSERT INTO `t2` VALUES (1007, '貝爾', '上海');
INSERT INTO `t2` VALUES (1008, '孫琪', '北京');
INSERT INTO `t2` VALUES (1009, '曹操', '魏國');
INSERT INTO `t2` VALUES (1010, '劉備', '蜀國');
INSERT INTO `t2` VALUES (1011, '孫權', '吳國');
INSERT INTO `t2` VALUES (1012, '諸葛亮', '蜀國');
INSERT INTO `t2` VALUES (1013, '典韋', '魏國');

那么,對于當前的問題,就很簡單了,用 not in 或者 not exists 都可以把 t1 表中比 t2 表多出的那部分資料給挑出來,(當然,t2 比 t1 多出來的那部分不算)

這里假設用 name 來匹配資料,

select * from t1 where name not in (select name from t2);
或者用
select * from t1 where not exists (select name from t2 where t1.name=t2.name);

得到的結果都是一樣的,

但是,需要注意的是,not in 和 not exists 還是有不同點的,

在使用 not in 的時候,需要保證子查詢的匹配欄位是非空的,如,此表 t2 中的 name 需要有非空限制,如若不然,就會導致 not in 回傳的整個結果集為空,

例如,我在 t2 表中加入一條 name 為空的資料,

INSERT INTO `t2` VALUES (1014, NULL, '魏國');

則此時,not in 結果就會回傳空,

另外需要明白的是, exists 回傳的結果是一個 boolean 值 true 或者 false ,而不是某個結果集,因為它不關心回傳的具體資料是什么,只是外層查詢需要拿這個布林值做判斷,

區別是,用 exists 時,若子查詢查到了資料,則回傳真, 用 not exists 時,若子查詢沒有查到資料,則回傳真,

由于 exists 子查詢不關心具體回傳的資料是什么,因此,以上的陳述句完全可以修改為如下,

-- 子查詢中 name 可以修改為其他任意的欄位,如此處改為 1 ,
select * from t1 where not exists (select 1 from t2 where t1.name=t2.name);

從執行效率來說,1 > column > * ,因此推薦用 select 1,(準確的說應該是常量值)

in, exists 執行流程

1、 對于 in 查詢來說,會先執行子查詢,如上邊的 t2 表,然后把查詢得到的結果和外表 t1 做笛卡爾積,再通過條件進行篩選(這里的條件就是指 name 是否相等),把每個符合條件的資料都加入到結果集中,

sql 如下,

select * from t1 where name in (select name from t2);

偽代碼如下:

for(x in A){
    for(y in B){
    	if(condition is true) {result.add();}
    }
}

這里的 condition 其實就是對比兩張表中的 name 是否相同,

2、對于 exists 來說,是先查詢遍歷外表 t1 ,然后每次遍歷時,再檢查在內表是否符合匹配條件,即檢查是否存在 name 相等的資料,

sql 如下,

select * from t1 where name exists (select 1 from t2);

偽代碼如下:

for(x in A){
	if(exists condition is true){result.add();}
}

對應于此例,就是從 id 為 1001 開始遍歷 t1 表 ,然后遍歷時檢查 t2 中是否有相等的 name ,

如 id=1001時,張三存在于 t2 表中,則回傳 true,把 t1 中張三的這條記錄加入到結果集,繼續下次回圈, id=1002 時,李四不在 t2 表中,則回傳 false,不做任何操作,繼續下次回圈,直到遍歷完整個 t1 表,

是否走索引?

針對網上說的 in 和 exists 不走索引,那么究竟是否如此呢?

我們在 MySQL 5.7.18 中驗證一下,(注意版本號哦)

單表查詢

首先,驗證單表的最簡單的情況,我們就以 t1 表為例,id為主鍵, name 為普通索引,

分別執行以下陳述句,

explain select * from t1 where id in (1001,1002,1003,1004);
explain select * from t1 where id in (1001,1002,1003,1004,1005);
explain select * from t1 where name in ('張三','李四');
explain select * from t1 where name in ('張三','李四','王五');

為什么我要分別查不同的 id 個數呢? 看截圖,

會驚奇的發現,當 id 是四個值時,還走主鍵索引,而當 id 是五個值時,就不走索引了,這就很耐人尋味了,

再看 name 的情況,

同樣的當值多了之后,就不走索引了,

所以,我猜測這個跟匹配欄位的長度有關,按照漢字是三個位元組來計算,且程式設計中喜歡用2的n次冪的尿性,這里大概就是以 16 個位元組為分界點,

然而,我又以同樣的資料,去我的服務器上查詢(版本號 5.7.22),發現四個id值時,就不走索引了,因此,估算這里的臨界值為 12 個位元組,

不管怎樣,這說明了,在 MySQL 中應該對 in 查詢的位元組長度是有限制的,(沒有官方確切說法,所以,僅供參考)

多表涉及子查詢

我們主要是去看當前的這個例子中的兩表查詢時, in 和 exists 是否走索引,

一、分別執行以下陳述句,主鍵索引(id)和普通索引(name),在 in , not in 下是否走索引,

explain select * from t1 where id in (select id from t2); --1
explain select * from t1 where name in (select name from t2); --2
explain select * from t1 where id not in (select id from t2); --3
explain select * from t1 where name not in (select name from t2); --4

結果截圖如下,

1、t1 走索引,t2 走索引,

1

2、t1 不走索引,t2不走索引,(此種情況,實測若把name改為唯一索引,則t1也會走索引)

2

3、t1 不走索引,t2走索引,

3

4、t1不走索引,t2不走索引,

4

我滴天,這結果看起來亂七八糟的,好像走不走索引,完全看心情,

但是,我們發現只有第一種情況,即用主鍵索引欄位匹配,且用 in 的情況下,兩張表才都走索引,

這個到底是不是規律呢?有待考察,且往下看,

二、接下來測驗,主鍵索引和普通索引在 exists 和 not exists 下的情況,sql如下,

explain select * from t1 where exists (select 1 from t2 where t1.id=t2.id);
explain select * from t1 where exists (select 1 from t2 where t1.name=t2.name);
explain select * from t1 where not exists (select 1 from t2 where t1.id=t2.id);
explain select * from t1 where not exists (select 1 from t2 where t1.name=t2.name);

這個結果就非常有規律了,且看,

有沒有發現, t1 表哪種情況都不會走索引,而 t2 表是有索引的情況下就會走索引,為什么會出現這種情況?

其實,上一小節說到了 exists 的執行流程,就已經說明問題了,

它是以外層表為驅動表,無論如何都會回圈遍歷的,所以會全表掃描,而內層表通過走索引,可以快速判斷當前記錄是否匹配,

效率如何?

針對網上說的 exists 一定比 in 的執行效率高,我們做一個測驗,

分別在 t1,t2 中插入 100W,200W 條資料,

我這里,用的是自定義函式來回圈插入,陳述句參考如下,(沒有把表名抽離成變數,因為我沒有找到方法,尷尬)

-- 傳入需要插入資料的id開始值和資料量大小,函式回傳結果為最終插入的條數,此值正常應該等于資料量大小,
-- id自增,回圈往 t1 表添加資料,這里為了方便,id、name取同一個變數,address就為北京,
delimiter // 
drop function if exists insert_datas1//
create function insert_datas1(in_start int(11),in_len int(11)) returns int(11)
begin  
  declare cur_len int(11) default 0;
  declare cur_id int(11);
  set cur_id = in_start;
	
  while cur_len < in_len do
     insert into t1 values(cur_id,cur_id,'北京');
  set cur_len = cur_len + 1;
  set cur_id = cur_id + 1;
  end while; 
  return cur_len;
end  
//
delimiter ;
-- 同樣的,往 t2 表插入資料
delimiter // 
drop function if exists insert_datas2//
create function insert_datas2(in_start int(11),in_len int(11)) returns int(11)
begin  
  declare cur_len int(11) default 0;
  declare cur_id int(11);
  set cur_id = in_start;
	
  while cur_len < in_len do
     insert into t2 values(cur_id,cur_id,'北京');
  set cur_len = cur_len + 1;
  set cur_id = cur_id + 1;
  end while; 
  return cur_len;
end  
//
delimiter ;

在此之前,先清空表里的資料,然后執行函式,

select insert_datas1(1,1000000);

對 t2 做同樣的處理,不過為了兩張表資料有交叉,就從 70W 開始,然后插入 200W 資料,

select insert_datas2(700000,2000000);

在家里的電腦,實際執行時間,分別為 36s 和 74s,

不知為何,家里的電腦還沒有在 Docker 虛擬機中跑的腳本快,,害,就這樣湊合著用吧,

等我有了新歡錢,就把它換掉,哼哼,

同樣的,把上邊的執行計劃都執行一遍,進行對比,我這里就不貼圖了,

in 和 exists 孰快孰慢

為了方便,主要拿以下這兩個 sql 來對比分析,

select * from t1 where id in (select id from t2);
select * from t1 where exists (select 1 from t2 where t1.id=t2.id);

執行結果顯示,兩個 sql 分別執行 1.3s 和 3.4s ,

注意此時,t1 表資料量為 100W, t2 表資料量為 200W ,

按照網上對 in 和 exists 區別的通俗說法,

如果查詢的兩個表大小相當,那么用in和exists差別不大;如果兩個表中一個較小一個較大,則子查詢表大的用exists,子查詢表小的用in;

對應于此處就是:

  • 當 t1 為小表, t2 為大表時,應該用 exists ,這樣效率高,
  • 當 t1 為大表,t2 為小表時,應該用 in,這樣效率較高,

而我用實際資料測驗,就把第一種說法給推翻了,因為很明顯,t1 是小表,但是 in 比 exists 的執行速度還快,

為了繼續測驗它這個觀點,我把兩個表的內表外表關系調換一下,讓 t2 大表作為外表,來對比查詢,

select * from t2 where id in (select id from t1);
select * from t2 where exists (select 1 from t1 where t1.id=t2.id);

執行結果顯示,兩個 sql 分別執行 1.8s 和 10.0s ,

是不是很有意思, 可以發現,

  • 對于 in 來說,大表小表調換了內外層關系,執行時間并無太大區別,一個是 1.3s,一個是 1.8s,
  • 對于 exists 來說,大小表調換了內外層關系,執行時間天壤之別,一個是 3.4s ,一個是 10.0s,足足慢了兩倍,

一、以查詢優化器維度對比,

為了探究這個結果的原因,我去查看它們分別在查詢優化器中優化后的 sql ,

select * from t1 where id in (select id from t2); 為例,順序執行以下兩個陳述句,

-- 此為 5.7 寫法,如果是 5.6版本,需要用 explain extended ...
explain select * from t1 where id in (select id from t2);
-- 本意為顯示警告資訊,但是和 explain 一塊兒使用,就會顯示出優化后的sql,需要注意使用順序,
show warnings;

在結果 Message 里邊就會顯示我們要的陳述句,

-- message 優化后的sql
select `test`.`t1`.`id` AS `id`,`test`.`t1`.`name` AS `name`,`test`.`t1`.`address` AS `address` from `test`.`t2` join `test`.`t1` where (`test`.`t2`.`id` = `test`.`t1`.`id`)

可以發現,這里它把 in 轉換為了 join 來執行,

這里沒有用 on,而用了 where,是因為當只有 join 時,后邊的 on 可以用 where 來代替,即 join on 等價于 join where ,

PS: 這里我們也可以發現,select * 最侄訓被轉化為具體的欄位,知道為什么我們不建議用 select * 了吧,

同樣的,以 t2 大表為外表的查詢情況,也查看優化后的陳述句,

explain select * from t2 where id in (select id from t1);
show warnings;

我們會發現,它也會轉化為 join 的,

select `test`.`t2`.`id` AS `id`,`test`.`t2`.`name` AS `name`,`test`.`t2`.`address` AS `address` from `test`.`t1` join `test`.`t2` where (`test`.`t2`.`id` = `test`.`t1`.`id`)

這里不再貼 exists 的轉化 sql ,其實它沒有什么大的變化,

二、以執行計劃維度對比,

我們再以執行計劃維度來對比他們的區別,

explain select * from t1 where id in (select id from t2);
explain select * from t2 where id in (select id from t1);
explain select * from t1 where exists (select 1 from t2 where t1.id=t2.id);
explain select * from t2 where exists (select 1 from t1 where t1.id=t2.id);

執行結果分別為,

1

2

3

4

可以發現,對于 in 來說,大表 t2 做外表還是內表,都會走索引的,小表 t1 做內表時也會走索引,看它們的 rows 一列也可以看出來,前兩張圖結果一樣,

對于 exists 來說,當小表 t1 做外表時,t1 全表掃描,rows 近 100W;當 大表 t2 做外表時, t2 全表掃描,rows 近 200W ,這也是為什么 t2 做外表時,執行效率非常低的原因,

因為對于 exists 來說,外表總會執行全表掃描的,當然表資料越少越好了,

最終結論: 外層大表內層小表,用in,外層小表內層大表,in和exists效率差不多(甚至 in 比 exists 還快,而并不是網上說的 exists 比 in 效率高),

not in 和 not exists 孰快孰慢

此外,實測對比 not in 和 not exists ,

explain select * from t1 where id not in (select id from t2);
explain select * from t1 where not exists (select 1 from t2 where t1.id=t2.id);
explain select * from t1 where name not in (select name from t2);
explain select * from t1 where not exists (select 1 from t2 where t1.name=t2.name);

explain select * from t2 where id not in (select id from t1);
explain select * from t2 where not exists (select 1 from t1 where t1.id=t2.id);
explain select * from t2 where name not in (select name from t1);
explain select * from t2 where not exists (select 1 from t1 where t1.name=t2.name);

小表做外表的情況下,對于主鍵來說, not exists 比 not in 快,對于普通索引來說, not in 和 not exists 差不了多少,甚至 not in 會稍快,

大表做外表的情況下,對于主鍵來說, not in 比 not exists 快,對于普通索引來說, not in 和 not exists 差不了多少,甚至 not in 會稍快,

感興趣的同學,可自行嘗試,以上邊的兩個維度(查詢優化器和執行計劃)分別來對比一下,

join 的嵌套回圈 (Nested-Loop Join)

為了理解為什么這里的 in 會轉換為 join ,我感覺有必要了解一下 join 的三種嵌套回圈連接,

1、簡單嵌套回圈連接,Simple Nested-Loop Join ,簡稱 SNLJ

join 即是 inner join ,內連接,它是一個笛卡爾積,即利用雙層回圈遍歷兩張表,

我們知道,一般在 sql 中都會以小表作為驅動表,所以,對于 A,B 兩張表,若A的結果集較少,則把它放在外層回圈,作為驅動表,自然,B 就在內層回圈,作為被驅動表,

簡單嵌套回圈,就是最簡單的一種情況,沒有做任何優化,

因此,復雜度也是最高的,O(mn),偽代碼如下,

for(id1 in A){
    for(id2 in B){
        if(id1==id2){
            result.add();
        }
    }
}

2、索引嵌套回圈連接,Index Nested-Loop Join ,簡稱 INLJ

看名字也能看出來了,這是通過索引進行匹配的,外層表直接和內層表的索引進行匹配,這樣就不需要遍歷整個內層表了,利用索引,減少了外層表和內層表的匹配次數,

所以,此種情況要求內層表的列要有索引,

偽代碼如下,

for(id1 in A){
    if(id1 matched B.id){
        result.add();
    }
}

3、塊索引嵌套連接,Block Nested-Loop Join ,簡稱 BNLJ

塊索引嵌套連接,是通過快取外層表的資料到 join buffer 中,然后 buffer 中的資料批量和內層表資料進行匹配,從而減少內層回圈的次數,

以外層回圈100次為例,正常情況下需要在內層回圈讀取外層資料100次,如果以每10條資料存入快取buffer中,并傳遞給內層回圈,則內層回圈只需要讀取10次(100/10)就可以了,這樣就降低了內層回圈的讀取次數,

MySQL 官方檔案也有相關說明,可以參考:https://dev.mysql.com/doc/refman/5.7/en/nested-loop-joins.html#block-nested-loop-join-algorithm

所以,這里轉化為 join,可以用到索引嵌套回圈連接,從而提高了執行效率,

宣告: 以上是以我的測驗資料為準,測出來的結果,實際真實資料和測驗結果很有可能會不太一樣,如果有不同意見,歡迎留言討論,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/33555.html

標籤:Java

上一篇:go:流程控制

下一篇:Redis秒殺實戰-微信搶紅包-秒殺庫存,附案例原始碼(Jmeter壓測)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more