MySQL的鎖到底有多少內(nèi)容 ?再和騰訊大佬的技術(shù)面談,我還是小看鎖了!
對(duì)酒當(dāng)歌,人生幾何! 朝朝暮暮,唯有己脫。
苦苦尋覓找工作之間,殊不知今日之時(shí)乃我心之痛,難到是我不配擁有工作嘛。自面試后他所謂的等待都過去一段時(shí)日,可惜在下京東上的小金庫都要見低啦。每每想到不由心中一緊。正處為難之間,手機(jī)忽然來了個(gè)短信預(yù)約后續(xù)面試。 我即刻三下五除二拎包踢門而出。飛奔而去。
此刻面試門外首先映入眼簾的是一個(gè)白色似皮球的東西,似圓非圓。好奇冬瓜落地一般。上半段還有一段濕濕的部分,顯得尤為入目。這是什么情況?
緊接著現(xiàn)身一名中年男子。他身著純白色T桖衫的,一灰色寬松的休閑西褲,腰圍至少得三十好幾。外加一雙夏日必備皮制涼鞋。只見,他正低頭看著手上的一張A4紙。透過一頭黑色短發(fā)。滿臉的贅肉橫生。外加上那大腹便便快要把那T桖衫給撐爆的肚子。
看得我好生害怕,不由得咽了咽口水,生怕自己說錯(cuò)話。這宛如一顆肉粽呀。不在職場(chǎng)摸滾打拼8、9年,也不會(huì)有當(dāng)前這景象。
什么是鎖
面試官:: 你是來參加面試的吧?
吒吒輝: 不 不 不,我是來參加復(fù)試呢。
面試官:: 看到上次別人點(diǎn)評(píng),MySQL優(yōu)化還闊以。那你先談?wù)剬?duì)鎖的理解?
吒吒輝: 嘿嘿,還好!
鎖是計(jì)算機(jī)在進(jìn)行多 進(jìn)程、線程執(zhí)行調(diào)度時(shí)強(qiáng)行限制資源訪問的同步機(jī)制,用于在并發(fā)訪問時(shí)保證數(shù)據(jù)的一致性、有效性;
鎖是在執(zhí)行多線程時(shí),用于強(qiáng)行限制資源訪問的同步機(jī)制,即用在并發(fā)控制中保證對(duì)互斥的要求。
一般的鎖是建議鎖(advisory lock),每個(gè)線程在訪問對(duì)應(yīng)資源前都需獲取鎖的信息,再根據(jù)信息決定是否可以訪問。若訪問對(duì)應(yīng)信息,鎖的狀態(tài)會(huì)改變?yōu)殒i定,因此其它線程此時(shí)不會(huì)來訪問該資源,當(dāng)資源結(jié)束后,會(huì)恢復(fù)鎖的狀態(tài),允許其他線程的訪問。
有些系統(tǒng)有強(qiáng)制鎖(mandatory lock),若有未授權(quán)的線程想要訪問鎖定的數(shù)據(jù),在訪問時(shí)就會(huì)產(chǎn)生異常。
---《維基百科》
鎖的類型和應(yīng)用原理
面試官:: 那一般數(shù)據(jù)庫有哪些鎖? 一般怎么使用?
此刻,用我那呆若木雞的眼神看向面試官,內(nèi)心實(shí)屬尷尬+害怕,數(shù)據(jù)庫不就是共享和互斥鎖嗎?
這樣看來,是我太嫩。此處必有坑。殊不知此刻我內(nèi)心已把你拿捏,定斬不饒。
吒吒輝: 數(shù)據(jù)庫的鎖根據(jù)不同劃分方式有很多種說法,在業(yè)務(wù)訪問上有以下兩種:
- 排他鎖
在訪問共享資源之前對(duì)其進(jìn)行加鎖,在訪問完成后進(jìn)行解鎖操作。 加鎖成功后,任何其它線程請(qǐng)求來獲取鎖都會(huì)被阻塞,直到當(dāng)前線自行釋放鎖。
線程3狀態(tài):就緒、阻塞、執(zhí)行
如解鎖時(shí),有一個(gè)以上的線程阻塞(資源已釋放),那么所有嘗試獲取該鎖的線程都被CPU認(rèn)為就緒狀態(tài), 如果第一個(gè)就緒狀態(tài)的線程又執(zhí)行加鎖操作,那么其他的線程又會(huì)進(jìn)入就緒狀態(tài)。 在這種方式下,只能有一個(gè)線程訪問被互斥鎖保護(hù)的資源
故此,MySQL的SQL語句加了互斥鎖后,只有接受到請(qǐng)求并獲取鎖的線程才能夠訪問和修改數(shù)據(jù)。 因?yàn)榛コ怄i是針對(duì)線程訪問控制而不是請(qǐng)求本身。
- 共享鎖
被加鎖資源是可被共享的,但僅限于讀請(qǐng)求。它的寫請(qǐng)求只能被獲取到鎖的請(qǐng)求獨(dú)占。 也就是加了共享鎖的數(shù)據(jù),只能夠當(dāng)前線程修改,其它線程只能讀數(shù)據(jù),并不能修改。
吒吒輝: 在 SQL 請(qǐng)求上可分為讀、寫鎖。但本質(zhì)還是對(duì)應(yīng)對(duì)共享鎖和排它鎖。
面試官: 那 SQL 請(qǐng)求上不加鎖怎么訪問? 為啥說它們屬于共享鎖和排他鎖? 這之間有何聯(lián)系?
吒吒輝: 除加鎖讀外,還有一種不加鎖讀的情況。這種方式稱為快照讀,讀請(qǐng)求加鎖稱為共享讀。
針對(duì)請(qǐng)求加共享、排它鎖的原因在于,讀請(qǐng)求天生是冪等性的,不論你讀多少次數(shù)據(jù)不會(huì)發(fā)生變化,所以給讀請(qǐng)求加上鎖就應(yīng)該為共享鎖。 不然怎么保證它的特點(diǎn)呢?
而寫請(qǐng)求,本身就需對(duì)數(shù)據(jù)進(jìn)行修改,所以就需要排它鎖來保證數(shù)據(jù)修改的一致性。
吒吒輝: 如果按照鎖的顆粒度劃分看,就有表鎖和行鎖
- 表鎖:
是MySQL中最基本的鎖策略,并且是開銷最小的策略。并發(fā)處理較少。表鎖由MySQL服務(wù)或存儲(chǔ)引擎管理。多數(shù)情況由服務(wù)層管理,具體看SQL操作。
例如:服務(wù)器會(huì)為諸如 ALTER TABLE 之類的語句使用表鎖
,而忽略存儲(chǔ)引擎的鎖。
加鎖機(jī)制:
它會(huì)鎖定整張表。一個(gè)用戶在對(duì)表進(jìn)行寫操作(插人、刪除、更新等)前,需要先獲得寫鎖,這會(huì)阻塞其他用戶對(duì)該表的所有讀寫操作。只有沒有寫鎖時(shí),其他用戶才能獲取到讀鎖。
- 行鎖:
鎖定當(dāng)前訪問行的數(shù)據(jù),并發(fā)處理能力很強(qiáng)。但鎖開銷最大。具體視行數(shù)據(jù)多少?zèng)Q定。由innoDB存儲(chǔ)引擎支持。
- 頁級(jí)鎖:
頁級(jí)鎖是 MySQL 中鎖定粒度介于行級(jí)鎖和表級(jí)鎖中間的一種鎖。表級(jí)鎖速度快,但沖突多,行級(jí)沖突少,但速度慢。因此,采取了折衷的頁級(jí)鎖,一次鎖定相鄰的一組記錄。由BDB 存儲(chǔ)引擎管理頁級(jí)鎖。
面試官: 為啥是表鎖開銷小,而不是行鎖呢? 畢竟表鎖鎖定是整張表
吒吒輝: 表鎖鎖定的是表沒錯(cuò),但它不是把表里面所有的數(shù)據(jù)行都上鎖,相當(dāng)于是封鎖了表的入口,這樣它只是需要判斷每個(gè)請(qǐng)求是否可以獲取到表的鎖,沒有就不鎖定。
而行鎖是針對(duì)表的每一行數(shù)據(jù),數(shù)據(jù)量一多,鎖定內(nèi)容就多,故開銷大。 但因它顆粒度小,鎖定行不會(huì)影響到別的行。所以并發(fā)就高。而如果表鎖在一個(gè)入口就卡死了,那整體請(qǐng)求處理肯定就會(huì)下降。
面試官: 我記得行鎖里面有幾種不同的實(shí)現(xiàn)方式,你知道嗎?
您可真貼心啊,替我考慮這么多,大佬都是這么心比針細(xì)? 我要是說不知道,你老是不是又準(zhǔn)備給出穿小鞋啦。強(qiáng)忍內(nèi)心啃人的沖動(dòng)
ps:讀懂圖,說明你有故事
吒吒輝: innodb雖支持行鎖,但鎖實(shí)現(xiàn)的算法卻和SQL的查詢形式有關(guān)系:
- Record Lock(記錄鎖):?jiǎn)蝹€(gè)行記錄上的鎖。也就是我們?nèi)粘UJ(rèn)為的行鎖。由
`
where =
`
的形式觸發(fā)
- Gap Lock(間隙鎖):間隙鎖,鎖定一個(gè)范圍,但不包括記錄本身(它鎖住了某個(gè)范圍內(nèi)的多個(gè)行,包括根本不存在的數(shù)據(jù))。
GAP鎖的目的,是為了防止事務(wù)插入而導(dǎo)致幻讀的情況。該鎖只會(huì)在隔離級(jí)別是RR或者以上的級(jí)別內(nèi)存在。間隙鎖的目的是為了讓其他事務(wù)無法在間隙中新增數(shù)據(jù)。 SQL里面用 where >、>=等范圍條件觸發(fā),但會(huì)根據(jù)鎖定的范圍內(nèi),是否包含了表中真實(shí)存在的記錄進(jìn)行變化,如果存在真實(shí)記錄就會(huì)進(jìn)化為 臨建鎖。反之就為間隙所。
- Next-Key Lock(臨鍵鎖):它是記錄鎖和間隙鎖的結(jié)合,鎖定一個(gè)范圍,并且鎖定記錄本身。對(duì)于行的查詢,都是采用該方法,主要目的是解決幻讀的問題。next-key 鎖是InnoDB默認(rèn)的。是一個(gè)左開右閉的規(guī)則
- IS鎖:意向共享鎖、Intention Shared Lock。當(dāng)事務(wù)準(zhǔn)備在某條記錄上加S(讀)鎖時(shí),需要先在表級(jí)別加一個(gè)IS鎖。
- IX鎖:意向排它鎖、Intention Exclusive Lock。當(dāng)事務(wù)準(zhǔn)備在某條記錄上加X(寫)鎖時(shí),需要先在表級(jí)別加一個(gè)IX鎖。
面試官: 那這個(gè)東西是怎么實(shí)現(xiàn)的?
t(id PK, name KEY, sex, flag);
表中有四條記錄:
- 1, zhazhahui, m, A
- 3, nezha, m, A
- 5, lisi, m, A
- 9, wangwu, f, B
- 記錄鎖
select * from t where id=1 for update;
鎖定 id =1的記錄
- 間隙鎖
select * from t where id > 3 and id < 9 ;
鎖定(3,5],(5,9)范圍的值,因?yàn)楫?dāng)前訪問3到9的范圍記錄,就需要鎖定表里面已經(jīng)存在的數(shù)據(jù)來解決幻讀和不可重復(fù)讀的問題
- 臨建鎖
select * from t where id >=9 ;
會(huì)鎖定 [9,+∞) 。查詢會(huì)先選中 9 號(hào)記錄,所以鎖定范圍就以9開始到正無窮數(shù)據(jù)。
面試官: 那意向排它、共享鎖呢?是怎么個(gè)內(nèi)容
吒吒輝: 意向排它鎖和意向共享鎖,是針對(duì)當(dāng)前SQL請(qǐng)求訪問數(shù)據(jù)行時(shí),會(huì)提前進(jìn)行申請(qǐng)?jiān)L問,如果最終行鎖未命中就會(huì)退化為該類型的表鎖。
面試官: 那有這個(gè)意向排它鎖有什么好處呢?
吒吒輝: 可提前做預(yù)判,每次嘗試獲取行鎖之前會(huì)檢查是否有表鎖,如果存在就不會(huì)繼續(xù)申請(qǐng)行鎖,從而減少鎖的開銷。從而整個(gè)表就退化為表鎖。
面試官: 那你動(dòng)手給我演示下每個(gè)場(chǎng)景
嗯。。。(瞳孔放大2倍)我這不說的很明白嗎?
難道故意和作對(duì),這是干嘛啊。欺負(fù)人嘛不是
只見那面試官忽然翹起來二郎腿,還有節(jié)拍的抖動(dòng)著腿,看向我。一看就是抖音整多了
哎,沒辦法 官大以及壓死人。打碎了牙齒自己咽。你給我看細(xì)細(xì)看好了,最好眼睛都別眨
吒吒輝: 因?yàn)殒i就是解決事務(wù)并發(fā)的問題,所以記錄鎖就不演示了,直接游蕩在間隙和臨建鎖里面。
建立語句:
- CREATE TABLE `t1` (
- `id` int(10) NOT NULL AUTO_INCREMENT,
- `name` varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
- `age` tinyint(3) unsigned DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
表數(shù)據(jù):
間隙鎖:
- 關(guān)閉 MySQL 默認(rèn)的事務(wù)自動(dòng)提交機(jī)制。
- 關(guān)閉前:
- 關(guān)閉后:
加鎖:
直接插入 >8 的數(shù)據(jù)就阻塞,都會(huì)上鎖。為的就解決插入新數(shù)據(jù)而導(dǎo)致幻讀。
【??!幻讀不知道呀。下篇文章給大家安排上】
面試官: 你這條件不是>=8嗎? 那等于8呢? 被吃辣?
吒吒輝: 別著急嘛,這不還沒說完嗎。為什么不指定8呢?
因?yàn)?>=8 的條件會(huì)從間隙鎖升級(jí)為臨建鎖,因?yàn)槟銞l件里面包含了 8 這個(gè)真實(shí)存在的數(shù)據(jù)。所以會(huì)把它鎖起來。如下:
所以,最終的行鎖會(huì)和SQL語句的條件觸發(fā)有關(guān)系,一旦范圍查詢包含了數(shù)據(jù)庫里面真實(shí)存在數(shù)據(jù),就會(huì)升級(jí)為臨建鎖。不要問我為什么? 看前面的定義
面試官獨(dú)白:這小伙多少看來還有有點(diǎn)貨,不錯(cuò)。此刻面試官露出一絲笑容。殊不知他內(nèi)心又開醞釀起了新的想法。就等我入甕
面試官: 那什么場(chǎng)景下行鎖不會(huì)生效呢?鎖 鎖定的又是什么?
此刻,我呆了,這都什么跟什么啊。不帶這么玩的吧。天殺的,凈使壞
鎖的觸發(fā)機(jī)制
吒吒輝:
innodb的行鎖是根據(jù)索引觸發(fā),如果沒有相關(guān)的索引,那行鎖將會(huì)退化成表鎖(即鎖定整個(gè)表里的行)。
而 鎖 鎖定的是索引即索引樹里面的數(shù)據(jù)庫字段的值。
- id為主鍵索引字段。
- 給 age 字段上鎖
- age 字段沒索引,退化成表鎖。直接查詢將失敗。
有索引,用索引字段查詢可得數(shù)據(jù),其余字段查詢將失敗。因?yàn)楂@取不到行鎖,只能等待。而鎖定的是索引,故此其它用其它索引值查詢能拿查詢數(shù)據(jù)
- 索引字段上鎖
- 索引當(dāng)前字段鎖定,用其余索引字段可查詢
- 不是索引字段都差不到。
面試官: 你前面說到的鎖可以解決事務(wù)并發(fā),然而MVCC也是用于解決并發(fā),那干嘛還用鎖來呢?你給說說
吒吒輝: 通過MVCC可以解決臟讀、不可重復(fù)讀、幻讀這些讀一致性問題,但實(shí)際上這只是解決了普通select語句的數(shù)據(jù)讀取問題。
事務(wù)利用MVCC進(jìn)行的讀取操作稱之為快照讀,所有普通的SELECT語句在READ COMMITTED、REPEATABLE READ隔離級(jí)別下都算是快照讀。
除了快照讀之外,還有一種是鎖定讀,即在讀取的時(shí)候給記錄加鎖,在鎖定讀的情況下依然要解決臟讀、不可重復(fù)讀、幻讀的問題。
比如:如果 1 4 7 9 的數(shù)據(jù)。如果條件為 where > 4 的,那如果不鎖定到 (4,7] (7,9],(9,+∞)。那勢(shì)必就會(huì)早幻讀,不可重復(fù)讀的問題。
ps:不重復(fù)讀?臟讀是如何產(chǎn)生的?
死鎖
面試官: 那你說下數(shù)據(jù)庫的死鎖是個(gè)什么情況?
吒吒輝: 死鎖是指兩個(gè)或多個(gè)事務(wù)在同一資源上相互占用,并請(qǐng)求鎖定對(duì)方占用的資源,從而導(dǎo)致惡性循環(huán)。
當(dāng)事務(wù)試圖以不同的順序鎖定資源時(shí),就可能產(chǎn)生死鎖。多個(gè)事務(wù)同時(shí)鎖定同一個(gè)資源時(shí)也可能會(huì)產(chǎn)生死鎖。
一般可通過死鎖檢測(cè)和死鎖超時(shí)機(jī)制來解決該問題。
死鎖檢查:
像InnoDB存儲(chǔ)引擎,就能檢測(cè)到死鎖的循環(huán)依賴,并立即返回一個(gè)錯(cuò)誤。否則死鎖會(huì)導(dǎo)致出現(xiàn)非常慢的查詢。通過參數(shù) innodb_deadlock_detect 設(shè)置為on,來開啟。
超時(shí)機(jī)制:
就是當(dāng)查詢的時(shí)間達(dá)到鎖等待超時(shí)的設(shè)定后放棄鎖請(qǐng)求。InnoDB目前處理死鎖的方法是,將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾(這是相對(duì)比較簡(jiǎn)單的死鎖回滾算法)。
可通過配置參數(shù) innodb_lock_wait_timeout 用來設(shè)置超時(shí)時(shí)間。如果有些用戶使用哪種大事務(wù),就設(shè)置鎖超時(shí)時(shí)間大于事務(wù)執(zhí)行時(shí)間。
但這種情況下死鎖超時(shí)檢查的發(fā)現(xiàn)時(shí)間是無法接受的。
面試官: 那你說說InnoDB和MyisAM是如何發(fā)現(xiàn)死鎖的?
吒吒輝:
- innodb
數(shù)據(jù)庫會(huì)把事務(wù)單元鎖維持的鎖和它所等待的鎖都記錄下來,Innodb提供了wait-for graph算法來主動(dòng)進(jìn)行死鎖檢測(cè),每當(dāng)加鎖請(qǐng)求無法立即滿足需要進(jìn)入等待時(shí),wait-for graph算法都會(huì)被觸發(fā)。當(dāng)數(shù)據(jù)庫檢測(cè)到兩個(gè)事務(wù)不同方向地給同一個(gè)資源加鎖(產(chǎn)生循序),它就認(rèn)為發(fā)生了死鎖,觸發(fā)wait-for graph算法。
比如:事務(wù)1給A加鎖,事務(wù)2給B加鎖,同時(shí)事務(wù)1給B加鎖(等待),事務(wù)2給A加鎖就發(fā)生了死鎖。那么死鎖解決辦法就是終止一邊事務(wù)的執(zhí)行即可,這種效率一般來說是最高的,也是主流數(shù)據(jù)庫采用的辦法。
Innodb目前處理死鎖的方法就是將持有最少行級(jí)排他鎖的事務(wù)進(jìn)行回滾。這是相對(duì)比較簡(jiǎn)單的死鎖回滾方式。死鎖發(fā)生以后,只有部分或者完全回滾其中一個(gè)事務(wù),才能打破死鎖。
對(duì)于事務(wù)型的系統(tǒng),這是無法避免的,所以應(yīng)用程序在設(shè)計(jì)必須考慮如何處理死鎖。大多數(shù)情況下只需要重新執(zhí)行因死鎖回滾的事務(wù)即可。
- MyisAM
MyisAM自身只支持表級(jí)鎖,故加鎖后一次性獲取的。所以資源上不會(huì)出現(xiàn)多個(gè)事務(wù)之間互相需要對(duì)方釋放鎖之后再來進(jìn)行處理。故不會(huì)有死鎖
面試官: wait-for graph 算法怎么理解?
吒吒輝: 如下所示,四輛車就是死鎖
它們相互等待對(duì)方的資源,而且形成環(huán)路!每輛車可看為一個(gè)節(jié)點(diǎn),當(dāng)節(jié)點(diǎn)1需要等待節(jié)點(diǎn)2的資源時(shí),就生成一條有向邊指向節(jié)點(diǎn)2,最后形成一個(gè)有向圖。我們只要檢測(cè)這個(gè)有向圖是否出現(xiàn)環(huán)路即可,出現(xiàn)環(huán)路就是死鎖!這就是wait-for graph算法。
Innodb將各個(gè)事務(wù)看為一個(gè)個(gè)節(jié)點(diǎn),資源就是各個(gè)事務(wù)占用的鎖,當(dāng)事務(wù)1需要等待事務(wù)2的鎖時(shí),就生成一條有向邊從1指向2,最后行成一個(gè)有向圖。
面試官: 既然死鎖無法避免,那如何減少發(fā)生呢?
吒吒輝:
- 對(duì)應(yīng)用程序進(jìn)行調(diào)整/修改。某些情況下,你可以通過把大事務(wù)分解成多個(gè)小事務(wù),使得鎖能夠更快被釋放,從而極大程度地降低死鎖發(fā)生的頻率。在其他情況下,死鎖的發(fā)生是因?yàn)閮蓚€(gè)事務(wù)采用不同的順序操作了一個(gè)或多個(gè)表的相同的數(shù)據(jù)集。需要改成以相同順序讀寫這些數(shù)據(jù)集,換言之,就是對(duì)這些數(shù)據(jù)集的訪問采用串行化方式。這樣在并發(fā)事務(wù)時(shí),就讓死鎖變成了鎖等待。
- 修改表的schema,例如:刪除外鍵約束來分離兩張表,或者添加索引來減少掃描和鎖定的行。
- 如果發(fā)生了間隙鎖,你可以把會(huì)話或者事務(wù)的事務(wù)隔離級(jí)別更改為RC(read committed)級(jí)別來避免,可以避免掉很多因?yàn)間ap鎖造成的死鎖,但此時(shí)需要把binlog_format設(shè)置成row或者mixed格式。
- 為表添加合理的索引,不走索引將會(huì)為表的每一行記錄添加上鎖(等同表鎖),死鎖的概率大大增大。
- 為了在單個(gè)InnoDB 表上執(zhí)行多個(gè)并發(fā)寫入操作時(shí)避免死鎖,可以在事務(wù)開始時(shí)通過為預(yù)期要修改的每個(gè)元祖(行)使用SELECT ... FOR UPDATE語句來獲取必要的鎖,即使這些行的更改語句是在之后才執(zhí)行的。
- 通過SELECT ... LOCK IN SHARE MODE獲取行的讀鎖后,如果當(dāng)前事務(wù)再需要對(duì)該記錄進(jìn)行更新操作,則很有可能造成死鎖。因進(jìn)行獲鎖讀取在修改
這時(shí),只見對(duì)面所坐面試官,捋了捋那沒有毛發(fā)的下巴,故作深思熟慮,像是在端詳這什么。 難道 難道 是讓我通過了嗎?
此刻內(nèi)心猶如小鹿亂撞,吶喊到我要干它二量。真的是不容易。 就在此時(shí),他起身而立,那白色T桖衫包裹著那甩大肚子,猶如波浪上下翻滾。一看就是沒少在酒桌上擼肉。
只見開口到,小伙子不錯(cuò)啊。
這是肯定我嗎? 不容易啊,今天不開幾把LOL,難消我心頭之恨
面試官: 其實(shí)這數(shù)據(jù)庫嘛 ,內(nèi)容還是有很多的,你回去準(zhǔn)備下,下一次的面試吧
。。。。什么個(gè)玩意兒,下次? 那就是這次不行啦, 這還沒考?jí)虬。掳捅緛頉]毛,你捋個(gè)什么勁兒,整得個(gè)神神忽忽的。 此時(shí)內(nèi)心猶如翻江倒海,猛龍過江。白鶴亮翅的沖動(dòng)打他,奈何我這小身板子不行
吒吒輝: 那行吧,下次是多久啊,我這好多天都沒整頓好的啦,你給我個(gè)準(zhǔn)信唄。
我用那水汪汪可憐的小眼神望向他說到。他卻很斯文的笑著,說道
面試官: 快了,小伙子別著急,我看好你的,加油
我加你那擼啊絲壓榨花生油。 面?zhèn)€試,還嫌我臉上出油出的不多,都是被你擠出來的。只有強(qiáng)忍住內(nèi)心的沖動(dòng)。 哎 官大一級(jí)壓死人啊
吒吒輝: 行吧,那我走啦
此刻,露出我那灰溜溜的背影,猶如魯迅先生筆下的孔乙己