這篇文章為大家帶來了關於mysql中鎖的各種類型與模式的相關知識,希望對大家有幫助。
在日常開發工作中,我們幾乎需要天天與資料庫打交道,作為一個只會CRUD的SQL BOY,除了每天用mybatis-generator自動產生DAO層程式碼之外,我們幾乎不用去care資料庫中如何處理並發請求,但是突然某一天MYSQL資料庫告警了,出現了死鎖,我們的內心慌的一匹,不禁想問:這不就是個普通查詢嗎,咋還鎖起來了?
為了避免慌亂的表情被主管捕捉到,我們需要事先了解資料庫中到底有哪些鎖定。
在MySQL中,其實將鎖定分成了兩類:鎖定類型(lock_type)和鎖定模式(lock_mode)。
鎖類型所描述的鎖的粒度,也就是把鎖具體加在什麼地方;而鎖模式描述的是到底加的是什麼鎖,是讀鎖還是寫鎖。鎖模式通常和鎖類型結合使用。
按鎖定的模式分
讀取鎖定
#讀取鎖定,又叫共用鎖定/S鎖定/share locks 。
讀鎖是某個事務(例如事務A)在進行讀取操作(例如讀取一張表或讀取某一行)時創建出來的鎖,其他的事務可以並發地讀取這些數據(被加了鎖的),但是不能修改這些資料(除非持有鎖的用戶已經釋放鎖)。
事務A對資料加上讀鎖之後,其他事務依然可以對其添加讀鎖(共享),但是不能添加寫鎖。
在記錄上加讀鎖定
InnoDB支援表鎖和行鎖,在行(也就是記錄)上加鎖,並不是鎖住該筆記錄,而是在記錄對應的索引上加鎖。如果where條件中不走索引,則會對所有的記錄加鎖。
明確加鎖語句為:
注意:這裡所說的讀,是指當前讀,快照讀是無需加鎖的。普通select讀一般都是快照讀,除了select...lock in share mode這樣的明確加鎖語句下會變成當前讀,在InnoDB引擎的serializable級別下,普通select讀也會變成快照讀。
另外要注意,對於行鎖的加鎖過程分析,要根據事務隔離等級、是否使用索引(哪種類型的索引)、記錄是否存在等因素結合分析,才能判斷在哪裡加上了鎖。
innodb引擎中的加讀鎖定的幾種情況
普通查詢在隔離層級為 serializable 會為記錄加上S鎖定。但這也取決於場景:非事務讀取(auto-commit)在Serializable 隔離等級下,無需加鎖;
Serializable隔離等級時:如果查詢條件為唯一索引且是唯一等值查詢時:是在該筆記錄上加S鎖;非唯一條件查詢(查詢會掃描到多筆記錄時):記錄本身記錄的間隙(需要具體分析間隙的範圍),加S鎖;
select … in share mode,會為記錄加S鎖,但是根據隔離等級的不同,加鎖的行為有所不同:
RC隔離等級:是在記錄上加S鎖。 RR/Serializable隔離等級:如果查詢條件為唯一索引且是唯一等值查詢時:是在該筆記錄上加S鎖;非唯一條件查詢(查詢會掃描到多筆記錄時):記錄本身記錄的間隙(需要具體分析間隙的範圍),加上S鎖;
通常insert操作是不加鎖的,但如果在插入或更新記錄時,檢查到duplicate key(或者有一個被標記刪除的duplicate key),對於普通的insert/update,會加S鎖,而對於類似replace into或insert … on duplicate 這樣的SQL語句加的是X鎖。
insert … select 插入資料時,會對select 的表上掃描到的資料加上S鎖;
外鍵檢查:當我們刪除一條父表上的記錄時,需要去檢查是否有引用約束,這時候會掃描子表上對應的記錄,並加上S鎖定。
在表上加讀鎖定
表鎖定由 MySQL伺服器實現,無論儲存引擎是什麼,都可以使用表鎖定。一般在執行 DDL 語句時,譬如 ALTER TABLE 時就會對整個表格加鎖。執行 SQL 語句時,也可以明確地對某個表格加鎖。
給表明確加鎖語句為:
在使用MYISAM引擎時,通常我們不需要手動加鎖,因為MYISAM引擎會針對我們的sql語句會自動進行加鎖,整個過程不需要使用者乾預:
查詢語句(select):會自動為涉及的表加讀鎖定;
更新語句(update、delete、insert):會自動為涉及的表加上寫鎖定。
寫鎖定
寫鎖,排他鎖/X鎖/exclusive locks。寫鎖的阻塞性比讀鎖要嚴格的多,一個事務對資料添加寫鎖之後,其他的事務對該數據,既不能讀取也不能更改。
與讀取鎖定加鎖的範圍相同,寫入鎖定既可以加在記錄上,也可以加在表上。
在記錄上加上寫鎖定
在記錄上加寫鎖定,引擎需要使用InnoDB。
通常普通的select語句是不會加鎖的(隔離等級為Serializable除外),想要在查詢時加入排他鎖需要使用以下語句:
查詢時加寫鎖定:
與加讀鎖定相同,寫鎖定也是加在索引上的。
更新時加上寫入鎖定:
#在表格上加寫鎖定
明確給表加上寫鎖定的語句為:
當引擎選擇myisam時,insert/update/delete語句,會自動給該表加上排他鎖定。
讀寫鎖定相容性:
讀取鎖定是共用的,它不會阻塞其他讀鎖定,但會阻塞其他的寫入鎖定;
寫鎖是排他的,它會阻塞其他讀鎖和寫鎖;
#總結:讀讀不互斥,讀寫互斥,寫寫互斥
意向鎖定
#意向鎖定是一種不與行級鎖定衝突的表格層級鎖定,表示表中的記錄所需要的鎖(S鎖或X鎖)的類型(其實就是告訴你,這張表中已經存在了行鎖(行鎖的類型),所以叫意向鎖)。 InnoDB支援多種粒度的鎖,允許行級鎖和表級鎖的共存。
意向鎖定分為:
意向共享鎖定(IS鎖定):IS鎖定表示當前交易意圖在表中的行上設定共享鎖定
下面語句執行時會先取得IS鎖,因為這個操作在取得S鎖:取得S鎖:select ... lock in share mode
意向排它鎖(IX鎖):IX鎖表示目前事務意圖在表中的行上設定排它鎖
下面語句執行時會先取得IX鎖,因為這個動作在取得X鎖:取得X鎖:select ... for update
交易要取得在某表上的S鎖和X鎖之前,必須先分別取得對應的IS鎖和IX鎖。
意向鎖有什麼作用呢:
如果另一個事務試圖在該表層級的共享鎖或排它鎖,則受到由第一個事務控制的表級意向鎖的阻塞。第二個交易在鎖定該表前不必檢查各個頁或行鎖,而只需檢查表上的意向鎖。
範例:表格test_user:
#交易A 取得了某一行的排他鎖,並未提交;
交易B 想要取得test_user 表的資料表共用鎖定;
因為共用鎖定與排他鎖定互斥,所以交易B 在試圖對test_user 表加上共用鎖定的時候,必須保證:
目前沒有其他交易持有users 資料表的排他鎖定(表排他鎖)。
目前沒有其他交易持有 users 表中任何一行的排他鎖定(行排他鎖)。
為了偵測是否符合第二個條件,交易 B 必須在確保 test_user表不存在任何排他鎖的前提下,去偵測表中的每一行是否存在排他鎖。很明顯這是一個效率很差的做法,但是有了意向鎖之後,情況就不一樣了:
因為此時事務A獲取了兩把鎖:users 表上的意向排他鎖與id為28 的資料行上的排他鎖。
交易B 想要取得test_user 表的共享鎖定:
交易B 只需要偵測交易A 是否持有test_user 表的意向排他鎖,就可以得知交易A 必然持有該表中某些資料行的排他鎖,那麼事務B 對test_users 表的加鎖請求就會被排斥(阻塞),從而無需去檢測表中的每一行資料是否存在排他鎖。
交易C 也想取得users 表中某一行的排他鎖:
-
#交易C 偵測到交易A 持有test_user 表的意向排他鎖定;
意向鎖定之間並不互斥,所以交易C 取得到了test_user 表的意向排他鎖;
因為id 為31 的資料行上不存在任何排他鎖,最終事務C 成功取得到了該資料行上的排他鎖。
意向鎖與意向鎖之間是不互斥的,但是意向鎖與其他表鎖之間存在一定的相容互斥,具體如下:
意向鎖定之間的相容互斥性:
意向鎖定與普通的排他/ 共享鎖定互斥:
#自增鎖
我們在設計表結構的時候,通常會把主鍵設定成自增長(思考為什麼?)。
在InnoDB儲存引擎中,針對每個自增長的欄位都設定了一個自增長的計數器。我們可以執行下面的語句來得到這個計數器的當前值:
當我們進行插入操作的時候,該操作會根據這個自增長的計數器的當前值進行1操作,並賦予自增長的列,這個操作我們稱之為auto-inc Locking,也就是自增長鎖,這種鎖其實採用的是特殊的表鎖機制,如果insert操作出現在一個事務中,這個鎖是在insert操作完成之後立即釋放,而不是等待交易提交。
按鎖的型別分
全域鎖定
#所謂全域鎖,其實就是要為整個資料庫實例加鎖。
資料庫實例與資料庫是有所區別的:
資料庫,就是保存資料的倉庫,具體到mysql中,資料庫其實是一系列資料檔案集合(也就是我們通常所說的database,例如建立資料庫語句就是create database...)。
資料庫實例,是指存取資料庫的應用程序,在Mysql中,就是mysqld進程了。
簡單來理解,資料庫實例中包含了你所建立的各種資料庫。
如果為資料庫實例加上全域鎖定會導致整個庫處於唯讀狀態(這是非常危險的)。
一般來說,全域鎖定的典型使用場景是用於全庫備份,也就是把資料庫中所有的表都select出來。但要注意,讓整個庫都處於唯讀狀態,會導致一些嚴重的問題:
#在主庫上加全域鎖,在加鎖期間,不能執行任何更新操作,業務基本上很多功能都不可用了;
在從庫上加全域鎖,在加鎖期間,不能執行主從同步,會導致主從同步延遲。
全域鎖定的加鎖語句是:
#解除全域鎖定的方法是:
-
#斷開執行全域鎖定的session即可;
- ##執行解鎖sql語句:unlock tables;
元資料鎖
元資料鎖(MetaData Lock),也叫MDL鎖,是用來保護元資料訊息,系統級的鎖無法主動控制。在MySQL5.5版本,開始引入MDL鎖定,主要是為了在並發環境下對DDL、DML同時操作下保持元資料的一致性。例如下面這種情況:隔離等級:RR- 對錶中的記錄進行增刪改查(DML操作)的時候,自動加上MDL讀鎖定;
- 對錶的結構(DDL操作)進行修改的時候,自動加MDL寫鎖。
MDL鎖定的粒度
#MDL鎖定是Mysql伺服器層面中實現的,而不是在儲存引擎插件中實現。依照鎖定的範圍,MDL鎖定可以分為以下幾類:MDL鎖定的模式
#頁級鎖定
#MySQL中鎖定粒度介於行級鎖定和表級鎖定中間的一種鎖定。表級鎖定速度快,但衝突多,行級衝突少,但速度慢。所以取了折衷的頁級,一次鎖定鄰近的一組記錄。不同的儲存引擎支援不同的鎖定機制。根據不同的儲存引擎,MySQL中鎖定的特性可以大致歸納如下:
#頁級鎖定是MySQL中比較獨特的一種鎖定級別,套用到BDB引擎,並發度一般,頁級鎖定的特點是鎖定顆粒度介於行級鎖定與表級鎖之間,所以獲取鎖定所需的資源開銷,以及所能提供的並發處理能力也同樣是介於上面二者之間。另外,頁級鎖定和行級鎖定一樣,會發生死鎖。
鎖定粒度大小比較:表級鎖定> 頁級鎖定>行級鎖定
表級鎖定
表鎖在上文我們已經介紹過,相較於行鎖的細粒度加鎖,表鎖是對整張表加鎖。由於是對整張表加鎖,就沒有行鎖的加鎖方式那麼複雜,所以加鎖比行鎖快,而且不會出現死鎖的情況(因為事務是一次性獲取想要加的表表鎖),但是表鎖也存在一些問題:鎖的範圍太大,在並發比較高的情況下,會導致搶鎖的衝突機率變高,這樣並發性能就大打折扣了。
表鎖的加鎖方式
- 手動新增表格級鎖定的語句如下:
在使用MYISAM引擎時,通常我們不需要手動加鎖,因為MYISAM引擎會針對我們的sql語句會自動進行加鎖,整個過程不需要使用者乾預:
查詢語句(select):會自動為涉及的表加讀鎖定;更新語句(update、delete、insert):會自動為涉及的表加上寫入鎖定
引擎選擇InnoDB時
- InnoDB引擎同時支援行級鎖定和表格級鎖定,預設為行級鎖定。
為InnoDB引擎的資料表手動加鎖,也同樣使用 lock table {tableName} read/write 語句進行讀取/寫入鎖定的新增。
除此之外,innodb也支援一種表級鎖定:意向鎖(上文已經介紹過)。 總的來說,InnoDB引擎的表格層級鎖定包含五種鎖定模式:
#LOCK_IS:表格意圖讀取鎖定
-
LOCK_IX:表格意圖寫入鎖定
LOCK_S:表格讀取鎖定
LOCK_X:表格寫鎖定
LOCK_AUTO_INC:自增鎖
- #行級鎖定
- 在撰寫業務程式碼的過程中,我們接觸最多的就是行級鎖了(表級鎖由於效能問題,一般不建議使用)。相較於表級鎖,行級鎖具有明顯的效能優勢:
- 衝突少:多執行緒中存取不同的記錄時只存在少量鎖定衝突;
#鎖定的粒度小:可以長時間鎖定單一的行,對其他的行沒有影響,所以並發度是最高的;
但是使用行鎖時,一旦稍不注意,是非常容易出現死鎖的(表鎖就不存在死鎖現象),所以使用行鎖需要注意加鎖的順序和鎖定的範圍。
###InnoDB的行鎖是透過對索引項目加鎖實現的,這表示只有透過索引查詢記錄時才會使用行鎖,如果不走索引查詢資料將使用表鎖,則效能會大打折扣。 ######要記住:行鎖也叫記錄鎖,記錄鎖都是加在索引上的。 ############where條件指定的是主鍵索引:則在主鍵索引上加鎖;############wehre條件指定的是二級索引:記錄鎖定不僅會加在這個二級索引上,還會加在這個二級索引所對應的叢集索引上;############where條件如果無法走索引:MySQL會給整張表所有資料行加記錄鎖,儲存引擎層將所有記錄傳回由MySQL服務端進行過濾。 ##################記錄鎖定:LOCK_REC_NOT_GAP(只鎖定記錄)############記錄鎖定是最簡單的行鎖定。例如在RR隔離等級時,執行 select * from t_user where id = 1 for update 語句時,實際上是對 id = 1 (這裡id為主鍵)這條記錄上鎖(鎖加在聚簇索引上)。 ###記錄鎖永遠都是加在索引上的,就算一個表沒有建立索引,資料庫也會隱含的建立一個索引。如果 WHERE 條件中指定的欄位是個二級索引,那麼記錄鎖定不僅會加在這個二級索引上,還會加在這個二級索引所對應的叢集索引上。
注意,如果 SQL 語句無法使用索引時會走主索引實作全表掃描,這個時候 MySQL 會為整個表的所有資料行加上記錄鎖定。
如果一個 WHERE 條件無法透過索引快速過濾,儲存引擎層面就會將所有記錄加鎖後傳回,再由 MySQL Server 層進行過濾。在沒有索引時,不僅會消耗大量的鎖定資源,增加資料庫的開銷,而且極大的降低了資料庫的並發效能,所以說,更新作業一定要記得走索引(因為更新作業會加X鎖定)。
行級鎖的幾種類型:
間隙鎖:LOCK_GAP(只鎖間隙)
間隙鎖是一種區間鎖。鎖加在不存在的空閒空間上,或者兩個索引記錄之間,或者第一個索引記錄,或者最後一個索引之後的空間,用來表示只鎖住一段範圍(一般在進行範圍查詢時且隔離級別在RR或Serializable隔時)。
一般在RR隔離等級下會使用到GAP鎖定。使用GAP鎖,主要是為了防止幻讀產生,在被GAP鎖鎖住的區間,不允許插入資料或更新資料。
間隙鎖的產生條件:innodb的隔離等級為 Repeatable Read 或 Serializable。
間隙鎖定的作用範圍說明:
隔離等級:RR
#以Student表作為範例數據,id為主鍵, stu_code為學生編號,新增普通索引。
間隙鎖定區域定義:
根據檢索條件向左尋找最靠近的值A,作為左區間,向右尋找最靠近的值B,作為右區間,間隙鎖定為( A,B)
向左找不到最近的值A,也是就無窮小,作為左區間,向右尋找最靠近的值B,作為右區間,間隙鎖為(無窮小,B)
向左找到最近的值A,作為左區間,向右尋找不到最近的值B,也就是無窮大,作為右區間,間隙鎖為(A,無窮大)
區間(A,B)範例:
事務1:
select * from student where stu_code = 4 for update
事務2:
insert into student vaues(2, 2, 'A'); insert into student values(4, 5, 'B');
根據事務1的sql語句分析,間隙鎖的範圍是:stu_code = 4記錄是存在的,所以左區間為最近的索引值為stu_code = 3,右區間為最近的索引值為stu_code =7,所以間隙範圍為:(3,7),因此事務2的兩個insert 語句,一個在範圍外,一個在範圍內,在範圍外的能插入,而範圍內的則阻塞,所以(2,2, 'A')能插入成功;(4,5, 'B')插入阻塞。
區間(無窮小,B)範例:
交易1:
select * from student where stu_code = 1 for update
交易2:
insert into student vaues(2, 0, 'c'); insert into student vaues(2, 2, 'r'); insert into student vaues(5, 2, 'o');
根據交易1的sql語句分析,間隙鎖的範圍是:stu_code = 1 是存在的,左邊最近沒有記錄,所以是左邊的無窮小,右邊最近的索引值為stu_code = 3,所以間隙鎖範圍為:(無窮小,3)。所以事務2的第一個和第二個insert sql語句執行被阻塞,是在間隙鎖範圍內的。第三個insert sql語句能執行成功,不在間隙鎖定範圍內。
區間(A,無窮大)範例:
交易1:
select * from student where stu_code = 7 for update
交易2:
insert into student vaues(2, 2, 'm'); insert into student vaues(20, 22, 'j');
根據交易1的sql語句分析,間隙鎖的範圍是:stu_code = 7 是存在的,左邊最近的索引值為stu_code = 4,而右邊是沒有索引值的,所以間隙鎖的範圍為:(4,無限大),第一個inset語句能執行成功,不在間隙範圍內;第二個insert語句執行被阻塞,是在間隙鎖範圍內的。
如果查詢語句在資料庫中沒有記錄,那該怎麼鎖呢?
以上是查詢是有記錄的,如果查詢語句在資料庫中沒有記錄,那該怎麼鎖呢?咱們繼續往下:
事務1:
update student set stu_name = '000' where stu_code = 10
交易2:
insert into student vaues(2, 2, 'm'); insert into student vaues(20, 22, 'j');
根據上面的執行語句是找不到記錄的,向左取最近的記錄( 10,7,'小明')作為左區間,即間隙鎖的範圍是:(7, 無窮大),第一個insert語句不在區間範圍內,能執行成功;第二個insert執行語句在區間內被阻塞,執行失敗。如果事務1的where 條件是大於10,也是向左找最近的記錄值作為左區間,所以間隙鎖的範圍也是:(7, 無窮大)
總結:間隙鎖產生的條件
RR/Serializable隔離等級下:Select ... Where...For Update 時:
#只使用唯一索引查詢,且只鎖定一筆記錄時,InnoDB會使用行鎖定。
只使用唯一索引查詢,但是檢索條件是範圍檢索,或是唯一檢索然而檢索結果不存在(試圖鎖住不存在的資料)時,會產生 Next-Key Lock。
使用普通索引檢索時,不管是何種查詢,只要加鎖,都會產生間隙鎖定。
同時使用唯一索引和普通索引時,由於資料行是優先根據普通索引排序,再根據唯一索引排序,所以也會產生間隙鎖定。
下一鍵鎖:LOCK_ORDINARY,也稱為Next-Key Lock
Next-Key鎖定是 record lock gap lock 的組合。和間隙鎖一樣,在 RC 隔離等級下沒有 Next-key 鎖(除非透過修改配置強制開啟),只有 RR/Serializable隔離等級才有。
MySQL InnoDB工作在可重複讀取隔離等級(RR)下,並且會以Next-Key Lock的方式對資料行進行加鎖,這樣可以有效防止幻讀的發生。 Next-Key Lock是行鎖定和間隙鎖定的組合,當InnoDB掃描索引記錄的時候,會先對索引記錄加上行鎖定(Record Lock),再對索引記錄兩邊的間隙加上間隙鎖定(Gap Lock)。加上間隙鎖之後,其他事務就不能在這個間隙修改或插入記錄。
當查詢的索引含有唯一屬性(唯一索引,主鍵索引)時,Innodb儲存引擎會對next-key lock進行最佳化,將其降為record lock,即僅鎖定索引本身,而不是範圍。
插入意圖鎖:LOCK_INSERT_INTENSION
#插入意圖鎖,插入記錄時使用,是一種特殊的間隙鎖。這個鎖表示插入的意向,只有在執行insert語句的時候才會有這個鎖。
假設有索引記錄的值分別是id = 1和id = 5(1到5之間沒有記錄),單獨的事務分別嘗試插入id = 2和id = 3,在獲得插入行的在排它鎖之前,每個事務都是用插入意圖鎖來鎖定1和5之間的空間,但不會互相阻塞。因為插入意向鎖之間是不會衝突的。
插入意向鎖定會跟間隙鎖定或Next-Key鎖定衝突:間隙鎖的作用是鎖住區間防止其他交易插入資料導致幻讀。
在上面的場景中,假設提前有事務A獲取了id 在(1,5)區間的間隙鎖,那麼事務B嘗試插入id = 2時,會先嘗試獲取插入意向鎖,但是由於插入意向鎖和間隙鎖衝突,導致插入失敗,也就避免了幻讀產生。
結語
MYSQL的鎖定機制非常複雜,在實際的開發工作中,對於隔離層級的設定都需要非常謹慎,例如RR等級會比RC等級多出一個間隙鎖,這可能會導致嚴重的效能問題。本文從鎖的模式和鎖的範圍對MYSQL鎖的分類進行了簡單介紹,希望我們在面向資料庫開發的過程中,能夠仔細分析研究我們的SQL語句是否合理(尤其需要注意是否會產生死鎖等問題)!
推薦學習:mysql影片教學
#以上是一起聊聊MYSQL中鎖的各種模式與類型的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本篇文章给大家带来了关于mysql的相关知识,其中主要介绍了关于架构原理的相关内容,MySQL Server架构自顶向下大致可以分网络连接层、服务层、存储引擎层和系统文件层,下面一起来看一下,希望对大家有帮助。

