2022年8月11日 星期四

lambda 的 this (java8 八)

/**
 * 印 this 的時候,會調用 Object.toString()
 * 匿名類別的 this:會先調用自己匿名類別的 toString,沒有才去 Object.toString
 * Lambda 的 this:會先調用「當下」類別的 toString,沒有才去 Object.toString
 */
public class ThisTest {
    Animal a1 = new Animal() {
        @Override
        public void m(String str) {
            System.out.println(this);
        }

//        @Override
//        public String toString() {
//            return "anonymous toString";
//        }
    };

    Animal a2 = s -> System.out.println(this);

//    @Override
//    public String toString() {
//        return "this toString";
//    }

    public static void main(String[] args) {
        ThisTest thisTest = new ThisTest();
        thisTest.a1.m("xxx");
        thisTest.a1.m("aaa");
        thisTest.a2.m("ooo");
        thisTest.a2.m("bbb");
    }

    @FunctionalInterface
    public interface Animal {
        void m(String str);
    }
}


2022年7月14日 星期四

Java 的 floorMod 和 %

// System.out.println(Math.floorMod(113, 30)); // 23
System.out.println(Math.floorMod(113, -30)); // -7
System.out.println(Math.floorMod(-113, 30)); // 7
System.out.println(Math.floorMod(-113, -30)); // -23
System.out.println("-----------------------");
// System.out.println(113 % 30); // 23
System.out.println(113 % -30); // 23
System.out.println(-113 % 30); // -23
System.out.println(-113 % -30); // -23
System.out.println("=======================");
// System.out.println(Math.floorDiv(113, 30)); // 3
System.out.println(Math.floorDiv(113, -30)); // -4
System.out.println(Math.floorDiv(-113, 30)); // -4
System.out.println(Math.floorDiv(-113, -30)); // 3
System.out.println("-----------------------");
// System.out.println(113 / 30); // 3
System.out.println(113 / -30); // -3
System.out.println(-113 / 30); // -3
System.out.println(-113 / -30); // 3

除數和被除數全正數和全負數時,% 和 mod 一模一樣

※全負數時,除法是負負得正;

※有負數時, % 的正負號看左邊,mod 的正負號看右邊,如 -113 % -30,-113 是左邊;-30 是右邊,一正一負時也是一樣

※113 / 30 是 3...23

floorDiv 時,一正一負為負,(3 + 1) * -1 = -4,固定加1乘-1

floorMod 時,30 - 23 = 7,然後看 30 是正或負,7 的正負號就和它一樣

目前不知道 mod 這樣有什麼用




 

2022年2月17日 星期四

檢查服務是否啟動、grep、sed、awk

檢查服務是否啟動

lsof -i:8080

netstat -lntup |grep 8080

ss -lntup |grep 8080

ps -ef |grep 8080



grep 

過濾文字

-A 之前x行(不包括搜尋的行)

-B 之後x行(不包括搜尋的行)

-C 前後x行(不包括搜尋的行)

-c 有幾行,類似 wc -l 的功能

-i 忽略大小寫

-w word,單字匹配

-v 回傳相反的匹配


grep -A3 5 fileName:找到 5 後,將前3行顯示出來,所以總共會顯示 4 行

seq 10 |grep -A3 5   ==> 5~8

seq 10 |grep -B3 5   ==> 2~5

seq 10 |grep -C3 5   ==> 2~8


seq 20 |grep 5 |wc -l

seq 20 |grep -c 5


grep -i a test.txt


seq 50 |grep 5

seq 50 |grep -w 5


seq 20 |grep -v 5




sed

針對行的增刪改查,stream edit 的縮寫


-n:sed 會一行一行執行,預設每行都會輸出,所以匹配到的會出現兩次,而加這個只會將匹配成功的輸出

-i:顯示的結果,預設不會覆蓋原檔案,如果要覆蓋要加這個參數

-r -E:加上正則表達式支援


增刪改查

c a i:replace想成 ce的c/append/insert

d:delete

s:substitute

p:print


p:

'3p' 印出第3行

'1,5p' 印出1~5行

'/xxx/p' 找 xxx

'/10:00/,/11:00/p' 找 10:00~11:00 之間的資料


