學習總結 2017-10

前言

記錄10月份學習記錄,不定期更新

2017-10-05

windows查看端口占用和殺進程

查看端口占用
netstat -aon|findstr "49157"

找到進程pid后,殺掉該進程

taskkill /pid 12188 /f

2017-10-10

Java中的十個"單行代碼編程"(One Liner)

本文列舉了十個使用一行代碼即可獨立完成(不依賴其他代碼)的業務邏輯,主要依賴的是Java8中的Lambda和Stream等新特性以及try-with-resources、JAXB等。

對列表/數組中的每個元素都乘以2

 // Range是半開區間
 int[] ia = range(1, 10).map(i -> i * 2).toArray();
 List<Integer> result = range(1, 10).map(i -> i * 2).boxed().collect(toList());

計算集合/數組中的數字之和

 range(1, 1000).sum();
 range(1, 1000).reduce(0, Integer::sum);
 Stream.iterate(0, i -> i + 1).limit(1000).reduce(0, Integer::sum);
 IntStream.iterate(0, i -> i + 1).limit(1000).reduce(0, Integer::sum);

驗證字符串是否包含集合中的某一字符串

final List<String> keywords = Arrays.asList("brown", "fox", "dog", "pangram");
final String tweet = "The quick brown fox jumps over a lazy dog. #pangram http://www.rinkworks.com/words/pangrams.shtml";

keywords.stream().anyMatch(tweet::contains);
keywords.stream().reduce(false, (b, keyword) -> b || tweet.contains(keyword), (l, r) -> l || r);

讀取文件內容

原作者認為try with resources也是一種單行代碼編程。

 try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
   String fileText = reader.lines().reduce("", String::concat);
 }

 try (BufferedReader reader = new BufferedReader(new FileReader("data.txt"))) {
   List<String> fileLines = reader.lines().collect(toCollection(LinkedList<String>::new));
 }

 try (Stream<String> lines = Files.lines(new File("data.txt").toPath(), Charset.defaultCharset())) {
   List<String> fileLines = lines.collect(toCollection(LinkedList<String>::new));
 }

輸出歌曲《Happy Birthday to You!》 - 根據集合中不同的元素輸出不同的字符串

 range(1, 5).boxed().map(i -> { out.print("Happy Birthday "); if (i == 3) return "dear NAME"; else return "to You"; }).forEach(out::println);

過濾并分組集合中的數字

 Map<String, List<Integer>> result = Stream.of(49, 58, 76, 82, 88, 90).collect(groupingBy(forPredicate(i -> i > 60, "passed", "failed")));

獲取并解析xml協議的Web Service

FeedType feed = JAXB.unmarshal(new URL("http://search.twitter.com/search.atom?&q=java8"), FeedType.class);
JAXB.marshal(feed, System.out);

獲得集合中最小/最大的數字

 int min = Stream.of(14, 35, -7, 46, 98).reduce(Integer::min).get();
 min = Stream.of(14, 35, -7, 46, 98).min(Integer::compare).get();
 min = Stream.of(14, 35, -7, 46, 98).mapToInt(Integer::new).min();

 int max = Stream.of(14, 35, -7, 46, 98).reduce(Integer::max).get();
 max = Stream.of(14, 35, -7, 46, 98).max(Integer::compare).get();
 max = Stream.of(14, 35, -7, 46, 98).mapToInt(Integer::new).max();

并行處理

 long result = dataList.parallelStream().mapToInt(line -> processItem(line)).sum();

集合上的各種查詢(LINQ in Java)

List<Album> albums = Arrays.asList(unapologetic, tailgates, red);

//篩選出至少有一個track評級4分以上的專輯,并按照名稱排序后打印出來。
albums.stream()
  .filter(a -> a.tracks.stream().anyMatch(t -> (t.rating >= 4)))
  .sorted(comparing(album -> album.name))
  .forEach(album -> System.out.println(album.name));