mysql的msi与zip版本的区别:1、zip包含的安装程序是一种主动安装,而msi包含的是被installer所用的安装文件以提交请求的方式安装;2、zip是一种数据压缩和文档存储的文件格式,msi是微软格式的安装包。

方法:1、利用right函数,语法为“update 表名 set 指定字段 = right(指定字段, length(指定字段)-1)...”;2、利用substring函数,语法为“select substring(指定字段,2)..”。

在mysql中,可以利用char()和REPLACE()函数来替换换行符;REPLACE()函数可以用新字符串替换列中的换行符,而换行符可使用“char(13)”来表示,语法为“replace(字段名,char(13),'新字符串') ”。

转换方法:1、利用cast函数,语法“select * from 表名 order by cast(字段名 as SIGNED)”;2、利用“select * from 表名 order by CONVERT(字段名,SIGNED)”语句。

本篇文章给大家带来了关于mysql的相关知识,其中主要介绍了关于MySQL复制技术的相关问题,包括了异步复制、半同步复制等等内容,下面一起来看一下,希望对大家有帮助。

在mysql中,可以利用REGEXP运算符判断数据是否是数字类型,语法为“String REGEXP '[^0-9.]'”;该运算符是正则表达式的缩写,若数据字符中含有数字时,返回的结果是true,反之返回的结果是false。

在mysql中,可利用“ALTER TABLE 表名 DROP INDEX unique key名”语句来删除unique key;ALTER TABLE语句用于对数据进行添加、删除或修改操作,DROP INDEX语句用于表示删除约束操作。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

ZendStudio 13.5.1 Mac
強大的PHP整合開發環境

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Dreamweaver Mac版
視覺化網頁開發工具