增刪改查範例:

sed -n '/7/,/10/p' test.txt

sed '/7/,/10/d' test.txt // 加 -n 會看不到結果,每次的7~10都會刪除

sed 's/a/aaa/' test.txt // s寫在前面,s/a/aaa/、s#a#aaa#,分隔符不要太奇怪都可以

sed 's#[0-9]##g' test.txt // 將數字取代成空,但仍然有換行,最後不加 g,只會取代每行的第一個數字

sed '6a xxxxxx' test.txt // 在第6行之後加一行 xxxxxx 

sed '6i xxxxxx' test.txt // 在第6行之前加一行 xxxxxx 

sed '6c xxxxxx' test.txt // 取代第6行,變成 xxxxxx 


awk

發明人的名字第一個字母組成的


record:行(左右),預設 \n,-v RS 可改

field:列(上下),預設空隔,-F 或 -v FS 可改


$數字:第x列,$0是整行

NF:最後一列的數字,可以加減,但要用圓括號包起來

NR:行號

-F分隔符

FS:-v FS=分隔符,給 awk 的分隔符,相當於 -F,預設是空格

OFS:-v OFS=分隔符,awk 返回結果的分隔符,預設是空格

RS:-v RS=換行符,預設 \n

ORS:-v ORS=換行符,預設 \n

print 會換行

printf 不會換行


NR

Number of Record

NR==1 取出第一行

NR >=1 && NR <= 5 取出 1~5 行,還有 == != 可用

awk 'NR >= 12 && NR <= 15' test.txt


假設有一行 1,2,3,4,5,6,7,8,9,10

awk -F, 'NR==1{print $NF}' test.txt // 取第一行,然後以,分隔,再取最後一列,所以是10

awk -F, 'NR==1{print $(NF-1)}' test.txt // 取到數第二列

ls -l |awk '{print $5, $NF}' |column -t // 取第五列和最後一列,並排版

ls -l |awk '{for(i=5;i<=NF;i++){printf $i" "};printf "\n"}' |column -t // 取第五列到最後一列


正則

awk '$10~/^1/{print $10}' fileName    // 第 10 個欄位是 1 開頭就印出來

~ 包含

!~ 不包含

^$ 空

//,//

//,//{print $2}


BEGIN、END

BEGIN{} 內容在 awk 讀檔案之前執行

END{} 內容在 awk 讀檔案之後執行


seq 100 |awk 'BEGIN {pring 0} {sum=sum+$1; print sum} END{print "finish"}'

印出 0~100 相加的過程,BEGIN 和 END 可以只用一個



陣列

非數字要用字串包起來

awk 'BEGIN{x[0]=123; x[1]="xxx"; print x[0], x[1]}'

awk 'BEGIN{x[0]=123; x[1]="xxx"; for(i in x) print x[i]}'

2021年12月12日 星期日

rocketMQ 4.x

下載點

文件

控制台:就是有畫面可以操作


※啟動注意事項

此文章使用的是 Windows 的 rocketMQ,使用 java8 可以跑得起來,但不能安裝在有空格的路徑,如 Program Files,網上有說用字串包起來可以解決,但我試不成功,但可以用 PROGRA~1 解決

9 10 不是 LTS 版,我沒試過,而 11 會出現 JVM 參數已經沒有的問題,會跑不起來,雖然網路上也有將沒有的 JVM 參數註解掉,但我試還是不成功

windows 要執行副檔名是 cmd 的;linux 要執行 sh 的

mqnamesrv.cmd 啟動 name server,內部會執行 runserver.cmd

mqbroker.cmd 啟動 broker,內部會執行 runbroker.cmd

以上這兩個用文件編輯器可打開查看,其中需要 ROCKETMQ_HOME 變數,所以要設環境變數

-Xms2g -Xmx2g 可以設小一點,設 256m 就夠測試了

我設的是 ROCKETMQ_HOME=C:\Users\bruce\Downloads\rocketmq-4.9.2,要在 bin 的上一層,最後在放到 path 裡,然後後面加上 bin

然後先啟動 name server,後啟動 broker,啟動成功畫面如下:


以上的命令可在官網的文件複製


※測試生產消費