//合并所有專輯的track
List<Track> allTracks = albums.stream()
  .flatMap(album -> album.tracks.stream())
  .collect(toList());

//根據track的評分對所有track分組
Map<Integer, List<Track>> tracksByRating = allTracks.stream()
  .collect(groupingBy(Track::getRating));

2017-10-17

Jackson的@JsonFormat注解日期少一天問題

項目中使用了@JsonFormat來進行格式化日期,卻意外發現日期少了一天,
比如數據庫存的日期是2017-10-15,轉成json則變成了2017-10-14
于是查資料,發現這個注解有個坑,JsonFormat默認是不帶時區
解決辦法:

@JsonFormat(pattern="yyyy-MM-dd")
 public Date getRegistDate() {
  return this.registDate;
 }

改成

@JsonFormat(pattern="yyyy-MM-dd",timezone="GMT+8")
 public Date getRegistDate() {
  return this.registDate;
 }

加上時區即可,中國是東八區

2017-10-18

mysql如何解決幻讀問題

mysql默認的事務隔離級別是repeatable-read,也就是可重復讀,按照sql事務標準的話,這個隔壁級別是可以解決臟讀和不可重復的問題的,但是不能解決幻讀的問題,但是mysql卻解決了幻讀這個問題,那到底是怎么實現的呢?

官方文檔

http://dev.mysql.com/doc/refman/5.0/en/innodb-record-level-locks.html

By default, InnoDB operates in REPEATABLE READ transaction isolation level and with the innodb_locks_unsafe_for_binlog system variable disabled. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows (see Section 13.6.8.5, “Avoiding the Phantom Problem Using Next-Key Locking”).

準備的理解是,當隔離級別是可重復讀,且禁用innodb_locks_unsafe_for_binlog的情況下,在搜索和掃描index的時候使用的next-key locks可以避免幻讀。

關鍵點在于,是InnoDB默認對一個普通的查詢也會加next-key locks,還是說需要應用自己來加鎖呢?如果單看這一句,可能會以為InnoDB對普通的查詢也加了鎖,如果是,那和序列化(SERIALIZABLE)的區別又在哪里呢?

MySQL manual里還有一段:

13.2.8.5. Avoiding the Phantom Problem Using Next-Key Locking (http://dev.mysql.com/doc/refman/5.0/en/innodb-next-key-locking.html)

To prevent phantoms, InnoDB uses an algorithm called next-key locking that combines index-row locking with gap locking.

You can use next-key locking to implement a uniqueness check in your application: If you read your data in share mode and do not see a duplicate for a row you are going to insert, then you can safely insert your row and know that the next-key lock set on the successor of your row during the read prevents anyone meanwhile inserting a duplicate for your row. Thus, the next-key locking enables you to “lock” the nonexistence of something in your table.

我的理解是說,InnoDB提供了next-key locks,但需要應用程序自己去加鎖。manual里提供一個例子:

SELECT * FROM child WHERE id > 100 FOR UPDATE;

SHOW ENGINE INNODB STATUS 來查看是否給表加上了鎖。

mySQL manual里對可重復讀里的鎖的詳細解釋:

http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html#isolevel_repeatable-read

For locking reads (SELECT with FOR UPDATE or LOCK IN SHARE MODE),UPDATE, and DELETE statements, locking depends on whether the statement uses a unique index with a unique search condition, or a range-type search condition. For a unique index with a unique search condition, InnoDB locks only the index record found, not the gap before it. For other search conditions, InnoDB locks the index range scanned, using gap locks or next-key (gap plus index-record) locks to block insertions by other sessions into the gaps covered by the range.

實驗

一致性讀和提交讀,先看實驗,實驗四:

t Session A                      Session B
|
| START TRANSACTION;             START TRANSACTION;
|
| SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 | a     |
| +----+-------+
|                                INSERT INTO t_bitfly
|                                VALUES (2, 'b');
|                                COMMIT;
|
| SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 | a     |
| +----+-------+
|
| SELECT * FROM t_bitfly LOCK IN SHARE MODE;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 | a     |
| |  2 | b     |
| +----+-------+
|
| SELECT * FROM t_bitfly FOR UPDATE;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 | a     |
| |  2 | b     |
| +----+-------+
|
| SELECT * FROM t_bitfly;
| +----+-------+
| | id | value |
| +----+-------+
| |  1 | a     |
| +----+-------+
v

如果使用普通的讀,會得到一致性的結果,如果使用了加鎖的讀,就會讀到“最新的”“提交”讀的結果。

本身,可重復讀和提交讀是矛盾的。在同一個事務里,如果保證了可重復讀,就會看不到其他事務的提交,違背了提交讀;如果保證了提交讀,就會導致前后兩次讀到的結果不一致,違背了可重復讀。

可以這么講,InnoDB提供了這樣的機制,在默認的可重復讀的隔離級別里,可以使用加鎖讀去查詢最新的數據。

http://dev.mysql.com/doc/refman/5.0/en/innodb-consistent-read.html

If you want to see the “freshest” state of the database, you should use either the READ COMMITTED isolation level or a locking read:
SELECT * FROM t_bitfly LOCK IN SHARE MODE;

------

mvcc

在官方文檔中寫道

http://dev.mysql.com/doc/refman/5.7/en/innodb-consistent-read.html

A consistent read means that InnoDB uses multi-versioning to present to a query a snapshot of the database at a point in time. The query sees the changes made by transactions that committed before that point of time, and no changes made by later or uncommitted transactions. The exception to this rule is that the query sees the changes made by earlier statements within the same transaction. This exception causes the following anomaly: If you update some rows in a table, a SELECT sees the latest version of the updated rows, but it might also see older versions of any rows. If other sessions simultaneously update the same table, the anomaly means that you might see the table in a state that never existed in the database.

一致性讀是通過 MVCC 為查詢提供了一個基于時間的點的快照。這個查詢只能看到在自己之前提交的數據,而在查詢開始之后提交的數據是不可以看到的。一個特例是,這個查詢可以看到于自己開始之后的同一個事務產生的變化。這個特例會產生一些反常的現象

If the transaction isolation level is REPEATABLE READ (the default level), all consistent reads within the same transaction read the snapshot established by the first such read in that transaction. You can get a fresher snapshot for your queries by committing the current transaction and after that issuing new queries.

在默認隔離級別REPEATABLE READ下,同一事務的所有一致性讀只會讀取第一次查詢時創建的快照

結論

結論:MySQL InnoDB的可重復讀并不保證避免幻讀,需要應用使用加鎖讀來保證。而這個加鎖度使用到的機制就是next-key locks。

MySQL MVCC簡介

什么是MVCC

MVCC是一種多版本并發控制機制。

MVCC是為了解決什么問題?

大多數的MYSQL事務型存儲引擎,如,InnoDB,Falcon以及PBXT都不使用一種簡單的行鎖機制.事實上,他們都和MVCC–多版本并發控制來一起使用.
大家都應該知道,鎖機制可以控制并發操作,但是其系統開銷較大,而MVCC可以在大多數情況下代替行級鎖,使用MVCC,能降低其系統開銷.

MVCC實現

MVCC是通過保存數據在某個時間點的快照來實現的. 不同存儲引擎的MVCC. 不同存儲引擎的MVCC實現是不同的,典型的有樂觀并發控制和悲觀并發控制.

具體實現分析

下面,我們通過InnoDB的MVCC實現來分析MVCC使怎樣進行并發控制的.
InnoDB的MVCC,是通過在每行記錄后面保存兩個隱藏的列來實現的,這兩個列,分別保存了這個行的創建時間,一個保存的是行的刪除時間。這里存儲的并不是實際的時間值,而是系統版本號(可以理解為事務的ID),沒開始一個新的事務,系統版本號就會自動遞增,事務開始時刻的系統版本號會作為事務的ID.下面看一下在REPEATABLE READ隔離級別下,MVCC具體是如何操作的.

簡單的小例子

create table zhang( 
id int primary key auto_increment, 
name varchar(20));

假設系統的版本號從1開始.

INSERT

InnoDB為新插入的每一行保存當前系統版本號作為版本號.
第一個事務ID為1;

start transaction;
insert into yang values(NULL,'zhang') ;
insert into yang values(NULL,'guo');
insert into yang values(NULL,'ji');
commit;

對應在數據中的表如下(后面兩列是隱藏列,我們通過查詢語句并看不到)

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 undefined
2 guo 1 undefined
3 ji 1 undefined

SELECT

InnoDB會根據以下兩個條件檢查每行記錄:
a.InnoDB只會查找版本早于當前事務版本的數據行(也就是,行的系統版本號小于或等于事務的系統版本號),這樣可以確保事務讀取的行,要么是在事務開始前已經存在的,要么是事務自身插入或者修改過的.
b.行的刪除版本要么未定義,要么大于當前事務版本號,這可以確保事務讀取到的行,在事務開始之前未被刪除.
只有a,b同時滿足的記錄,才能返回作為查詢結果.

DELETE

InnoDB會為刪除的每一行保存當前系統的版本號(事務的ID)作為刪除標識.
看下面的具體例子分析:
第二個事務,ID為2;

start transaction;
select * from zhang;  //(1)
select * from zhang;  //(2)
commit; 

假設1

假設在執行這個事務ID為2的過程中,剛執行到(1),這時,有另一個事務ID為3往這個表里插入了一條數據;
第三個事務ID為3;

start transaction;
insert into zhang values(NULL,'tian');
commit;

這時表中的數據如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 undefined
2 guo 1 undefined
3 ji 1 undefined
4 tian 3 undefined

然后接著執行事務2中的(2),由于id=4的數據的創建時間(事務ID為3),執行當前事務的ID為2,而InnoDB只會查找事務ID小于等于當前事務ID的數據行,所以id=4的數據行并不會在執行事務2中的(2)被檢索出來,在事務2中的兩條select 語句檢索出來的數據都只會下表:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 undefined
2 guo 1 undefined
3 ji 1 undefined

假設2

假設在執行這個事務ID為2的過程中,剛執行到(1),假設事務執行完事務3后,接著又執行了事務4;
第四個事務:

start transaction;  
delete from zhang where id=1;
commit;

此時數據庫中的表如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 4
2 guo 1 undefined
3 ji 1 undefined
4 tian 3 undefined

接著執行事務ID為2的事務(2),根據SELECT 檢索條件可以知道,它會檢索創建時間(創建事務的ID)小于當前事務ID的行和刪除時間(刪除事務的ID)大于當前事務的行,而id=4的行上面已經說過,而id=1的行由于刪除時間(刪除事務的ID)大于當前事務的ID,所以事務2的(2)select * from yang也會把id=1的數據檢索出來.所以,事務2中的兩條select 語句檢索出來的數據都如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 undefined
2 guo 1 undefined
3 ji 1 undefined

UPDATE

InnoDB執行UPDATE,實際上是新插入了一行記錄,并保存其創建時間為當前事務的ID,同時保存當前事務ID到要UPDATE的行的刪除時間.

假設3

假設在執行完事務2的(1)后又執行,其它用戶執行了事務3,4,這時,又有一個用戶對這張表執行了UPDATE操作:
第5個事務:

start  transaction;
update yang set name='Guo' where id=2;
commit;

根據update的更新原則:會生成新的一行,并在原來要修改的列的刪除時間列上添加本事務ID,得到表如下:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 4
2 guo 1 5
3 ji 1 undefined
4 tian 3 undefined
5 Guo 5 undefined

繼續執行事務2的(2),根據select 語句的檢索條件,得到下表:

id name 創建時間(事務ID) 刪除時間(事務ID)
1 zhang 1 undefined
2 guo 1 undefined
3 ji 1 undefined

還是和事務2中(1)select 得到相同的結果.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容