阿里工程师是如何系统化地总结缓存相关知识的
隨著互聯(lián)網(wǎng)業(yè)務(wù)的快速迭代以及用戶量激增,應(yīng)用架構(gòu)需要不斷調(diào)整甚至重構(gòu)以適應(yīng)這種業(yè)務(wù)的快速發(fā)展。當(dāng)數(shù)據(jù)量迅速增長,業(yè)務(wù)邏輯越復(fù)雜,服務(wù)鏈路不斷增加等等一系列問題,會導(dǎo)致RT過長,服務(wù)性能需要逐漸提升以滿足更優(yōu)的用戶體驗(yàn)。在優(yōu)化系統(tǒng)架構(gòu)時通常的所用的兩種方式scale up以及scale out,scale out就是通常所說的水平擴(kuò)展,將應(yīng)用服務(wù)設(shè)計(jì)成無狀態(tài)性,可以方便水平擴(kuò)展通過增加硬件的方式分解訪問壓力。而scale up則是將單個服務(wù)鏈路性能提升,以提升QPS以及系統(tǒng)的吞吐量。在追求更優(yōu)的性能時,大多數(shù)業(yè)務(wù)場景是讀多寫少的情況,一般會通過引入緩存的方式解決。
1. What——什么是緩存?
關(guān)于緩存的定義,在wiki中為:
a collection of data duplicating original values stored elsewhere on a computer, usually for easier access.
簡單理解就是保存在計(jì)算機(jī)設(shè)備中的一個數(shù)據(jù)副本,以便于后續(xù)能夠進(jìn)行快速訪問。
從定義上可以看出所謂緩存一定是針對已有數(shù)據(jù)的一個副本存在,也可以看出緩存的使用是為了解決快速訪問數(shù)據(jù)(讀數(shù)據(jù))的場景。在現(xiàn)有的互聯(lián)網(wǎng)應(yīng)用中,緩存的使用是一種能夠提升服務(wù)快速響應(yīng)的關(guān)鍵技術(shù),也是產(chǎn)品經(jīng)理無暇顧及的非功能需求,需要在設(shè)計(jì)技術(shù)方案時對業(yè)務(wù)場景,具有一定的前瞻性評估后,決定在技術(shù)架構(gòu)中是否需要引入緩存解決這種這種非功能需求。
緩存在計(jì)算機(jī)領(lǐng)域中實(shí)際案例存在很多,比如CPU的緩存是為了解決CPU的運(yùn)算速度和內(nèi)存的讀取數(shù)據(jù)不平衡的問題,CPU的運(yùn)算速度遠(yuǎn)快與內(nèi)存的讀寫速度,為了降低CPU等待數(shù)據(jù)讀寫的時間,在CPU中引入L1/L2/L3多級緩存。
再比如Linux中的文件緩存,實(shí)際上我們在編程時,會談?wù)摰綌?shù)據(jù)的內(nèi)存地址,但是我們接觸的都是虛擬地址而不是真實(shí)的物理地址,計(jì)算機(jī)中的內(nèi)存管理單元(MMU)和頁表會將虛擬地址轉(zhuǎn)換成物理地址。在計(jì)算機(jī)硬件領(lǐng)域中就已有很多關(guān)于緩存的應(yīng)用案例,實(shí)際上在軟件架構(gòu)中關(guān)于緩存的設(shè)計(jì)會借鑒于很多傳統(tǒng)且成熟的計(jì)算機(jī)硬件緩存設(shè)計(jì)的思想。
2. Why——為什么需要使用緩存?
軟件服務(wù)能夠得到用戶的信賴,并將產(chǎn)品的價值帶給用戶,能夠解決目標(biāo)用戶的痛點(diǎn)問題這是決定用戶會不會一開始決定使用,也就是《增長黑客》中提到了產(chǎn)品能夠帶來給用戶“啊哈時刻”,而決定用戶會不會高頻使用以及持續(xù)使用,用戶體驗(yàn)則是被認(rèn)為是軟件產(chǎn)品提升用戶黏性的關(guān)鍵影響因素。
2.1 什么是用戶體驗(yàn)
用戶體驗(yàn)被專業(yè)定義和推廣需要推廣到20世紀(jì)90年代,由Donald Norman布道推廣。用戶體驗(yàn)在人機(jī)交互領(lǐng)域上受到了重視,并一度和傳統(tǒng)的三大可用性指標(biāo)(即效率、效益以及基本滿意度)不相上下。
ISO 9241-210標(biāo)準(zhǔn)將用戶體驗(yàn)官方定義為:人們對正在使用或者期待使用的產(chǎn)品、系統(tǒng)或者服務(wù)的認(rèn)知印象和回應(yīng)。可以看出用戶體驗(yàn)是用戶的對軟件產(chǎn)品的主觀感受,具體包含了用戶在使用之前、使用中以及使用后的情感、喜好、認(rèn)知印象、心理反應(yīng)以及情緒表達(dá)等等多種主觀感受,每個用戶對產(chǎn)品的主觀感受的視角不同,關(guān)注點(diǎn)不同,也就導(dǎo)致軟件產(chǎn)品讓大多數(shù)用戶都能夠獲得很好的用戶體驗(yàn)本身就是一件很有挑戰(zhàn)性的事情。
在業(yè)界大多數(shù),將用戶體驗(yàn)分為三類:使用者狀態(tài)、軟件產(chǎn)品的系統(tǒng)性能以及環(huán)境,使用者狀態(tài)以及環(huán)境(使用者環(huán)境以及產(chǎn)品在同類產(chǎn)品中的產(chǎn)品大環(huán)境)這兩個因素需要交互設(shè)計(jì)和用研等多個專業(yè)領(lǐng)域同學(xué)去攻克,軟件開發(fā)者則需要解決系統(tǒng)性能的問題。對用戶而言,最基本的需求就是在使用軟件服務(wù)時,軟件產(chǎn)品提供服務(wù)內(nèi)容的及時性,也就是通常所說的在使用過程中持續(xù)的Loading(轉(zhuǎn)菊花)一定會導(dǎo)致用戶體驗(yàn)很差,內(nèi)容的及時性也是系統(tǒng)性能的最低要求。
而系統(tǒng)性能的問題,是產(chǎn)品經(jīng)理無暇顧及的點(diǎn),也是非功能性需求,需要開發(fā)者去花心思去思考的地方。評估系統(tǒng)性能的指標(biāo)有很多,在以提升用戶體驗(yàn)為前提的情況下,我們需要著重關(guān)注的性能指標(biāo)有哪些呢?
2.2 常見的性能指標(biāo)
在設(shè)計(jì)軟件架構(gòu)時需要關(guān)注的幾個常見指標(biāo):響應(yīng)時間、延遲時間、吞吐量、并發(fā)用戶數(shù)和資源利用率。
1)系統(tǒng)響應(yīng)時間 :響應(yīng)時間是指系統(tǒng)對用戶請求做出響應(yīng)的時間,不同的功能的鏈路長短不同,并且同一功能在不同數(shù)據(jù)量等這些情況都會導(dǎo)致響應(yīng)時間的不同。因此,在衡量系統(tǒng)響應(yīng)時間時,通常會關(guān)注軟件產(chǎn)品所有功能的平均響應(yīng)時間以及最大響應(yīng)時間。
2)延遲時間 :在討論系統(tǒng)響應(yīng)時間時,更細(xì)粒度的劃分可以劃分為:
客戶端在接受數(shù)據(jù)進(jìn)行渲染的內(nèi)容“呈現(xiàn)時間”;
服務(wù)端在接受用戶請求發(fā)送至服務(wù)端以及服務(wù)端將數(shù)據(jù)返回到客戶端這兩個過程中涉及到的:網(wǎng)絡(luò)傳輸時間以及應(yīng)用延遲時間。應(yīng)用延遲時間即是服務(wù)端在執(zhí)行整個服務(wù)鏈路時所花費(fèi)的時間,也是性能優(yōu)化首要降低的就是這個時間。
3)吞吐量 :吞吐量指的是單位時間內(nèi)能夠處理請求的數(shù)量,對于無并發(fā)的應(yīng)用來說,吞吐量和請求響應(yīng)時間成反比,服務(wù)延遲更長則系統(tǒng)吞吐量更低。
4)并發(fā)用戶數(shù) :并發(fā)用戶數(shù)指的是系統(tǒng)能夠同時承載正常使用系統(tǒng)功能的用戶數(shù),相較于吞吐量,這個指標(biāo)更為籠統(tǒng)但是對于非軟件領(lǐng)域的人來說更容易理解。
5)資源利用率 :資源利用率反映的是在一段時間內(nèi)資源被占用的情況。
2.3 緩存帶來的優(yōu)勢
在追求更優(yōu)的優(yōu)化體驗(yàn)時,客觀的來說需要不斷提升以上這些性能指標(biāo),不斷逼近系統(tǒng)體驗(yàn)的最優(yōu)解。緩存到底具有什么樣的優(yōu)勢,值得我們花費(fèi)很大的精力去設(shè)計(jì)一套能很好的適應(yīng)現(xiàn)在的業(yè)務(wù)場景的緩存結(jié)構(gòu)呢?
1)極大的提升軟件用戶體驗(yàn)?
軟件產(chǎn)品主要圍繞兩個核心問題,一是解決目標(biāo)用戶的痛點(diǎn)問題,二是提升產(chǎn)品黏性。在提供軟件服務(wù)時,抽象的來看是解決數(shù)據(jù)在整個鏈路上的流轉(zhuǎn)問題,如何讓數(shù)據(jù)流轉(zhuǎn)更加高效、更加順暢是在實(shí)現(xiàn)時著重關(guān)注的地方,事實(shí)上,無論是瀏覽器、負(fù)載均衡、應(yīng)用服務(wù)器還是數(shù)據(jù)庫等等各個環(huán)節(jié)都會應(yīng)用到緩存,當(dāng)數(shù)據(jù)離用戶“更近”,比如數(shù)據(jù)副本在客戶端上,也就意味著請求能夠很快的進(jìn)行響應(yīng),相應(yīng)的給用戶進(jìn)行數(shù)據(jù)呈現(xiàn)的耗時就更短。現(xiàn)如今用戶爸爸們“日理萬機(jī)”,如果一個軟件產(chǎn)品不能在很短時間就獲取用戶的注意力,很大可能性就意味著失敗。因此,使用緩存能夠讓用戶從主觀上獲取更優(yōu)的用戶體驗(yàn)。
2)提升吞吐量?
試想,如果在服務(wù)鏈路上,請求能夠在緩存中獲取服務(wù)數(shù)據(jù)的話,也就意味著很多數(shù)據(jù)并不需要從源應(yīng)用服務(wù)器進(jìn)行獲取,降低了源服務(wù)器網(wǎng)絡(luò)傳輸?shù)念l率,在一定IDC帶寬下,系統(tǒng)能夠降低網(wǎng)絡(luò)傳輸時間以及應(yīng)用延遲時間,從而支撐更多的系統(tǒng)訪問以提升系統(tǒng)整體吞吐量以及并發(fā)用戶數(shù),硬件的使用效率也會明顯提升。
從實(shí)際場景下,在系統(tǒng)性能優(yōu)化時大概率會優(yōu)先選擇使用緩存進(jìn)行系統(tǒng)優(yōu)化,也是一種被證明有效的手段,緩存也被認(rèn)為是一種“空間換時間”的藝術(shù)。
3. Where——緩存存在鏈路中的哪些地方?
3.1 緩存分類
從一個請求到最終獲取響應(yīng),會經(jīng)過很多環(huán)節(jié),緩存可以幾乎存在整個鏈路的每個節(jié)點(diǎn)。緩存按照不同的維度可以有如下分類:
1)緩存所處鏈路節(jié)點(diǎn)的位置:
客戶端緩存
網(wǎng)絡(luò)緩存
服務(wù)端緩存
2)緩存架構(gòu)部署方式:
單機(jī)緩存
緩存集群
分布式緩存
3)緩存的內(nèi)存區(qū)域
本地緩存/進(jìn)程內(nèi)緩存
進(jìn)程間緩存
遠(yuǎn)程緩存
按照緩存在服務(wù)鏈路上的位置來劃分,可以系統(tǒng)性的梳理下緩存的不同應(yīng)用。
3.2 客戶端緩存
客戶端緩存是離用戶“最近”的一種存儲介質(zhì),經(jīng)常和網(wǎng)絡(luò)測和服務(wù)端緩存一起配合使用,常見的客戶端緩存有如下幾種:
1)頁面緩存:頁面緩存是指將靜態(tài)頁面獲取頁面中的部分元素緩存到本地,以便下次請求不需要重復(fù)資源文件,h5很好的支持的離線緩存的功能,具體實(shí)現(xiàn)可通過頁面指定manifest文件,當(dāng)瀏覽器訪問一個帶有manifest屬性的文件時,會先從應(yīng)用緩存中獲取加載頁面的資源文件,并通過檢查機(jī)制處理緩存更新的問題。
2)瀏覽器緩存:瀏覽器緩存通常會專門開辟內(nèi)存空間以存儲資源副本,當(dāng)用戶后退或者返回上一步操作時可以通過瀏覽器緩存快速的獲取數(shù)據(jù),在HTTP 1.1中通過引入e-tag標(biāo)簽并結(jié)合expire、cache-control兩個特性能夠很好的支持瀏覽器緩存,關(guān)于瀏覽器緩存更為細(xì)節(jié)的知識可以查看該文章。
3)APP緩存:APP可以將內(nèi)容緩存到內(nèi)存或者本地?cái)?shù)據(jù)庫中,例如在一些開源的圖片庫中都具備緩存的技術(shù)特性,當(dāng)圖片等資源文件從遠(yuǎn)程服務(wù)器獲取后會進(jìn)行緩存,以便下一次不再進(jìn)行重復(fù)請求,并可以減少用戶的流量費(fèi)用。
客戶端緩存是前端性能優(yōu)化的一個重要方向,畢竟客戶端是距離“用戶”最近的地方,是一個可以充分挖掘優(yōu)化潛力的地方。
3.3 網(wǎng)絡(luò)緩存
網(wǎng)絡(luò)緩存位于客戶端以及服務(wù)端中間,通過通過代理的方式解決數(shù)據(jù)請求的響應(yīng),降低數(shù)據(jù)請求的回源率。通常具有如下幾種形式的網(wǎng)路緩存:
1)web代理緩存:常見的代理形式分為分為:正向代理、反向代理以及透明代理。web代理緩存通常是指正向代理,會將資源文件和熱點(diǎn)數(shù)據(jù)放在代理服務(wù)器上,當(dāng)新的請求到來時,如果在代理服務(wù)器上能獲取數(shù)據(jù),則不需要重復(fù)請求到應(yīng)用服務(wù)器上;
2)邊緣緩存:和正向代理一樣,反向代理同樣可以用于緩存,例如nginx就提供了緩存的功能。進(jìn)一步,如果這些反向代理服務(wù)器能夠做到和用戶請求來自同一個網(wǎng)絡(luò),那么獲取資源的速度進(jìn)一步提升,這類的反向代理服務(wù)器可以稱之為邊緣緩存。常見的邊緣緩存就是CDN(Content Delivery Network),可以將圖片等靜態(tài)資源文件放到CDN上。
3.4 服務(wù)端緩存
服務(wù)端緩存是后端開發(fā)中進(jìn)行性能優(yōu)化的發(fā)力點(diǎn),常見的后端性能優(yōu)化也是通過引入緩存來進(jìn)行解決,常見的有數(shù)據(jù)庫的查詢緩存、緩存框架以及引入應(yīng)用級緩存。
★? 3.4.1 數(shù)據(jù)庫查詢緩存
例如,MySQL的緩存機(jī)制是通過將SELECT語句以及相應(yīng)的ResultSet進(jìn)行緩存,當(dāng)后續(xù)接受到SELECT請求后,如果MySQL已經(jīng)開啟了Query Cache功能,會將SELECT語句以字符串的方式進(jìn)行hash,然后去從緩存中進(jìn)行查詢,如果查詢出數(shù)據(jù),則直接進(jìn)行返回,省去了后續(xù)的優(yōu)化器以及存儲引擎IO的操作,能夠極大的提升響應(yīng)時效。如何優(yōu)化Query Cache需要從如下幾個指標(biāo)上進(jìn)行考慮:
query_cache_size:設(shè)置能夠緩存ResultSet的內(nèi)存區(qū)域大小
query_cache_type:表示使用緩存的場景。0表示任何場景下都不使用Query Cache,1表示顯式指定不使用Query Cache的查詢都可以使用,2(DEMAND)表示只有明確指示使用Query Cache才會生效;
Qcache hits:表示多少次查詢命中Query Cache
Qcache inserts:表示多少次沒有命中Query Cache而插入數(shù)據(jù)
Qcahce lowmem prunes:表示多少條Query引入空間不足而被清除
Qcache free memory:表示剩余內(nèi)存大小
Qcache free blocks:該值很大表示內(nèi)存碎片很多,需要及時清理
在進(jìn)行Qcache優(yōu)化時,可以對以上指標(biāo)綜合進(jìn)行分析,比如了解Qcache的緩存命中率 = Qcache hits/ Qcache hits + Qcache inserts,來判斷當(dāng)前Qcache的效率。也可以結(jié)合Qcahce lowmem prunes、Qcache free memory以及Qcache free blocks來判斷當(dāng)前Qcache的內(nèi)存使用效率。
另外,如果使用Innodb存儲引擎的話,也需要著重關(guān)注innodb_buffer_pool_size參數(shù),該參數(shù)決定了innodb的索引以及數(shù)據(jù)是否有足夠大的空間放入到緩存中。table_cache決定了能夠緩存表的最大數(shù)量,也是需要關(guān)注的一個參數(shù)。
★? 3.4.2 緩存框架
在功能開發(fā)時,會常用提供緩存特性的緩存框架或者實(shí)現(xiàn)緩存功能的類庫來高效的完成開發(fā),常見的緩存框架有Ehcache、Guava等,這些緩存框架配置簡單,能夠簡單靈活的使用。這些開源的緩存框架不僅支持單機(jī)的本地緩存還能配置集群的方式達(dá)到靈活伸縮。
★? 3.4.3 應(yīng)用級緩存
當(dāng)緩存框架不能滿足需求的時候,就需要引入應(yīng)用級緩存,比如Redis、MongoDB等NoSQL數(shù)據(jù)庫,應(yīng)用級緩存具備高可用性以及伸縮性的分布式架構(gòu)能夠支撐業(yè)務(wù)需求,當(dāng)然,做好一款應(yīng)用級緩存產(chǎn)品其中的挑戰(zhàn)也是巨大。
4. When——什么時候需要使用緩存?
緩存不是架構(gòu)設(shè)計(jì)的必選項(xiàng),也不是業(yè)務(wù)開發(fā)中的必要功能點(diǎn),只有在業(yè)務(wù)出現(xiàn)性能瓶頸,進(jìn)行優(yōu)化性能的時候才需要考慮使用緩存來提升系統(tǒng)性能。也不是所有的業(yè)務(wù)場景都適合使用緩存,讀多寫少且數(shù)據(jù)時效要求越低的場景越適合使用緩存,緩存并不是所有性能問題的靈丹妙藥,如果濫用緩存反而會成為毒藥,并且會引入維護(hù)緩存的操作成本,使得系統(tǒng)復(fù)雜度更高不利于維護(hù)。
另外把緩存當(dāng)做存儲來使用是一件極其致命的做法,這種錯誤的認(rèn)識,將緩存引入系統(tǒng)的那一刻起就意味著已經(jīng)讓系統(tǒng)走上了危險的局面,對緩存的使用邊界要有深刻的理解,才能盡可能保證做出引入緩存才是一個正確的決定。
在進(jìn)行緩存結(jié)構(gòu)設(shè)計(jì)的時候,需要考慮的點(diǎn)有很多:
1)業(yè)務(wù)流量量級以及應(yīng)用規(guī)模:對于低并發(fā)低流量的應(yīng)用而言,引入緩存并不會帶來性能的顯著提升,反而會帶來應(yīng)用的復(fù)雜度以及極高的運(yùn)維成本。也不是任何數(shù)據(jù)都需要使用緩存,比如圖片視頻等文件使用分布式文件系統(tǒng)更合適而不是緩存。因此,在引入緩存前,需要對當(dāng)前業(yè)務(wù)的流量進(jìn)行評估,在高并發(fā)大流量的業(yè)務(wù)場景中引入緩存相對而言收益會更高;
2)緩存應(yīng)用的選擇:緩存應(yīng)用有很多如Redis、Memcached以及tair等等,針對每一種分布式緩存應(yīng)用的優(yōu)缺點(diǎn)以及適用范圍、內(nèi)存效率、運(yùn)維成本甚至團(tuán)隊(duì)開發(fā)人員的知識結(jié)構(gòu)都需要了解,才能做好技術(shù)選型;
3)緩存影響因素的正確評估:在引入緩存前,需要著重評估value大小、緩存內(nèi)存空間、峰值QPS、過期時間、緩存命中率、讀寫更新策略、key值分布路由策略、過期策略以及數(shù)據(jù)一致性方案等等多個因素,要做到心中有數(shù);
4)緩存高可用架構(gòu):分布式緩存要高可用,這也是分布式系統(tǒng)追求的三高指標(biāo)中的一個,緩存的集群設(shè)計(jì),主從同步方案的設(shè)計(jì)等等,只有緩存足夠可靠,才能服務(wù)于業(yè)務(wù)系統(tǒng),為業(yè)務(wù)帶來價值;
5)完善的監(jiān)控平臺:當(dāng)緩存投入生產(chǎn)環(huán)境后,需要有一套監(jiān)控系統(tǒng)能夠顯式的觀測緩存系統(tǒng)的運(yùn)行情況,才能更早的發(fā)現(xiàn)問題,同時對于預(yù)估不足的非預(yù)期熱點(diǎn)數(shù)據(jù),也需要熱點(diǎn)發(fā)現(xiàn)系統(tǒng)去解決非預(yù)期的熱點(diǎn)數(shù)據(jù)緩存問題。
6)緩存最近原則:將緩存數(shù)據(jù)放在離用戶最近的地方,無疑會極大的提升響應(yīng)的速度,這也是多級緩存設(shè)計(jì)的核心思想。
6.參考資料
《深入分布式緩存》
https://book.douban.com/subject/27602483/?spm=ata.13261165.0.0.1fbe62a58KTCCF
緩存穿透、緩存擊穿以及緩存雪崩問題探討:
1.https://www.cnblogs.com/Leo_wl/p/9062029.html?spm=ata.13261165.0.0.1fbe62a58KTCCF
2.https://blog.csdn.net/zeb_perfect/article/details/54135506?spm=ata.13261165.0.0.1fbe62a58KTCCF
3.https://www.cnblogs.com/Leo_wl/p/9062029.html?spm=ata.13261165.0.0.1fbe62a58KTCCF#_label0_0
緩存更新策略:
1.https://www.jianshu.com/p/8950c52ce53b?spm=ata.13261165.0.0.3d1462a5BuyJzs
2.https://www.jianshu.com/p/22c7e9ab5d15?spm=ata.13261165.0.0.3d1462a5BuyJzs
3.https://www.cnblogs.com/rjzheng/p/9041659.html?spm=ata.13261165.0.0.3d1462a5BuyJzs
4.https://www.jianshu.com/p/8950c52ce53b?spm=ata.13261165.0.0.3d1462a5BuyJzs
數(shù)據(jù)最終一致性:
1.https://blog.kido.site/2018/11/24/db-and-cache-preface/?spm=ata.13261165.0.0.3d1462a5BuyJzs
2.https://msd.misuland.com/pd/3255817997595443436?spm=ata.13261165.0.0.3d1462a5BuyJzs
3.https://blog.51cto.com/14214194/2411931?spm=ata.13261165.0.0.3d1462a5BuyJzs
4.https://www.cnblogs.com/pomer-huang/p/8998623.html?spm=ata.13261165.0.0.3d1462a5BuyJzs
緩存設(shè)計(jì):
1.https://blog.csdn.net/zjttlance/article/details/80234341?spm=ata.13261165.0.0.3d1462a5BuyJzs
2.https://stor.51cto.com/art/201908/600603.htm?spm=ata.13261165.0.0.3d1462a5BuyJzs
3.https://yq.aliyun.com/articles/652472?spm=ata.13261165.0.0.3d1462a5BuyJzs&utm_content=m_1000018600
4.https://blog.csdn.net/ruanchengmin/article/details/79210632?spm=ata.13261165.0.0.3d1462a5BuyJzs
END
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的阿里工程师是如何系统化地总结缓存相关知识的的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Lambda、函数式接口、Stream
- 下一篇: poj 1185 NYOJ 85 炮兵