啟好了之後,試試能不能生產和消費,官網說要設定 NAMESRV_ADDR="localhost:9876"

所以再開啟命令提示字元,set NAMESRV_ADDR=localhost:9876,官網前後有字串,但我試的結果不行,注意 set 設定的變數只有在這個視窗有效,要永久就要在環境變數裡設定

設定好之後,tools.cmd  org.apache.rocketmq.example.quickstart.Producer 就會看到畫面一直跑,

接著是消費者,再開一個命令提示字元,如果變數是暫時的,還要在設定 NAMESRV_ADDR,打上 tools.cmd  org.apache.rocketmq.example.quickstart.Consumer,就會看到開始消費了



※啟動主控台

就是這文章最上面的控制台,使用 ide 打開後,可改兩個地方,如下:


rocketmq.config.namesrvAddr 要設定 name server 的地址; server.port 可以改成這個主控台想要的 port

server.port 預設是 8080;rocketmq.config.namesrvAddr 預設是 localhost:9876

然後下 mvn clean package -Dmaven.test.skip=true,成功後會在 target 裡生成一個 jar

執行這個 jar,java -jar xxx.jar 即可啟動,在瀏覽器打上 localhost:8080


看到這畫面表示成功了啟動了,再生產一次資料後,可在 Message 活頁標籤裡搜尋 Topic 是   TopicTest 的,可以看到生產的資料


※關閉 name server 和 broker

和啟動相反,先關閉 broker,再關閉 name server

mqshutdown.cmd broker

mqshutdown.cmd namesrv 



※broker 的雙主雙從等

在 rocketMQ 目錄有個 conf,裡面有三個資料夾

2m-2s-async:雙主雙從加異步

2m-2s-sync:雙主雙從加同步

2m-noslave:雙主

在啟動 broker 時可以加上 -c 的參數,後面加檔案路徑,就可以抓到裡面的設定,但裡面的設定還不是很多,可到官網查看


※Message

這個是傳訊息要用到的 class

tag 可以再 topic 裡在過濾一層,如生產時 topic 是 animal,tag 有 cat、dog…等,消費者可以針對某一個 tag 進行過濾,如 tag 給 dog,就只抓 dog,給 * 會全抓

key 是用戶端自己生成的,可以把它寫成唯一 id



※持久化資料

windows 在 %userProfile%\store

commitlog:真正存資料的地方,預設一個檔案 1G

consumequeue:用 topic 區分,連到 commitlog 的索引,消費者群組來這裡找資料的

index:如果在生產時有給 key 就可以利用這個 key 和時間區間來找資料



※Queue 數量

預設的數量是 4,可以使用 producer.setDefaultTopicQueueNums 修改,但如果已經有 topic 改不了,所以是 setDefault,因為資料在讀或寫的時候,更改這個值會有影響,要用下面要說的 writeQueueNums 和 readQueueNums 

已經有 topic,可以刪除或修改,但要在控制台操作,如下:

每個橫條都是一個 topic,最右的按鈕可以刪除 topic

status 按鈕可以看有幾個 queue

topic config 按鈕可以修改幾個 queue,如下:


perm 有三種值 2 4 6,分別是可寫可讀可讀寫

writeQueueNums 是 Queue 的數量,改好可到 status 查看

通常 writeQueueNums 和 readQueueNums 都是設成一樣的,只有在擴容或縮容時才會不一樣

假設 writeQueueNums 4; readQueueNums 8 會有 4 個消費者消費不到,想擴容時可暫時這樣設定

假設 writeQueueNums 8; readQueueNums 4 會有 4 個 queue 沒有消費,想縮容時可暫時這樣設定


※消費者組、主題、tag

當設定完消費者組、主題、tag 後,消費者組就會開始監聽,但只要同一個消費者組,不管主題、tag 是什麼都會收到資料,所以有可能收錯資料,而另一個資料丟失

正確的做法是消費者組、主題、tag 都要一樣,要訂閱不同主題時,也要用新的消費者組



※刷盤和複製策略

在下載好的 rocketmq\conf\broker.conf 有如下的設定

brokerRole = ASYNC_MASTER,表示異步複製,改同步只要去掉 A 即可,而 SLAVE 是接受方

flushDiskType = ASYNC_FLUSH,表示異步刷盤,改同步只要去掉 A 即可

這兩個預設都是異步的


複製策略

brokerRole 表示 MASTER 複製給 SLAVE 用什麼方式

1. ASYNC_MASTER 消息在記憶體並寫到 master 的硬碟後就回應,然後再寫到 slave

所以在還沒寫到硬碟時,忽然網路延遲或斷電,但生產者會以為已經送到 broker 了,這時會造成 slave 資料丟失

2. SYNC_MASTER 收到消息並寫到 master 和 slave 的硬碟後才回應已完成

3. SLAVE 接收 MASTER 的資料


刷盤策略

flushDiskType 表示消息在記憶體,寫入硬碟的方式

1. ASYNC_FLUSH 消息在記憶體就回應,然後再寫到碟碟

所以忽然網路延遲或斷電,就會認為沒有刷盤成功,然後再送出資料給消費者,造成消息重覆

2. SYNC_FLUSH 記憶體寫到硬碟後再回應



※Queue 和消費者

1.同個消費者組下的消費者可以消費同個主題的不同 queue;但同個主題的同個 queue 不能被多個消費者消費


如果想一個 queue 對應一個消費者,在 spring boot 裡,可以用

@RocketMQMessageListener(

    consumerGroup = "myConsumerGroup",

    topic = "myTopic",

    selectorExpression = "myTag",

    consumeMode = ConsumeMode.ORDERLY,

    consumeThreadMax = 1)

ConsumeMode.ORDERLY 表示一個 queue 對應一個消費者,注意原碼的註解 one queue, one thread,並不是指一個 thread,要指定一個 thread 要用下面的 consumeThreadMax = 1

consumeThreadMax 的數量是指藍色的線



2.不同消費者組下的消費者可以同時消費同個主題的同個 queue




3.同個消費者組下的消費者不可以同時消費不同 topic 的 queue






2021年12月10日 星期五

Mongodb

 官網連結


enterprise 需要錢,community 不用錢

下載解壓安裝,安裝過程會問要不要再安裝 compass,這個是 gui 工具,看要不要安裝或者安裝自己熟悉的 gui 工具,mongodb://127.0.0.1:27017 可以連自己的,但下面的 mongod 要執行成功

完成後,看要不要設定環境變數

啟動用 mongod,但會出現錯誤,說路徑要有 c:\data\db,自行創建後,啟動成功

或者用參數  mongod --dbpath 資料夾路徑名,這樣就可以使用自己喜歡的資料夾

port 預設是 27017,想改可用 mongod --port 2222,這要就可以改 port

然後再開一個視窗,打上 mongo 或 mongo --port 27017 即可連線,出現「>」表示連現成功


database 資料庫

collection 集合

document 文件

資料庫和集合不用手動創建,直接 use 名稱即可,在新增文件時,會自動新增資料庫和文件

show dbs 或 shw databases 看有什麼資料庫

db 看目前在哪個資料庫

show collections 或 show tables看當前資料庫有什麼集合


官方開發文件

新增文件 db.<collection>.insertOne   insertMany

 db.zoo.insertOne(name:"monkey");  zoo 是這行才開始新增的 collection,如果資料庫沒新增也會一併新增,insertOne 裡的 bson 就是 document

其他 deleteOne、deleteMany、updateOne、updateMany、find 都看官方文件即可


※NoSQLBooster for MongoDB

這也是個 IDE


projection 是投影的意思,在這裡表示哪些欄位要顯示,1顯示,0不顯示

另一種方法是將 document 寫在 find 的第二個參數裡,這樣就不用寫 projection 了



※索引

預設 _id 是 PK,就已經是索引了

db.<collection>.createIndex(

  { field1: 1 } ,

  { field2: -1 }

);

對 field1 做正序,對 field2 做倒序

查看 index: db.<collection>.getIndexes();


如圖,表示有兩個索引,key 為索引的欄位名稱,第二個是我建的複合索引


查看執行計畫,官方文檔

db.<collection>.explain("queryPlanner");

裡面可以寫三種值 queryPlanner、executionStats、allPlansExecution

看結果的 winningPlan,有個 stage,如下


stage 包括五種

COLLSCAN:掃描 collection,全表掃描的意思

IXSCAN:掃描 index,表示吃到索引了

FETCH:用於檢索文件

SHARD_MERGE:用於合併分片的結果

SHARDING_FILTER:用於從分片中過濾掉孤立文件


2021年10月2日 星期六

LBCC 和 MVCC

 LBCC (Lock Base Concurrency Control) 基於鎖的併發控制

英文文件中文文件

※行鎖鎖的是索引,沒索引會變成表鎖,有 PK 會自動有主鍵索引

SELECT * FROM bruce_test.bruce_tbl where id = 1 for update; 假設沒主鍵,第一個 session 鎖住後,不管資料庫有沒有資料都會鎖表,所以 SELECT * FROM bruce_test.bruce_tbl for update; 會阻塞

※自動提交

SET AUTOCOMMIT=0; # 關閉自動提交

show variables like 'autocommit';

※關閉不可寫的指令 

SET SQL_SAFE_UPDATES=0; # 這樣才可以新增、修改、刪除

show variables like 'sql_safe_updates';

※開啟事務 

START TRANSACTION 或 BEGIN

※回滾和提交

ROLBACK、COMMIT

※隔離等級

# READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE

mysql 預設是 REPEATABLE-READ

set session transaction_isolation='REPEATABLE-READ'; # 不寫 session 也行,預設就是 session

set global transaction_isolation='REPEATABLE-READ';

SELECT @@global.transaction_isolation;

SELECT @@session.transaction_isolation;

兩個 session 隔離等級不同,如 A 是 READ-UNCOMMITTED,B 是 READ-COMMITTED,以自己設定的為準,A 讀 B 的會有髒資料;相反則不會


紅框為同一個 session,如果改了活頁籤其中一個,同一個 session 都會改到;綠框為不同 session,mysql workbench 要在圖的下方再按一次就會出現


※準備測試資料

CREATE TABLE `bruce_tbl` (

  `id` int DEFAULT NULL,

  `name` varchar(10) DEFAULT NULL,

  KEY `id_index` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;


INSERT INTO bruce_test.bruce_tbl (`id`,`name`) VALUES (1,'abc');

INSERT INTO bruce_test.bruce_tbl (`id`,`name`) VALUES (5,'bbb');

INSERT INTO bruce_test.bruce_tbl (`id`,`name`) VALUES (9,'ccc');

INSERT INTO bruce_test.bruce_tbl (`id`,`name`) VALUES (11,'ddd');


以下的測試是用 8.0.23 版

以下的測試都是用鎖的,如 selec * from table 是沒有鎖的,不管怎樣都可以執行的,不在這裡說明的範圍

以下的測試第一次都要先 SET AUTOCOMMIT=0; 而且還要執行 start transaction 或 begin 再執行以下要測試的語句,然後都測試完後,再 rollback 或 commit


※exclusive locks 排他鎖、shared locks 共享鎖

排他鎖:在 select 最後加 for update、增刪改

共享鎖:在 select 最後加 for share 或 lock in share mode,但後者還可以有更多選項,如會阻塞就直接返回

排他鎖只能一個,一個 sql 取得了排他鎖,其他的鎖都會阻塞要查的出資料才有鎖

共享鎖可以多個,但不能有排他鎖,要查的出資料才有鎖

測試排他鎖:

A session:

SELECT * FROM bruce_test.bruce_tbl where id = 1 for update; # 執行到這裡先停,然後執行 B session


B session:

SELECT * FROM bruce_test.bruce_tbl where id = 1 for update; # 會阻塞

SELECT * FROM bruce_test.bruce_tbl where id = 5 for update; # 不會阻塞

# 資料庫的 id 有四筆,1 5 9 11,如果不是這四個值也不會阻塞


測試共享鎖:

A session:

SELECT * FROM bruce_test.bruce_tbl where id = 1 for share;


B session:

SELECT * FROM bruce_test.bruce_tbl where id = 1 for share; # 不會阻塞

SELECT * FROM bruce_test.bruce_tbl where id = 1 for update; # 會阻塞


※Intentioin locks 意向鎖

分成意向共享鎖和意向排他鎖,都是表鎖,無法人工增加,是 mysql 自動幫我們加上的

事務要取得排他鎖先取得意向鎖排他鎖;事務要取得排他鎖要先取得意向共享鎖

意向鎖目的是為了加快效能,如某一行被 for update,就表示有意向排他鎖和排他鎖,另一個 session 想鎖這張表 (lock tables talble_name read) 會發現有意向排他鎖,就知道有其中一行被鎖了,這樣就不用一行一行去找哪一行被鎖了 

這兩個鎖都可以多個而且互相都是相容的

以下是官網截出來的表,IX 表示意向排他鎖;IS 表示意向共享鎖,指的都是表鎖,共享和排他的例子鎖的是行,mysql 是可以同時有表鎖和行鎖的


首先看直的,排他鎖只能有一個,所以全部都是 confilict

再來是共享鎖,可以多個共享鎖和意向共享鎖,其他有排他的都不行

意向共享除了排他外都是可以的

意向排他只能和意向排他和意向共享,其他都不行,連共享鎖也不行



※record locks 記錄鎖

測試方法同測試排他鎖


※gap locks 間隙鎖

隔離等級為 RR 才有用,範圍條件裡沒有命中記錄時為間隙鎖,不能新增記錄,但可以用排他鎖查詢,因為都是沒有記錄的範圍

  ● A 是 RR,B 是 RC,A 的範圍沒有記錄,會有間隙鎖,這時 B 想新增範圍裡的資料會等待

  ● 資料庫的 id 是 1 5 9 11

測試一 A session:

SELECT * FROM bruce_test.bruce_tbl where id > 5 and id < 9 for update; # 這個範圍裡資料庫沒有資料才能叫間隙鎖


測試一 B session:

SELECT * FROM bruce_test.bruce_tbl where id = 8 for update;

主鍵索引:都沒鎖

唯一索引:5不會鎖,鎖的是 9

普通索引:5不會鎖,鎖的是 9


insert into bruce_test.bruce_tbl (id, name) values (5, 'xxx');

主鍵索引:鎖的是 6~8,也就是 where 的範圍

唯一索引:鎖的是 6~9

普通索引:鎖的是 5~8


測試二 A session:

SELECT * FROM bruce_test.bruce_tbl where id > 15 for update;


測試二 B session:

insert into bruce_test.bruce_tbl (id, name) values (11, 'xxx'); 

# 主鍵索引:鎖 12(含)之後

# 唯一索引:鎖 12(含)之後

# 普通索引:鎖 11(含)之後


寫的很好的文章


※next-key locks 臨鍵鎖

為記錄鎖 + 間隙鎖,隔離等級為 RR 才有用

  ●  假設資料庫的 id(index) 是 1 5 9 11,這時 id > 5 and id < 10,除了不能新增外也不可用排他鎖

A session:

SELECT * FROM bruce_test.bruce_tbl where id > 5 and id < 10 for update;


B session:

SELECT * FROM bruce_test.bruce_tbl where id = 11 for update;

# 主鍵、唯一、普通索引都是鎖 9 和 11,不鎖 5


insert into bruce_test.bruce_tbl (id, name) values (8, 'xxx');

# 主鍵索引:鎖 6~10

# 唯一索引:鎖 6~11

# 普通索引:鎖 5~10


測試二 A session:

SELECT * FROM bruce_test.bruce_tbl where id > 5 for update;


測試二 B session:

SELECT * FROM bruce_test.bruce_tbl where id = 9 for update;

# 主鍵、唯一、普通索引都是鎖 9 和 11,不鎖 5


insert into bruce_test.bruce_tbl (id, name) values (9, 'xxx');

# 主鍵索引:鎖 6(含) 以上

# 唯一索引:鎖 6(含) 以上

# 普通索引:鎖 5(含) 以上



MVCC (Multi-Version Concurrency Control) 多版本併發控制

snapshot read 快照讀:像照相機一樣照起來,以後資料都從這邊拿

current read 當前讀:直接去資料庫拿,所以拿的都是最新的資料

在兩個讀之間有增刪改其中之一,會觸發當前讀

目的是想解決幻讀,但必竟隔離等級是 RR,不是 SERIALIZABLE,所以只解決了一部分,在第一次讀的時候用的是快照讀,之後其他 session 新增了記錄,此時在查詢一次並不會產生新的記錄,會有問題的如下:


測試:

A session:# 執行第一行後時先停一下,然後執行 B session,然後再繼續

SELECT * FROM bruce_test.bruce_tbl where id = 1; # 假設查出來的 name 是 aaa

update bruce_test.bruce_tbl set name = CONCAT(name, '123') where id = 1; # 這行產生了讀已提交的資料

SELECT * FROM bruce_test.bruce_tbl where id = 1; # sesion 還沒結束,name 應該要是 aaa123,但確變成了 abc123,因為觸發了當前讀


B session:

update bruce_test.bruce_tbl set name = 'abc' where id = 1; # 直接提交

2021年8月15日 星期日

浮點數

 整數的 1/0 會出 by zero 的例外,但浮點數不會,有三種型式,「/」左右邊其中一個是 .0 就不會出錯了

1.0/0.0 = Infinity (正無限)

-1.0/0.0 = -Infinity (負無限)

0.0/0.0 = NaN (非數字)

Math.sqrt(-2) = NaN (只要是負數就會 NaN,但 0 沒關係)

其中只有 NaN 不等於自己,所以 Float 和 Double 的 isNaN 原碼如下:

public static boolean isNaN(float v) {return (v != v);}

public static boolean isNaN(double v) {return (v != v);}


isInfinite 是否是正無限或負無限 (不包括 NaN)

isFinite 只要不是正無限、負無限、NaN 其中之一就會回傳 true



IEEE 754

程式用的是 IEEE 754 的規則,維基連結

會將浮點數分成三部分

sign + exponent + fraction => 正負數+指數+小數,sign 佔一位,0是正數;1是負數

Float 32 位元為 1 + 8 + 23,指數(10進位)要加 127

Double 64位元為 1 + 11 + 52,指數(10進位)要加 1023

指數的正規化:整數只留一位,而且一定是 1,假設 10 進位是 0.5,轉成二進位是 0.1,第一個必需是 1,所以要改成 1 * 2^-1 次方

0 01111110 00000000000000000000000 中間的是 -1 + 127  = 126 的二進位


Infinity 正無限:0 + 指數全是 1 + 小數全是 0

-Infinity 負無限:1 + 指數全是 1 + 小數全是 0

NaN 非數字:指數全是 1 + 小數不是 0,java 定義為 指數全是 1 + 小數第一個是 1 後面都是 0


※驗證:

一、以 8.5 為例,轉二進制不會循環,1000.1 轉成左邊只有一位 => 1.0001 * 2^3 (二的3次方,3是指數)

32 位元 (可用 Float.floatToIntBits(8.5F) 驗證):

0 3+127 0001000...

0 10000010 0001+19個0 => 01000001000010000000000000000000 => 1091043328


64位元(可用 Double.doubleToLongBits(8.5) 驗證)

0 3+1023 00010000000...

0 10000000010 (1後8個0) 0001+48個0 => 0100000000100001000000000000000000000000000000000000000000000000 => 4620974692658839552


二、以 12.4 為例,轉二進制會循環,1100.0110 0110 0110...(0110 沒完沒了) => 1.1000110 0110 0110...*2^3

只舉例 32 位元,看懂了自己算算看 64 位元

0 3+127 1000110 0110 0110 0110....

0 10000010 1000110 0110 0110 0110 0110 => 01000001010001100110011001100110 => 1095132774


三、正負無限

32位元

0 + 8個1 + 23個0 01111111100000000000000000000000 --> 7F800000

1 + 8個1 + 23個0 11111111100000000000000000000000 --> FF800000


64位元

0 + 11個1 + 52個0 0111111111110000000000000000000000000000000000000000000000000000 --> 7FF0000000000000

1 + 11個1 + 52個0 1111111111110000000000000000000000000000000000000000000000000000 --> FFF0000000000000


四、NaN

32位元

0 + 8個1 + 頭一個是1後面都是0

01111111110000000000000000000000 --> 7FC00000


64位元

0 + 11個1 + 頭一個是1後面都是0

0111111111111000000000000000000000000000000000000000000000000000

7FF8000000000000