客戶端發送請求給web.xml中springmvc的dispatcherServlet;php
dispatcherServlet調用handlerMapping處理器映射器;html
HandlerMapping經過XML配置、註解查找並返回具體處理器對象以及處理攔截器(若是有則返回)給dispatcherServlet;前端
dispatcherServlet調用handlerAdapter處理器適配器;java
handlerAdapter調用具體的處理器(Controller,也叫後端控制器);python
Controller執行完成返回ModelAndView。mysql
HandlerAdapter將controller執行結果ModelAndView返回給DispatcherServlet。nginx
DispatcherServlet將ModelAndView傳給ViewReslover視圖解析器。git
ViewReslover解析後返回具體View。程序員
DispatcherServlet根據View進行渲染視圖(即將模型數據填充至視圖中)。github
DispatcherServlet響應用戶。
組件說明:
如下組件一般使用框架提供實現:
DispatcherServlet:做爲前端控制器,整個流程控制的中心,控制其它組件執行,統一調度,下降組件之間的耦合性,提升每一個組件的擴展性。
HandlerMapping:經過擴展處理器映射器實現不一樣的映射方式,例如:配置文件方式,實現接口方式,註解方式等。
HandlAdapter:經過擴展處理器適配器,支持更多類型的處理器。
ViewResolver:經過擴展視圖解析器,支持更多類型的視圖解析,例如:jsp、freemarker、pdf、excel等。
組件:
1、前端控制器DispatcherServlet(不須要工程師開發),由框架提供
做用:接收請求,響應結果,至關於轉發器,中央處理器。有了dispatcherServlet減小了其它組件之間的耦合度。
用戶請求到達前端控制器,它就至關於mvc模式中的c,dispatcherServlet是整個流程控制的中心,由它調用其它組件處理用戶的請求,dispatcherServlet的存在下降了組件之間的耦合性。
二、處理器映射器HandlerMapping(不須要工程師開發),由框架提供
做用:根據請求的url查找Handler
HandlerMapping負責根據用戶請求找到Handler即處理器,springmvc提供了不一樣的映射器實現不一樣的映射方式,例如:配置文件方式,實現接口方式,註解方式等。
三、處理器適配器HandlerAdapter
做用:按照特定規則(HandlerAdapter要求的規則)去執行Handler
經過HandlerAdapter對處理器進行執行,這是適配器模式的應用,經過擴展適配器能夠對更多類型的處理器進行執行。
四、處理器Handler(須要工程師開發)
注意:編寫Handler時按照HandlerAdapter的要求去作,這樣適配器才能夠去正確執行Handler
Handler 是繼DispatcherServlet前端控制器的後端控制器,在DispatcherServlet的控制下Handler對具體的用戶請求進行處理。
因爲Handler涉及到具體的用戶業務請求,因此通常狀況須要工程師根據業務需求開發Handler。
五、視圖解析器View resolver(不須要工程師開發),由框架提供
做用:進行視圖解析,根據邏輯視圖名解析成真正的視圖(view)
View Resolver負責將處理結果生成View視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址,再生成View視圖對象,最後對View進行渲染將處理結果經過頁面展現給用戶。 springmvc框架提供了不少的View視圖類型,包括:jstlView、freemarkerView、pdfView等。
通常狀況下須要經過頁面標籤或頁面模版技術將模型數據經過頁面展現給用戶,須要由工程師根據業務需求開發具體的頁面。
六、視圖View(須要工程師開發jsp...)
View是一個接口,實現類支持不一樣的View類型(jsp、freemarker、pdf...)
核心架構的具體流程步驟以下:
一、首先用戶發送請求——>DispatcherServlet,前端控制器收到請求後本身不進行處理,而是委託給其餘的解析器進行處理,做爲統一訪問點,進行全局的流程控制;
二、DispatcherServlet——>HandlerMapping, HandlerMapping 將會把請求映射爲HandlerExecutionChain 對象(包含一個Handler 處理器(頁面控制器)對象、多個HandlerInterceptor 攔截器)對象,經過這種策略模式,很容易添加新的映射策略;
三、DispatcherServlet——>HandlerAdapter,HandlerAdapter 將會把處理器包裝爲適配器,從而支持多種類型的處理器,即適配器設計模式的應用,從而很容易支持不少類型的處理器;
四、HandlerAdapter——>處理器功能處理方法的調用,HandlerAdapter 將會根據適配的結果調用真正的處理器的功能處理方法,完成功能處理;並返回一個ModelAndView 對象(包含模型數據、邏輯視圖名);
五、ModelAndView的邏輯視圖名——> ViewResolver, ViewResolver 將把邏輯視圖名解析爲具體的View,經過這種策略模式,很容易更換其餘視圖技術;
六、View——>渲染,View會根據傳進來的Model模型數據進行渲染,此處的Model實際是一個Map數據結構,所以很容易支持其餘視圖技術;
七、返回控制權給DispatcherServlet,由DispatcherServlet返回響應給用戶,到此一個流程結束。
下邊兩個組件一般狀況下須要開發:
Handler:處理器,即後端控制器用controller表示。
View:視圖,即展現給用戶的界面,視圖中一般須要標籤語言展現模型數據。
在將SpringMVC以前咱們先來看一下什麼是MVC模式
MVC:MVC是一種設計模式
MVC的原理圖:
分析:
M-Model 模型(完成業務邏輯:有javaBean構成,service+dao+entity)
V-View 視圖(作界面的展現 jsp,html……)
C-Controller 控制器(接收請求—>調用模型—>根據結果派發頁面)
springMVC是什麼:
springMVC是一個MVC的開源框架,springMVC=struts2+spring,springMVC就至關因而Struts2加上sring的整合,可是這裏有一個疑惑就是,springMVC和spring是什麼樣的關係呢?這個在百度百科上有一個很好的解釋:意思是說,springMVC是spring的一個後續產品,其實就是spring在原有基礎上,又提供了web應用的MVC模塊,能夠簡單的把springMVC理解爲是spring的一個模塊(相似AOP,IOC這樣的模塊),網絡上常常會說springMVC和spring無縫集成,其實springMVC就是spring的一個子模塊,因此根本不須要同spring進行整合。
SpringMVC的原理圖:
看到這個圖你們可能會有不少的疑惑,如今咱們來看一下這個圖的步驟:(能夠對比MVC的原理圖進行理解)
第一步:用戶發起請求到前端控制器(DispatcherServlet)
第二步:前端控制器請求處理器映射器(HandlerMappering)去查找處理器(Handle):經過xml配置或者註解進行查找
第三步:找到之後處理器映射器(HandlerMappering)像前端控制器返回執行鏈(HandlerExecutionChain)
第四步:前端控制器(DispatcherServlet)調用處理器適配器(HandlerAdapter)去執行處理器(Handler)
第五步:處理器適配器去執行Handler
第六步:Handler執行完給處理器適配器返回ModelAndView
第七步:處理器適配器向前端控制器返回ModelAndView
第八步:前端控制器請求視圖解析器(ViewResolver)去進行視圖解析
第九步:視圖解析器像前端控制器返回View
第十步:前端控制器對視圖進行渲染
第十一步:前端控制器向用戶響應結果
看到這些步驟我相信你們很感受很是的亂,這是正常的,可是這裏主要是要你們理解springMVC中的幾個組件:
前端控制器(DispatcherServlet):接收請求,響應結果,至關於電腦的CPU。
處理器映射器(HandlerMapping):根據URL去查找處理器
處理器(Handler):(須要程序員去寫代碼處理邏輯的)
處理器適配器(HandlerAdapter):會把處理器包裝成適配器,這樣就能夠支持多種類型的處理器,類比筆記本的適配器(適配器模式的應用)
視圖解析器(ViewResovler):進行視圖解析,多返回的字符串,進行處理,能夠解析成對應的頁面
本篇講訴數據庫中事務的四大特性(ACID),而且將會詳細地說明事務的隔離級別。
若是一個數據庫聲稱支持事務的操做,那麼該數據庫必需要具有如下四個特性:
⑴ 原子性(Atomicity)
原子性是指事務包含的全部操做要麼所有成功,要麼所有失敗回滾,這和前面兩篇博客介紹事務的功能是同樣的概念,所以事務的操做若是成功就必需要徹底應用到數據庫,若是操做失敗則不能對數據庫有任何影響。
⑵ 一致性(Consistency)
一致性是指事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態,也就是說一個事務執行以前和執行以後都必須處於一致性狀態。
拿轉帳來講,假設用戶A和用戶B二者的錢加起來一共是5000,那麼無論A和B之間如何轉帳,轉幾回帳,事務結束後兩個用戶的錢相加起來應該還得是5000,這就是事務的一致性。
⑶ 隔離性(Isolation)
隔離性是當多個用戶併發訪問數據庫時,好比操做同一張表時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做所幹擾,多個併發事務之間要相互隔離。
即要達到這麼一種效果:對於任意兩個併發的事務T1和T2,在事務T1看來,T2要麼在T1開始以前就已經結束,要麼在T1結束以後纔開始,這樣每一個事務都感受不到有其餘事務在併發地執行。
關於事務的隔離性數據庫提供了多種隔離級別,稍後會介紹到。
⑷ 持久性(Durability)
持久性是指一個事務一旦被提交了,那麼對數據庫中的數據的改變就是永久性的,即使是在數據庫系統遇到故障的狀況下也不會丟失提交事務的操做。
例如咱們在使用JDBC操做數據庫時,在提交事務方法後,提示用戶事務操做完成,當咱們程序執行完成直到看到提示後,就能夠認定事務以及正確提交,即便這時候數據庫出現了問題,也必需要將咱們的事務徹底執行完成,不然就會形成咱們看到提示事務處理完畢,可是數據庫由於故障而沒有執行事務的重大錯誤。
以上介紹完事務的四大特性(簡稱ACID),如今重點來講明下事務的隔離性,當多個線程都開啓事務操做數據庫中的數據時,數據庫系統要能進行隔離操做,以保證各個線程獲取數據的準確性,在介紹數據庫提供的各類隔離級別以前,咱們先看看若是不考慮事務的隔離性,會發生的幾種問題:
1,髒讀
髒讀是指在一個事務處理過程裏讀取了另外一個未提交的事務中的數據。
當一個事務正在屢次修改某個數據,而在這個事務中這屢次的修改都還未提交,這時一個併發的事務來訪問該數據,就會形成兩個事務獲得的數據不一致。例如:用戶A向用戶B轉帳100元,對應SQL命令以下
update account set money=money+100 where name=’B’; (此時A通知B)
update account set money=money - 100 where name=’A’;
當只執行第一條SQL時,A通知B查看帳戶,B發現確實錢已到帳(此時即發生了髒讀),而以後不管第二條SQL是否執行,只要該事務不提交,則全部操做都將回滾,那麼當B之後再次查看帳戶時就會發現錢其實並無轉。
2,不可重複讀
不可重複讀是指在對於數據庫中的某個數據,一個事務範圍內屢次查詢卻返回了不一樣的數據值,這是因爲在查詢間隔,被另外一個事務修改並提交了。
例如事務T1在讀取某一數據,而事務T2立馬修改了這個數據而且提交事務給數據庫,事務T1再次讀取該數據就獲得了不一樣的結果,發送了不可重複讀。
不可重複讀和髒讀的區別是,髒讀是某一事務讀取了另外一個事務未提交的髒數據,而不可重複讀則是讀取了前一事務提交的數據。
在某些狀況下,不可重複讀並非問題,好比咱們屢次查詢某個數據固然以最後查詢獲得的結果爲主。但在另外一些狀況下就有可能發生問題,例如對於同一個數據A和B依次查詢就可能不一樣,A和B就可能打起來了……
3,虛讀(幻讀)
幻讀是事務非獨立執行時發生的一種現象。例如事務T1對一個表中全部的行的某個數據項作了從「1」修改成「2」的操做,這時事務T2又對這個表中插入了一行數據項,而這個數據項的數值仍是爲「1」而且提交給數據庫。而操做事務T1的用戶若是再查看剛剛修改的數據,會發現還有一行沒有修改,其實這行是從事務T2中添加的,就好像產生幻覺同樣,這就是發生了幻讀。
幻讀和不可重複讀都是讀取了另外一條已經提交的事務(這點就髒讀不一樣),所不一樣的是不可重複讀查詢的都是同一個數據項,而幻讀針對的是一批數據總體(好比數據的個數)。
如今來看看MySQL數據庫爲咱們提供的四種隔離級別:
① Serializable (串行化):可避免髒讀、不可重複讀、幻讀的發生。
② Repeatable read (可重複讀):可避免髒讀、不可重複讀的發生。
③ Read committed (讀已提交):可避免髒讀的發生。
④ Read uncommitted (讀未提交):最低級別,任何狀況都沒法保證。
以上四種隔離級別最高的是Serializable級別,最低的是Read uncommitted級別,固然級別越高,執行效率就越低。像Serializable這樣的級別,就是以鎖表的方式(相似於Java多線程中的鎖)使得其餘的線程只能在鎖外等待,因此平時選用何種隔離級別應該根據實際狀況。在MySQL數據庫中默認的隔離級別爲Repeatable read (可重複讀)。
在MySQL數據庫中,支持上面四種隔離級別,默認的爲Repeatable read (可重複讀);而在Oracle數據庫中,只支持Serializable (串行化)級別和Read committed (讀已提交)這兩種級別,其中默認的爲Read committed級別。
在MySQL數據庫中查看當前事務的隔離級別:
select @@tx_isolation;
在MySQL數據庫中設置事務的隔離 級別:
set [glogal | session] transaction isolation level 隔離級別名稱;
set tx_isolation=’隔離級別名稱;’
例1:查看當前事務的隔離級別:
例2:將事務的隔離級別設置爲Read uncommitted級別:
或:
記住:設置數據庫的隔離級別必定要是在開啓事務以前!
若是是使用JDBC對數據庫的事務設置隔離級別的話,也應該是在調用Connection對象的setAutoCommit(false)方法以前。調用Connection對象的setTransactionIsolation(level)便可設置當前連接的隔離級別,至於參數level,能夠使用Connection對象的字段:
在JDBC中設置隔離級別的部分代碼:
後記:隔離級別的設置只對當前連接有效。對於使用MySQL命令窗口而言,一個窗口就至關於一個連接,當前窗口設置的隔離級別只對當前窗口中的事務有效;對於JDBC操做數據庫來講,一個Connection對象至關於一個連接,而對於Connection對象設置的隔離級別只對該Connection對象有效,與其餘連接Connection對象無關。
參考博客:
http://www.zhihu.com/question/23989904
http://dev.mysql.com/doc/refman/5.6/en/set-transaction.html
http://www.cnblogs.com/xdp-gacl/p/3984001.html
什麼是線程
一個線程是進程的一個順序執行流。同類的多個線程共享一塊內存空間和一組系統資源,線程自己有一個供程序執行時的堆棧。線程在切換時負荷小,所以,線程也被稱爲輕負荷進程。一個進程中能夠包含多個線程。
進程與線程的區別
一個進程至少有一個線程。線程的劃分尺度小於進程,使得多線程程序的併發性高。另外,進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率。
線程在執行過程當中與進程的區別在於每一個獨立的線程有一個程序運行的入口、順序執行序列和程序的出口。可是線程不可以獨立執行,必須依存在應用程序中,由應用程序提供多個線程執行控制。
從邏輯角度來看,多線程的意義在於一個應用程序中,有多個執行部分能夠同時執行。但操做系統並無將多個線程看作多個獨立的應用來實現進程的調度和管理以及資源分配。
併發原理
多個線程或進程」同時」運行只是咱們感官上的一種表現。事實上進程和線程是併發運行的,OS的線程調度機制將時間劃分爲不少時間片斷(時間片),儘量均勻分配給正在運行的程序,獲取CPU時間片的線程或進程得以被執行,其餘則等待。而CPU則在這些進程或線程上來回切換運行。微觀上全部進程和線程是走走停停的,宏觀上都在運行,這種都運行的現象叫併發,可是不是絕對意義上的「同時發生。
線程狀態
1.新建
用new語句建立的線程對象處於新建狀態,此時它和其餘java對象同樣,僅被分配了內存。
2.等待
當線程在new以後,而且在調用start方法前,線程處於等待狀態。
3.就緒
當一個線程對象建立後,其餘線程調用它的start()方法,該線程就進入就緒狀態。處於這個狀態的線程位於Java虛擬機的可運行池中,等待cpu的使用權。
4.運行狀態
處於這個狀態的線程佔用CPU,執行程序代碼。在併發運行環境中,若是計算機只有一個CPU,那麼任什麼時候刻只會有一個線程處於這個狀態。
只有處於就緒狀態的線程纔有機會轉到運行狀態。
5.阻塞狀態
阻塞狀態是指線程由於某些緣由放棄CPU,暫時中止運行。當線程處於阻塞狀態時,Java虛擬機不會給線程分配CPU,直到線程從新進入就緒狀態,它纔會有機會得到運行狀態。
阻塞狀態分爲三種:
一、等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
二、同步阻塞:運行的線程在獲取對象同步鎖時,若該同步鎖被別的線程佔用,則JVM會把線程放入鎖池中。
三、其餘阻塞:運行的線程執行Sleep()方法,或者發出I/O請求時,JVM會把線程設爲阻塞狀態。當Sleep()狀態超時、或者I/O處理完畢時,線程從新轉入就緒狀態。
6.死亡狀態
當線程執行完run()方法中的代碼,或者遇到了未捕獲的異常,就會退出run()方法,此時就進入死亡狀態,該線程結束生命週期。
關於SpringMVC,Web.xml監聽器是否必須
咱們首先來看兩個配置:
<!-- Spring MVC配置 -->
<!-- ====================================== -->
<servlet>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 能夠自定義servlet.xml配置文件的位置和名稱,默認爲WEB-INF目錄下,名稱爲[<servlet-name>]-servlet.xml,如spring-servlet.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value> 默認
</init-param>
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- Spring配置 -->
<!-- ====================================== -->
<listener>
<listenerclass>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目錄。默認配置在WEB-INF目錄下 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/applicationContext.xml</param-value>
</context-param>
在項目中不配置Spring配置,spring同樣能夠管理項目的bean固然是在springMVC配置裏面掃描或維護了bean的關聯
爲何咱們又建議配置spring監聽器?
咱們看配置裏面加載spring.xml文件的兩個標籤,前者是在servlet裏面用的 <init-param> 初始化標籤,咱們在spring的 DispatcherServlet源碼中能夠看到如下代碼:
HttpServletBean類中(此類被DispatcherServlet所繼承)
Enumeration en = config.getInitParameterNames();
while (en.hasMoreElements()) {
String property = (String) en.nextElement();
Object value = config.getInitParameter(property);
addPropertyValue(new PropertyValue(property, value));
if (missingProps != null) {
missingProps.remove(property);
}
}
學過servlet的應該會明白了把就是在servlet初始化時調用,好比你在前者把spring標籤故意寫錯,啓動項目不會報錯,只有在訪問servlet時纔會報錯。由於此時才加載spring配置文件。可是後者監聽器若是故意在配置文件中出現檢查式錯誤,那麼啓動服務器時就會報錯。由於監聽器在服務器啓動的時候也被啓動此時也就開始加載spring配置文件。
<mvc:annotation-driven>會自動註冊RequestMappingHandlerMapping與RequestMappingHandlerAdapter兩個Bean,這是Spring MVC爲@Controller分發請求所必需的,而且提供了數據綁定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持讀寫XML的支持(JAXB)和讀寫JSON的支持(默認Jackson)等功能。
使用該註解後的springmvc-config.xml:
<!-- spring 能夠自動去掃描 base-package下面的包或子包下面的Java文件,若是掃描到有Spring的相關
註解的類,則把這些類註冊爲Spring的bean -->
<context:component-scan base-package="org.fkit.controller"/>
<!--設置配置方案 -->
<mvc:annotation-driven/>
<!--使用默認的Servlet來響應靜態文件-->
<mvc:default-servlet-handler/>
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前綴 -->
<property name="prefix">
<value>/WEB-INF/content/</value>
</property>
<!-- 後綴 -->
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
解釋2
<mvc:annotation-driven /> 說明:
是一種簡寫形式,可讓初學者快速成應用默認的配置方案,會默認註冊 DefaultAnnotationHandleMapping以及AnnotionMethodHandleAdapter 這兩個 Bean, 這兩個 Bean ,前者對應類級別, 後者對應到方法級別;
上在面的 DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter 是 Spring 爲 @Controller 分發請求所必需的。
annotation-driven 掃描指定包中類上的註解,經常使用的註解有:
複製代碼
@Controller 聲明Action組件
@Service 聲明Service組件 @Service("myMovieLister")
@Repository 聲明Dao組件
@Component 泛指組件, 當很差歸類時.
@RequestMapping("/menu") 請求映射
@Resource 用於注入,( j2ee提供的 ) 默認按名稱裝配,@Resource(name="beanName")
@Autowired 用於注入,(srping提供的) 默認按類型裝配
@Transactional( rollbackFor={Exception.class}) 事務管理
@ResponseBody
@Scope("prototype") 設定bean的做用域
今天,數據庫的操做愈來愈成爲整個應用的性能瓶頸了,這點對於Web應用尤爲明顯。關於數據庫的性能,這並不僅是DBA才須要擔憂的事,而這更是咱們程序員須要去關注的事情。當咱們去設計數據庫表結構,對操做數據庫時(尤爲是查表時的SQL語句),咱們都須要注意數據操做的性能。這裏,咱們不會講過多的SQL語句的優化,而只是針對MySQL這一Web應用最多的數據庫。但願下面的這些優化技巧對你有用。
1. 爲查詢緩存優化你的查詢
大多數的MySQL服務器都開啓了查詢緩存。這是提升性最有效的方法之一,並且這是被MySQL的數據庫引擎處理的。當有不少相同的查詢被執行了屢次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操做表而直接訪問緩存結果了。
這裏最主要的問題是,對於程序員來講,這個事情是很容易被忽略的。由於,咱們某些查詢語句會讓MySQL不使用緩存。請看下面的示例:
1 2 3 4 5 6 |
|
上面兩條SQL語句的差異就是 CURDATE() ,MySQL的查詢緩存對這個函數不起做用。因此,像 NOW() 和 RAND() 或是其它的諸如此類的SQL函數都不會開啓查詢緩存,由於這些函數的返回是會不定的易變的。因此,你所須要的就是用一個變量來代替MySQL的函數,從而開啓緩存。
2. EXPLAIN 你的 SELECT 查詢
使用 EXPLAIN 關鍵字可讓你知道MySQL是如何處理你的SQL語句的。這能夠幫你分析你的查詢語句或是表結構的性能瓶頸。
EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。
挑一個你的SELECT語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字EXPLAIN加到前面。你能夠使用phpmyadmin來作這個事。而後,你會看到一張表格。下面的這個示例中,咱們忘記加上了group_id索引,而且有表聯接:
當咱們爲 group_id 字段加上索引後:
咱們能夠看到,前一個結果顯示搜索了 7883 行,然後一個只是搜索了兩個表的 9 和 16 行。查看rows列可讓咱們找到潛在的性能問題。
3. 當只要一行數據時使用 LIMIT 1
當你查詢表的有些時候,你已經知道結果只會有一條結果,但由於你可能須要去fetch遊標,或是你也許會去檢查返回的記錄數。
在這種狀況下,加上 LIMIT 1 能夠增長性能。這樣同樣,MySQL數據庫引擎會在找到一條數據後中止搜索,而不是繼續日後查少下一條符合記錄的數據。
下面的示例,只是爲了找一下是否有「中國」的用戶,很明顯,後面的會比前面的更有效率。(請注意,第一條中是Select *,第二條是Select 1)
1 2 3 4 5 6 7 8 9 10 11 |
|
4. 爲搜索字段建索引
索引並不必定就是給主鍵或是惟一的字段。若是在你的表中,有某個字段你總要會常常用來作搜索,那麼,請爲其創建索引吧。
從上圖你能夠看到那個搜索字串 「last_name LIKE ‘a%'」,一個是建了索引,一個是沒有索引,性能差了4倍左右。
另外,你應該也須要知道什麼樣的搜索是不能使用正常的索引的。例如,當你須要在一篇大的文章中搜索一個詞時,如: 「WHERE post_content LIKE ‘%apple%'」,索引多是沒有意義的。你可能須要使用MySQL全文索引 或是本身作一個索引(好比說:搜索關鍵詞或是Tag什麼的)
5. 在Join表的時候使用至關類型的例,並將其索引
若是你的應用程序有不少 JOIN 查詢,你應該確認兩個表中Join的字段是被建過索引的。這樣,MySQL內部會啓動爲你優化Join的SQL語句的機制。
並且,這些被用來Join的字段,應該是相同的類型的。例如:若是你要把 DECIMAL 字段和一個 INT 字段Join在一塊兒,MySQL就沒法使用它們的索引。對於那些STRING類型,還須要有相同的字符集才行。(兩個表的字符集有可能不同)
1 2 3 4 5 6 |
|
6. 千萬不要 ORDER BY RAND()
想打亂返回的數據行?隨機挑一個數據?真不知道誰發明了這種用法,但不少新手很喜歡這樣用。但你確不瞭解這樣作有多麼可怕的性能問題。
若是你真的想把返回的數據行打亂了,你有N種方法能夠達到這個目的。這樣使用只讓你的數據庫的性能呈指數級的降低。這裏的問題是:MySQL會不得不去執行RAND()函數(很耗CPU時間),並且這是爲了每一行記錄去記行,而後再對其排序。就算是你用了Limit 1也無濟於事(由於要排序)
下面的示例是隨機挑一條記錄
1 2 3 4 5 6 7 8 9 |
|
7. 避免 SELECT *
從數據庫裏讀出越多的數據,那麼查詢就會變得越慢。而且,若是你的數據庫服務器和WEB服務器是兩臺獨立的服務器的話,這還會增長網絡傳輸的負載。
因此,你應該養成一個須要什麼就取什麼的好的習慣。
1 2 3 4 5 6 7 8 9 |
|
8. 永遠爲每張表設置一個ID
咱們應該爲數據庫裏的每張表都設置一個ID作爲其主鍵,並且最好的是一個INT型的(推薦使用UNSIGNED),並設置上自動增長的AUTO_INCREMENT標誌。
就算是你 users 表有一個主鍵叫 「email」的字段,你也別讓它成爲主鍵。使用 VARCHAR 類型來當主鍵會使用得性能降低。另外,在你的程序中,你應該使用表的ID來構造你的數據結構。
並且,在MySQL數據引擎下,還有一些操做須要使用主鍵,在這些狀況下,主鍵的性能和設置變得很是重要,好比,集羣,分區……
在這裏,只有一個狀況是例外,那就是「關聯表」的「外鍵」,也就是說,這個表的主鍵,經過若干個別的表的主鍵構成。咱們把這個狀況叫作「外鍵」。好比:有一個「學生表」有學生的ID,有一個「課程表」有課程ID,那麼,「成績表」就是「關聯表」了,其關聯了學生表和課程表,在成績表中,學生ID和課程ID叫「外鍵」其共同組成主鍵。
9. 使用 ENUM 而不是 VARCHAR
ENUM 類型是很是快和緊湊的。在實際上,其保存的是 TINYINT,但其外表上顯示爲字符串。這樣一來,用這個字段來作一些選項列表變得至關的完美。
若是你有一個字段,好比「性別」,「國家」,「民族」,「狀態」或「部門」,你知道這些字段的取值是有限並且固定的,那麼,你應該使用 ENUM 而不是 VARCHAR。
MySQL也有一個「建議」(見第十條)告訴你怎麼去從新組織你的表結構。當你有一個 VARCHAR 字段時,這個建議會告訴你把其改爲 ENUM 類型。使用 PROCEDURE ANALYSE() 你能夠獲得相關的建議。
10. 從 PROCEDURE ANALYSE() 取得建議
PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的字段和其實際的數據,並會給你一些有用的建議。只有表中有實際的數據,這些建議纔會變得有用,由於要作一些大的決定是須要有數據做爲基礎的。
例如,若是你建立了一個 INT 字段做爲你的主鍵,然而並無太多的數據,那麼,PROCEDURE ANALYSE()會建議你把這個字段的類型改爲 MEDIUMINT 。或是你使用了一個 VARCHAR 字段,由於數據很少,你可能會獲得一個讓你把它改爲 ENUM 的建議。這些建議,都是可能由於數據不夠多,因此決策作得就不夠準。
在phpmyadmin裏,你能夠在查看錶時,點擊 「Propose table structure」 來查看這些建議
必定要注意,這些只是建議,只有當你的表裏的數據愈來愈多時,這些建議纔會變得準確。必定要記住,你纔是最終作決定的人。
11. 儘量的使用 NOT NULL
除非你有一個很特別的緣由去使用 NULL 值,你應該老是讓你的字段保持 NOT NULL。這看起來好像有點爭議,請往下看。
首先,問問你本身「Empty」和「NULL」有多大的區別(若是是INT,那就是0和NULL)?若是你以爲它們之間沒有什麼區別,那麼你就不要使用NULL。(你知道嗎?在 Oracle 裏,NULL 和 Empty 的字符串是同樣的!)
不要覺得 NULL 不須要空間,其須要額外的空間,而且,在你進行比較的時候,你的程序會更復雜。 固然,這裏並非說你就不能使用NULL了,現實狀況是很複雜的,依然會有些狀況下,你須要使用NULL值。
下面摘自MySQL本身的文檔:
「NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.」
12. Prepared Statements
Prepared Statements很像存儲過程,是一種運行在後臺的SQL語句集合,咱們能夠從使用 prepared statements 得到不少好處,不管是性能問題仍是安全問題。
Prepared Statements 能夠檢查一些你綁定好的變量,這樣能夠保護你的程序不會受到「SQL注入式」攻擊。固然,你也能夠手動地檢查你的這些變量,然而,手動的檢查容易出問題,並且很常常會被程序員忘了。當咱們使用一些framework或是ORM的時候,這樣的問題會好一些。
在性能方面,當一個相同的查詢被使用屢次的時候,這會爲你帶來可觀的性能優點。你能夠給這些Prepared Statements定義一些參數,而MySQL只會解析一次。
雖然最新版本的MySQL在傳輸Prepared Statements是使用二進制形勢,因此這會使得網絡傳輸很是有效率。
固然,也有一些狀況下,咱們須要避免使用Prepared Statements,由於其不支持查詢緩存。但聽說版本5.1後支持了。
在PHP中要使用prepared statements,你能夠查看其使用手冊:mysqli 擴展 或是使用數據庫抽象層,如: PDO.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
13. 無緩衝的查詢
正常的狀況下,當你在當你在你的腳本中執行一個SQL語句的時候,你的程序會停在那裏直到沒這個SQL語句返回,而後你的程序再往下繼續執行。你能夠使用無緩衝查詢來改變這個行爲。
關於這個事情,在PHP的文檔中有一個很是不錯的說明: mysql_unbuffered_query() 函數:
「mysql_unbuffered_query() sends the SQL query query to MySQL without automatically fetching and buffering the result rows as mysql_query() does. This saves a considerable amount of memory with SQL queries that produce large result sets, and you can start working on the result set immediately after the first row has been retrieved as you don’t have to wait until the complete SQL query has been performed.」
上面那句話翻譯過來是說,mysql_unbuffered_query() 發送一個SQL語句到MySQL而並不像mysql_query()同樣去自動fethch和緩存結果。這會至關節約不少可觀的內存,尤爲是那些會產生大量結果的查詢語句,而且,你不須要等到全部的結果都返回,只須要第一行數據返回的時候,你就能夠開始立刻開始工做於查詢結果了。
然而,這會有一些限制。由於你要麼把全部行都讀走,或是你要在進行下一次的查詢前調用 mysql_free_result() 清除結果。並且, mysql_num_rows() 或 mysql_data_seek() 將沒法使用。因此,是否使用無緩衝的查詢你須要仔細考慮。
14. 把IP地址存成 UNSIGNED INT
不少程序員都會建立一個 VARCHAR(15) 字段來存放字符串形式的IP而不是整形的IP。若是你用整形來存放,只須要4個字節,而且你能夠有定長的字段。並且,這會爲你帶來查詢上的優點,尤爲是當你須要使用這樣的WHERE條件:IP between ip1 and ip2。
咱們必須要使用UNSIGNED INT,由於 IP地址會使用整個32位的無符號整形。
而你的查詢,你能夠使用 INET_ATON() 來把一個字符串IP轉成一個整形,並使用 INET_NTOA() 把一個整形轉成一個字符串IP。在PHP中,也有這樣的函數 ip2long() 和 long2ip()。
1 |
|
15. 固定長度的表會更快
若是表中的全部字段都是「固定長度」的,整個表會被認爲是 「static」 或 「fixed-length」。 例如,表中沒有以下類型的字段: VARCHAR,TEXT,BLOB。只要你包括了其中一個這些字段,那麼這個表就不是「固定長度靜態表」了,這樣,MySQL 引擎會用另外一種方法來處理。
固定長度的表會提升性能,由於MySQL搜尋得會更快一些,由於這些固定的長度是很容易計算下一個數據的偏移量的,因此讀取的天然也會很快。而若是字段不是定長的,那麼,每一次要找下一條的話,須要程序找到主鍵。
而且,固定長度的表也更容易被緩存和重建。不過,惟一的反作用是,固定長度的字段會浪費一些空間,由於定長的字段不管你用不用,他都是要分配那麼多的空間。
使用「垂直分割」技術(見下一條),你能夠分割你的表成爲兩個一個是定長的,一個則是不定長的。
16. 垂直分割
「垂直分割」是一種把數據庫中的表按列變成幾張表的方法,這樣能夠下降表的複雜度和字段的數目,從而達到優化的目的。(之前,在銀行作過項目,見過一張表有100多個字段,很恐怖)
示例一:在Users表中有一個字段是家庭地址,這個字段是可選字段,相比起,並且你在數據庫操做的時候除了我的信息外,你並不須要常常讀取或是改寫這個字段。那麼,爲何不把他放到另一張表中呢? 這樣會讓你的表有更好的性能,你們想一想是否是,大量的時候,我對於用戶表來講,只有用戶ID,用戶名,口令,用戶角色等會被常用。小一點的表老是會有好的性能。
示例二: 你有一個叫 「last_login」 的字段,它會在每次用戶登陸時被更新。可是,每次更新時會致使該表的查詢緩存被清空。因此,你能夠把這個字段放到另外一個表中,這樣就不會影響你對用戶ID,用戶名,用戶角色的不停地讀取了,由於查詢緩存會幫你增長不少性能。
另外,你須要注意的是,這些被分出去的字段所造成的表,你不會常常性地去Join他們,否則的話,這樣的性能會比不分割時還要差,並且,會是極數級的降低。
17. 拆分大的 DELETE 或 INSERT 語句
若是你須要在一個在線的網站上去執行一個大的 DELETE 或 INSERT 查詢,你須要很是當心,要避免你的操做讓你的整個網站中止相應。由於這兩個操做是會鎖表的,表一鎖住了,別的操做都進不來了。
Apache 會有不少的子進程或線程。因此,其工做起來至關有效率,而咱們的服務器也不但願有太多的子進程,線程和數據庫連接,這是極大的佔服務器資源的事情,尤爲是內存。
若是你把你的表鎖上一段時間,好比30秒鐘,那麼對於一個有很高訪問量的站點來講,這30秒所積累的訪問進程/線程,數據庫連接,打開的文件數,可能不只僅會讓你泊WEB服務Crash,還可能會讓你的整臺服務器立刻掛了。
因此,若是你有一個大的處理,你定你必定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個示例:
1 2 3 4 5 6 7 8 9 10 |
|
18. 越小的列會越快
對於大多數的數據庫引擎來講,硬盤操做多是最重大的瓶頸。因此,把你的數據變得緊湊會對這種狀況很是有幫助,由於這減小了對硬盤的訪問。
參看 MySQL 的文檔 Storage Requirements 查看全部的數據類型。
若是一個表只會有幾列罷了(好比說字典表,配置表),那麼,咱們就沒有理由使用 INT 來作主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會更經濟一些。若是你不須要記錄時間,使用 DATE 要比 DATETIME 好得多。
固然,你也須要留夠足夠的擴展空間,否則,你往後來幹這個事,你會死的很難看,參看Slashdot的例子(2009年11月06日),一個簡單的ALTER TABLE語句花了3個多小時,由於裏面有一千六百萬條數據。
19. 選擇正確的存儲引擎
在 MySQL 中有兩個存儲引擎 MyISAM 和 InnoDB,每一個引擎都有利有弊。酷殼之前文章《MySQL: InnoDB 仍是 MyISAM?》討論和這個事情。
MyISAM 適合於一些須要大量查詢的應用,但其對於有大量寫操做並非很好。甚至你只是須要update一個字段,整個表都會被鎖起來,而別的進程,就算是讀進程都沒法操做直到讀操做完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。
InnoDB 的趨勢會是一個很是複雜的存儲引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支持「行鎖」 ,因而在寫操做比較多的時候,會更優秀。而且,他還支持更多的高級應用,好比:事務。
下面是MySQL的手冊
target=」_blank」MyISAM Storage Engine
20. 使用一個對象關係映射器(Object Relational Mapper)
使用 ORM (Object Relational Mapper),你可以得到可靠的性能增漲。一個ORM能夠作的全部事情,也能被手動的編寫出來。可是,這須要一個高級專家。
ORM 的最重要的是「Lazy Loading」,也就是說,只有在須要的去取值的時候纔會去真正的去作。但你也須要當心這種機制的反作用,由於這頗有可能會由於要去建立不少不少小的查詢反而會下降性能。
ORM 還能夠把你的SQL語句打包成一個事務,這會比單獨執行他們快得多得多。
目前,我的最喜歡的PHP的ORM是:Doctrine。
21. 當心「永久連接」
「永久連接」的目的是用來減小從新建立MySQL連接的次數。當一個連接被建立了,它會永遠處在鏈接的狀態,就算是數據庫操做已經結束了。並且,自從咱們的Apache開始重用它的子進程後——也就是說,下一次的HTTP請求會重用Apache的子進程,並重用相同的 MySQL 連接。
在理論上來講,這聽起來很是的不錯。可是從我的經驗(也是大多數人的)上來講,這個功能製造出來的麻煩事更多。由於,你只有有限的連接數,內存問題,文件句柄數,等等。
並且,Apache 運行在極端並行的環境中,會建立不少不少的了進程。這就是爲何這種「永久連接」的機制工做地很差的緣由。在你決定要使用「永久連接」以前,你須要好好地考慮一下你的整個系統的架構。
1.JVM簡析:
做爲一名Java使用者,掌握JVM的體系結構也是頗有必要的。
提及Java,咱們首先想到的是Java編程語言,然而事實上,Java是一種技術,它由四方面組成:Java編程語言、Java類文件格式、Java虛擬機和Java應用程序接口(Java API)。它們的關係以下圖所示:
Java平臺由Java虛擬機和Java應用程序接口搭建,Java語言則是進入這個平臺的通道,用Java語言編寫並編譯的程序能夠運行在這個平臺上。這個平臺的結構以下圖所示: 運行期環境表明着Java平臺,開發人員編寫Java代碼(.java文件),而後將之編譯成字節碼(.class文件),再而後字節碼被裝入內存,一旦字節碼進入虛擬機,它就會被解釋器解釋執行,或者是被即時代碼發生器有選擇的轉換成機器碼執行。
JVM在它的生存週期中有一個明確的任務,那就是運行Java程序,所以當Java程序啓動的時候,就產生JVM的一個實例;當程序運行結束的時候,該實例也跟着消失了。 在Java平臺的結構中, 能夠看出,Java虛擬機(JVM) 處在覈心的位置,是程序與底層操做系統和硬件無關的關鍵。它的下方是移植接口,移植接口由兩部分組成:適配器和Java操做系統, 其中依賴於平臺的部分稱爲適配器;JVM 經過移植接口在具體的平臺和操做系統上實現;在JVM 的上方是Java的基本類庫和擴展類庫以及它們的API, 利用Java API編寫的應用程序(application) 和小程序(Java applet) 能夠在任何Java平臺上運行而無需考慮底層平臺, 就是由於有Java虛擬機(JVM)實現了程序與操做系統的分離,從而實現了Java 的平臺無關性。
下面咱們從JVM的基本概念和運過程程這兩個方面入手來對它進行深刻的研究。
2.JVM基本概念
(1) 基本概念:
JVM是可運行Java代碼的假想計算機 ,包括一套字節碼指令集、一組寄存器、一個棧、一個垃圾回收堆 和 一個存儲方法域。JVM是運行在操做系統之上的,它與硬件沒有直接的交互。
(2) 運行過程:
咱們都知道Java源文件,經過編譯器,可以生產相應的.Class文件,也就是字節碼文件,而字節碼文件又經過Java虛擬機中的解釋器,編譯成特定機器上的機器碼 。
也就是以下:
① Java源文件—->編譯器—->字節碼文件
② 字節碼文件—->JVM—->機器碼
每一種平臺的解釋器是不一樣的,可是實現的虛擬機是相同的,這也就是Java爲何可以跨平臺的緣由了 ,當一個程序從開始運行,這時虛擬機就開始實例化了,多個程序啓動就會存在多個虛擬機實例。程序退出或者關閉,則虛擬機實例消亡,多個虛擬機實例之間數據不能共享。
(3) 三種JVM:
① Sun公司的HotSpot;
② BEA公司的JRockit;
③ IBM公司的J9 JVM;
在JDK1.7及其之前咱們所使用的都是Sun公司的HotSpot,但因爲Sun公司和BEA公司都被oracle收購,jdk1.8將採用Sun公司的HotSpot和BEA公司的JRockit兩個JVM中精華造成jdk1.8的JVM。
3.JVM的體系結構
![]()
|
![]()
|
(1) Class Loader類加載器
負責加載 .class文件,class文件在文件開頭有特定的文件標示,而且ClassLoader負責class文件的加載等,至於它是否能夠運行,則由Execution Engine決定。
① 定位和導入二進制class文件
② 驗證導入類的正確性
③ 爲類分配初始化內存
④ 幫助解析符號引用.
(2) Native Interface本地接口:
本地接口的做用是融合不一樣的編程語言爲Java所用,它的初衷是融合C/C++程序,Java誕生的時候C/C++橫行的時候,要想立足,必須有調用C/C++程序,因而就在內存中專門開闢了一塊區域處理標記爲native的代碼,它的具體做法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies。
目前該方法使用的愈來愈少了,除非是與硬件有關的應用,好比經過Java程序驅動打印機,或者Java系統管理生產設備,在企業級應用中已經比較少見。
由於如今的異構領域間的通訊很發達,好比能夠使用Socket通訊,也能夠使用Web Service等。
(3) Execution Engine 執行引擎:執行包在裝載類的方法中的指令,也就是方法。
(4) Runtime data area 運行數據區:
虛擬機內存或者Jvm內存,衝整個計算機內存中開闢一塊內存存儲Jvm須要用到的對象,變量等,運行區數據有分不少小區,分別爲:方法區,虛擬機棧,本地方法棧,堆,程序計數器。
4.JVM數據運行區詳解(棧管運行,堆管存儲):
說明:JVM調優主要就是優化 Heap堆 和 Method Area 方法區。
(1) Native Method Stack本地方法棧
它的具體作法是Native Method Stack中登記native方法,在Execution Engine執行時加載native libraies。
(2) PC Register程序計數器
每一個線程都有一個程序計算器,就是一個指針,指向方法區中的方法字節碼(下一個將要執行的指令代碼),由執行引擎讀取下一條指令,是一個很是小的內存空間,幾乎能夠忽略不記。
(3) Method Area方法區
方法區是被全部線程共享,全部字段和方法字節碼,以及一些特殊方法如構造函數,接口代碼也在此定義。簡單說,全部定義的方法的信息都保存在該區域,此區域屬於共享區間。
靜態變量+常量+類信息+運行時常量池存在方法區中,實例變量存在堆內存中。
(4) Stack 棧
① 棧是什麼
棧也叫棧內存,主管Java程序的運行,是在線程建立時建立,它的生命期是跟隨線程的生命期,線程結束棧內存也就釋放,對於棧來講不存在垃圾回收問題,只要線程一結束該棧就Over,生命週期和線程一致,是線程私有的。
基本類型的變量和對象的引用變量都是在函數的棧內存中分配。
② 棧存儲什麼?
棧幀中主要保存3類數據:
本地變量(Local Variables):輸入參數和輸出參數以及方法內的變量;
棧操做(Operand Stack):記錄出棧、入棧的操做;
棧幀數據(Frame Data):包括類文件、方法等等。
③ 棧運行原理
棧中的數據都是以棧幀(Stack Frame)的格式存在,棧幀是一個內存區塊,是一個數據集,是一個有關方法和運行期數據的數據集,當一個方法A被調用時就產生了一個棧幀F1,並被壓入到棧中,A方法又調用了B方法,因而產生棧幀F2也被壓入棧,B方法又調用了C方法,因而產生棧幀F3也被壓入棧…… 依次執行完畢後,先彈出後進......F3棧幀,再彈出F2棧幀,再彈出F1棧幀。
遵循「先進後出」/「後進先出」原則。
(5) Heap 堆
堆這塊區域是JVM中最大的,應用的對象和數據都是存在這個區域,這塊區域也是線程共享的,也是 gc 主要的回收區,一個 JVM 實例只存在一個堆類存,堆內存的大小是能夠調節的。類加載器讀取了類文件後,須要把類、方法、常變量放到堆內存中,以方便執行器執行,堆內存分爲三部分:
① 新生區
新生區是類的誕生、成長、消亡的區域,一個類在這裏產生,應用,最後被垃圾回收器收集,結束生命。新生區又分爲兩部分:伊甸區(Eden space)和倖存者區(Survivor pace),全部的類都是在伊甸區被new出來的。倖存區有兩個:0區(Survivor 0 space)和1區(Survivor 1 space)。當伊甸園的空間用完時,程序又須要建立對象,JVM的垃圾回收器將對伊甸園進行垃圾回收(Minor GC),將伊甸園中的剩餘對象移動到倖存0區。若倖存0區也滿了,再對該區進行垃圾回收,而後移動到1區。那若是1去也滿了呢?再移動到養老區。若養老區也滿了,那麼這個時候將產生Major GC(FullGCC),進行養老區的內存清理。若養老區執行Full GC 以後發現依然沒法進行對象的保存,就會產生OOM異常「OutOfMemoryError」。
若是出現java.lang.OutOfMemoryError: Java heap space異常,說明Java虛擬機的堆內存不夠。緣由有二:
a.Java虛擬機的堆內存設置不夠,能夠經過參數-Xms、-Xmx來調整。
b.代碼中建立了大量大對象,而且長時間不能被垃圾收集器收集(存在被引用)。
② 養老區
養老區用於保存重新生區篩選出來的 JAVA 對象,通常池對象都在這個區域活躍。
③ 永久區
永久存儲區是一個常駐內存區域,用於存放JDK自身所攜帶的 Class,Interface 的元數據,也就是說它存儲的是運行環境必須的類信息,被裝載進此區域的數據是不會被垃圾回收器回收掉的,關閉 JVM 纔會釋放此區域所佔用的內存。
若是出現java.lang.OutOfMemoryError: PermGen space,說明是Java虛擬機對永久代Perm內存設置不夠。緣由有二:
a. 程序啓動須要加載大量的第三方jar包。例如:在一個Tomcat下部署了太多的應用。
b. 大量動態反射生成的類不斷被加載,最終致使Perm區被佔滿。
說明:
Jdk1.6及以前:常量池分配在永久代 。
Jdk1.7:有,但已經逐步「去永久代」 。
Jdk1.8及以後:無(java.lang.OutOfMemoryError: PermGen space,這種錯誤將不會出如今JDK1.8中)。
![]()
|
![]()
|
說明:方法區和堆內存的異議:
實際而言,方法區和堆同樣,是各個線程共享的內存區域,它用於存儲虛擬機加載的:類信息+普一般量+靜態常量+編譯器編譯後的代碼等等,雖然JVM規範將方法區描述爲堆的一個邏輯部分,但它卻還有一個別名叫作Non-Heap(非堆),目的就是要和堆分開。
對於HotSpot虛擬機,不少開發者習慣將方法區稱之爲「永久代(Parmanent Gen)」,但嚴格本質上說二者不一樣,或者說使用永久代來實現方法區而已,永久代是方法區的一個實現,jdk1.7的版本中,已經將本來放在永久代的字符串常量池移走。
常量池(Constant Pool)是方法區的一部分,Class文件除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,這部份內容將在類加載後進入方法區的運行時常量池中存放。
5.堆內存調優簡介
代碼測試:
[java] view plain copy
1. <span style="font-family:'Microsoft YaHei';font-size:14px;">public class JVMTest {
2. public static void main(String[] args){
3. long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虛擬機試圖使用的最大內存量。
4. Long totalMemory = Runtime. getRuntime().totalMemory();//返回Java虛擬機中的內存總量。
5. System.out.println("MAX_MEMORY ="+maxMemory +"(字節)、"+(maxMemory/(double)1024/1024) + "MB");
6. System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字節)"+(totalMemory/(double)1024/1024) + "MB");
7. }
8. }</span>
說明:在Run as ->Run Configurations中輸入"-XX:+PrintGCDetails"能夠查看堆內存運行原理圖:
(1) 在jdk1.7中:
(2) 在jdk1.8中:
6.經過參數設置自動觸發垃圾回收:
public class JVMTest {
public static void main(String[] args){
long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虛擬機試圖使用的最大內存量。
Long totalMemory = Runtime. getRuntime().totalMemory();//返回Java虛擬機中的內存總量。
System.out.println("MAX_MEMORY ="+maxMemory +"(字節)、"+(maxMemory/(double)1024/1024) + "MB");
System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字節)"+(totalMemory/(double)1024/1024) + "MB");
String str = "www.baidu.com";
while(true){
str += str + new Random().nextInt(88888888) + new Random().nextInt(99999999);
}
}
}
在Run as ->Run Configurations中輸入設置「-Xmx8m –Xms8m –xx:+PrintGCDetails」能夠參看垃圾回收機制原理:
調優
堆大小設置
JVM 中最大堆大小有三方面限制:相關操做系統的數據模型(32-bt仍是64-bit)限制;系統的可用虛擬內存限制;系統的可用物理內存限制。32位系統下,通常限制在1.5G~2G;64爲操做系統對內存無限制。我在Windows Server 2003 系統,3.5G物理內存,JDK5.0下測試,最大可設置爲1478m。
典型設置:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m:設置JVM最大可用內存爲3550M。
-Xms3550m:設置JVM促使內存爲3550m。此值能夠設置與-Xmx相同,以免每次垃圾回收完成後JVM從新分配內存。
-Xmn2g:設置年輕代大小爲2G。整個JVM內存大小=年輕代大小 + 年老代大小 + 持久代大小。持久代通常固定大小爲64m,因此增大年輕代後,將會減少年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
-Xss128k:設置每一個線程的堆棧大小。JDK5.0之後每一個線程堆棧大小爲1M,之前每一個線程堆棧大小爲256K。更具應用的線程所需內存大小進行調整。在相同物理內存下,減少這個值能生成更多的線程。可是操做系統對一個進程內的線程數仍是有限制的,不能無限生成,經驗值在3000~5000左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置爲4,則年輕代與年老代所佔比值爲1:4,年輕代佔整個堆棧的1/5
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的大小比值。設置爲4,則兩個Survivor區與一個Eden區的比值爲2:4,一個Survivor區佔整個年輕代的1/6
-XX:MaxPermSize=16m:設置持久代大小爲16m。
-XX:MaxTenuringThreshold=0:設置垃圾最大年齡。若是設置爲0的話,則年輕代對象不通過Survivor區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大值,則年輕代對象會在Survivor區進行屢次複製,這樣能夠增長對象再年輕代的存活時間,增長在年輕代即被回收的概論。
回收器選擇
JVM給了三種選擇:串行收集器、並行收集器、併發收集器,可是串行收集器只適用於小數據量的狀況,因此這裏的選擇主要針對並行收集器和併發收集器。默認狀況下,JDK5.0之前都是使用串行收集器,若是想使用其餘收集器須要在啓動時加入相應參數。JDK5.0之後,JVM會根據當前系統配置進行判斷。
吞吐量優先的並行收集器
如上文所述,並行收集器主要以到達必定的吞吐量爲目標,適用於科學技術和後臺處理等。
典型配置:
java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
-XX:+UseParallelGC:選擇垃圾收集器爲並行收集器。此配置僅對年輕代有效。即上述配置下,年輕代使用併發收集,而年老代仍舊使用串行收集。
-XX:ParallelGCThreads=20:配置並行收集器的線程數,即:同時多少個線程一塊兒進行垃圾回收。此值最好配置與處理器數目相等。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
-XX:+UseParallelOldGC:配置年老代垃圾收集方式爲並行收集。JDK6.0支持對年老代並行收集。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100
-XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間,若是沒法知足此時間,JVM會自動調全年輕代大小,以知足此值。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
-XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直打開。
響應時間優先的併發收集器
如上文所述,併發收集器主要是保證系統的響應時間,減小垃圾收集時的停頓時間。適用於應用服務器、電信領域等。
典型配置:
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+UseConcMarkSweepGC:設置年老代爲併發收集。測試中配置這個之後,-XX:NewRatio=4的配置失效了,緣由不明。因此,此時年輕代大小最好用-Xmn設置。
-XX:+UseParNewGC:設置年輕代爲並行收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,因此無需再設置此值。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction:因爲併發收集器不對內存空間進行壓縮、整理,因此運行一段時間之後會產生「碎片」,使得運行效率下降。此值設置運行多少次GC之後對內存空間進行壓縮、整理。
-XX:+UseCMSCompactAtFullCollection:打開對年老代的壓縮。可能會影響性能,可是能夠消除碎片
輔助信息
JVM提供了大量命令行參數,打印信息,供調試使用。主要有如下一些:
-XX:+PrintGC
輸出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]
[Full GC 121376K->10414K(130112K), 0.0650971 secs]
-XX:+PrintGCDetails
輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]
[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]
-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可與上面兩個混合使用
輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]
-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中斷的執行時間。可與上面混合使用
輸出形式:Application time: 0.5291524 seconds
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期間程序暫停的時間。可與上面混合使用
輸出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:PrintHeapAtGC:打印GC先後的詳細堆棧信息
輸出形式:
34.702: [GC {Heap before gc invocations=7:
def new generation total 55296K, used 52568K [0x1ebd0000, 0x227d0000, 0x227d0000)
eden space 49152K, 99% used [0x1ebd0000, 0x21bce430, 0x21bd0000)
from space 6144K, 55% used [0x221d0000, 0x22527e10, 0x227d0000)
to space 6144K, 0% used [0x21bd0000, 0x21bd0000, 0x221d0000)
tenured generation total 69632K, used 2696K [0x227d0000, 0x26bd0000, 0x26bd0000)
the space 69632K, 3% used [0x227d0000, 0x22a720f8, 0x22a72200, 0x26bd0000)
compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
34.735: [DefNew: 52568K->3433K(55296K), 0.0072126 secs] 55264K->6615K(124928K)Heap after gc invocations=8:
def new generation total 55296K, used 3433K [0x1ebd0000, 0x227d0000, 0x227d0000)
eden space 49152K, 0% used [0x1ebd0000, 0x1ebd0000, 0x21bd0000)
from space 6144K, 55% used [0x21bd0000, 0x21f2a5e8, 0x221d0000)
to space 6144K, 0% used [0x221d0000, 0x221d0000, 0x227d0000)
tenured generation total 69632K, used 3182K [0x227d0000, 0x26bd0000, 0x26bd0000)
the space 69632K, 4% used [0x227d0000, 0x22aeb958, 0x22aeba00, 0x26bd0000)
compacting perm gen total 8192K, used 2898K [0x26bd0000, 0x273d0000, 0x2abd0000)
the space 8192K, 35% used [0x26bd0000, 0x26ea4ba8, 0x26ea4c00, 0x273d0000)
ro space 8192K, 66% used [0x2abd0000, 0x2b12bcc0, 0x2b12be00, 0x2b3d0000)
rw space 12288K, 46% used [0x2b3d0000, 0x2b972060, 0x2b972200, 0x2bfd0000)
}
, 0.0757599 secs]
-Xloggc:filename:與上面幾個配合使用,把相關日誌信息記錄到文件以便分析。
常見配置彙總
堆設置
-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:設置年輕代大小
-XX:NewRatio=n:設置年輕代和年老代的比值。如:爲3,表示年輕代與年老代比值爲1:3,年輕代佔整個年輕代年老代和的1/4
-XX:SurvivorRatio=n:年輕代中Eden區與兩個Survivor區的比值。注意Survivor區有兩個。如:3,表示Eden:Survivor=3:2,一個Survivor區佔整個年輕代的1/5
-XX:MaxPermSize=n:設置持久代大小
收集器設置
-XX:+UseSerialGC:設置串行收集器
-XX:+UseParallelGC:設置並行收集器
-XX:+UseParalledlOldGC:設置並行年老代收集器
-XX:+UseConcMarkSweepGC:設置併發收集器
垃圾回收統計信息
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
並行收集器設置
-XX:ParallelGCThreads=n:設置並行收集器收集時使用的CPU數。並行收集線程數。
-XX:MaxGCPauseMillis=n:設置並行收集最大暫停時間
-XX:GCTimeRatio=n:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+n)
併發收集器設置
-XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU狀況。
-XX:ParallelGCThreads=n:設置併發收集器年輕代收集方式爲並行收集時,使用的CPU數。並行收集線程數。
4、調優總結
年輕代大小選擇
響應時間優先的應用:儘量設大,直到接近系統的最低響應時間限制(根據實際狀況選擇)。在此種狀況下,年輕代收集發生的頻率也是最小的。同時,減小到達年老代的對象。
吞吐量優先的應用:儘量的設置大,可能到達Gbit的程度。由於對響應時間沒有要求,垃圾收集能夠並行進行,通常適合8CPU以上的應用。
年老代大小選擇
響應時間優先的應用:年老代使用併發收集器,因此其大小須要當心設置,通常要考慮併發會話率和會話持續時間等一些參數。若是堆設置小了,能夠會形成內存碎片、高回收頻率以及應用暫停而使用傳統的標記清除方式;若是堆大了,則須要較長的收集時間。最優化的方案,通常須要參考如下數據得到:
併發垃圾收集信息
持久代併發收集次數
傳統GC信息
花在年輕代和年老代回收上的時間比例
減小年輕代和年老代花費的時間,通常會提升應用的效率
吞吐量優先的應用:通常吞吐量優先的應用都有一個很大的年輕代和一個較小的年老代。緣由是,這樣能夠儘量回收掉大部分短時間對象,減小中期的對象,而年老代盡存放長期存活對象。
較小堆引發的碎片問題
由於年老代的併發收集器使用標記、清除算法,因此不會對堆進行壓縮。當收集器回收時,他會把相鄰的空間進行合併,這樣能夠分配給較大的對象。可是,當堆空間較小時,運行一段時間之後,就會出現「碎片」,若是併發收集器找不到足夠的空間,那麼併發收集器將會中止,而後使用傳統的標記、清除方式進行回收。若是出現「碎片」,可能須要進行以下配置:
-XX:+UseCMSCompactAtFullCollection:使用併發收集器時,開啓對年老代的壓縮。
-XX:CMSFullGCsBeforeCompaction=0:上面配置開啓的狀況下,這裏設置多少次Full GC後,對年老代進行壓縮
1. Redis緩存數據持久化的幾種方式(RDB和AOF)
2. Redis緩存緩存穿透,緩存擊穿,緩存雪崩解決方案
緩存穿透方案:對一個必定不存在數據的key也進行緩存過時時間設置短一些
緩存擊穿方案:對一個熱點快要過時的key 併發訪問能夠使用互斥鎖(mutex key)、提早使用互斥鎖(mutex key)、永遠不過時、資源保護(netflix的hystrix)
緩存雪崩方案:設置緩存時採用了相同的過時時間,致使緩存在某一時刻同時失效,請求所有轉發到DB,DB瞬時壓力太重雪崩能夠設置時間時加一個隨機值,過時時間的重複率就會下降
兩種機制補償機制和消息機制
列如:銀行轉帳怎麼樣保證事務一致性
ELK是一款日誌分析系統,它是Logstash
+ElasticSearch
+Kibana
的技術組合,它能夠經過logstash收集各個微服務日誌,並經過Kibana進行可視化展現,並且還能夠對大量日誌信息經過ElasticSearch進行全文檢索
先介紹兩個概念:高併發和高可用。
高併發>即在單位時間內的併發請求數很是高,所以對網站的吞吐能力和處理能力比較高。例如12306,淘寶等。
高可用>即對網站的穩定性要求比較高,好比不容許中止服務,某臺機器出問題後不影響網站的正常訪問等。
解決方法:
高併發nginx + redis集羣 + 分佈式 + 數據庫讀寫分離
高可用 分佈式 +微服務 +zookeeper
水平分片 垂直分塊 詳細百度
1.線程提交時公平隊列和非公平隊列的區別
2.線程同步happen before 的規則
https://blog.csdn.net/ns_code/article/details/17348313
3. 線程中wait 和 notify的方法
4. Java中鎖的相關內容
5. 工程中微服務如何保證事物的一致性,同步對象在微服務中怎麼用,幾個工程都須要訪問和修改同一個對象(java後臺 和數據庫兩方面實現)
listA 和listb 分別包含 a,b,c 和 a,c 實現取交集,並集,差集幾種方式
如今咱們一步一步來分析put源碼。
對key作null檢查。若是key是null,會被存儲到table[0],由於null的hash值老是0。
key的hashcode()方法會被調用,而後計算hash值。hash值用來找到存儲Entry對象的數組的索引。有時候hash函數可能寫的很很差,因此JDK的設計者添加了另外一個叫作hash()的方法,它接收剛纔計算的hash值做爲參數。若是你想了解更多關於hash()函數的東西,能夠參考:hashmap中的hash和indexFor方法
indexFor(hash,table.length)用來計算在table數組中存儲Entry對象的精確的索引。
在咱們的例子中已經看到,若是兩個key有相同的hash值(也叫衝突),他們會以鏈表的形式來存儲。因此,這裏咱們就迭代鏈表。
· 若是在剛纔計算出來的索引位置沒有元素,直接把Entry對象放在那個索引上。
· 若是索引上有元素,而後會進行迭代,一直到Entry->next是null。當前的Entry對象變成鏈表的下一個節點。
· 若是咱們再次放入一樣的key會怎樣呢?邏輯上,它應該替換老的value。事實上,它確實是這麼作的。在迭代的過程當中,會調用equals()方法來檢查key的相等性(key.equals(k)),若是這個方法返回true,它就會用當前Entry的value來替換以前的value。
get源碼
當你理解了hashmap的put的工做原理,理解get的工做原理就很是簡單了。當你傳遞一個key從hashmap總獲取value的時候:
對key進行null檢查。若是key是null,table[0]這個位置的元素將被返回。
key的hashcode()方法被調用,而後計算hash值。
indexFor(hash,table.length)用來計算要獲取的Entry對象在table數組中的精確的位置,使用剛纔計算的hash值。
在獲取了table數組的索引以後,會迭代鏈表,調用equals()方法檢查key的相等性,若是equals()方法返回true,get方法返回Entry對象的value,不然,返回null。
要牢記如下關鍵點:
· HashMap有一個叫作Entry的內部類,它用來存儲key-value對。
· 上面的Entry對象是存儲在一個叫作table的Entry數組中。
· table的索引在邏輯上叫作「桶」(bucket),它存儲了鏈表的第一個元素。
· key的hashcode()方法用來找到Entry對象所在的桶。
· 若是兩個key有相同的hash值,他們會被放在table數組的同一個桶裏面。
· key的equals()方法用來確保key的惟一性。
· value對象的equals()和hashcode()方法根本一點用也沒有。
Statement用於執行不帶參數的簡單SQL語句,並返回它所生成的結果,每次執行SQL豫劇時,數據庫都要編譯該SQL語句。
Satatement stmt = conn.getStatement();
stmt.executeUpdate("insert into client values("aa","aaa")");
PreparedStatement表示預編譯的SQL語句的對象,用於執行帶參數的預編譯的SQL語句。
CallableStatement則提供了用來調用數據庫中存儲過程的接口,若是有輸出參數要註冊,說明是輸出參數。
雖然Statement對象與PreparedStatement對象可以完成相同的功能,可是相比之下,PreparedStatement具備如下優勢:
1.效率更高。
在使用PreparedStatement對象執行SQL命令時,命令會被數據庫進行編譯和解析,並放到命令緩衝區,而後,每當執行同一個PreparedStatement對象時,因爲在緩存區中能夠發現預編譯的命令,雖然它會被再解析一次,可是不會被再一次編譯,是能夠重複使用的,可以有效提升系統性能,所以,若是要執行插入,更新,刪除等操做,最好使用PreparedSatement。鑑於此,PreparedStatement適用於存在大量用戶的企業級應用軟件中。
2.代碼可讀性和可維護性更好。
下兩種方法分別使用Statement和PreparedStatement來執行SQL語句,顯然方法二具備更好的可讀性。
方法1:
stmt.executeUpdate("insert into t(col1,xol2) values('"+var2+"','"+var2+"')");
方法2:
perstmt = con.prepareStatement("insert into tb_name(col1,col2) values(?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
3.安全性更好。
使用PreparedStatement可以預防SQL注入攻擊,所謂SQL注入,指的是經過把SQL命令插入到Web表單提交或者輸入域名或者頁面請求的查詢字符串,最終達到欺騙服務器,達到執行惡意SQL命令的目的。注入只對SQL語句的編譯過程有破壞做用,而執行階段只是把輸入串做爲數據處理,再也不須要對SQL語句進行解析,所以也就避免了相似select * from user where name='aa' and password='bb' or 1=1的sql注入問題的發生。
CallableStatement由prepareCall()方法所建立,它爲全部的DBMS(Database Management System)提供了一種以標準形式調用已存儲過程的方法。它從PreparedStatement中繼承了用於處理輸入參數的方法,並且還增長了調用數據庫中的存儲過程和函數以及設置輸出類型參數的功能。
2016年07月11日 15:34:34 閱讀數:49634更多
我的分類: 數據庫
版權聲明:歡迎轉載,註明出處就好!若是不喜歡請留言說明緣由再踩!謝謝,我也能夠知道緣由,不斷進步!! https://blog.csdn.net/Scythe666/article/details/51881235
缺省狀況下是inner join,開發中使用的left join和right join屬於outer join,另外outer join還包括full join.下面我經過圖標讓你們認識它們的區別。
現有兩張表,Table A 是左邊的表。Table B 是右邊的表。其各有四條記錄,其中有兩條記錄name是相同的:
1.INNER JOIN 產生的結果是AB的交集
SELECT * FROM TableA INNER JOIN TableB ON TableA.name = TableB.name
2.LEFT [OUTER] JOIN 產生表A的徹底集,而B表中匹配的則有值,沒有匹配的則以null值取代。
SELECT * FROM TableA LEFT OUTER JOIN TableB ON TableA.name = TableB.name
3.RIGHT [OUTER] JOIN 產生表B的徹底集,而A表中匹配的則有值,沒有匹配的則以null值取代。
SELECT * FROM TableA RIGHT OUTER JOIN TableB ON TableA.name = TableB.name
圖標如left join相似。
4.FULL [OUTER] JOIN 產生A和B的並集。對於沒有匹配的記錄,則會以null作爲值。
SELECT * FROM TableA FULL OUTER JOIN TableB ON TableA.name = TableB.name
你能夠經過is NULL將沒有匹配的值找出來:
SELECT * FROM TableA FULL OUTER JOIN TableB ON TableA.name = TableB.name
WHERE TableA.id IS null OR TableB.id IS null
5. CROSS JOIN 把表A和表B的數據進行一個N*M的組合,即笛卡爾積。如本例會產生4*4=16條記錄,在開發過程當中咱們確定是要過濾數據,因此這種不多用。
SELECT * FROM TableA CROSS JOIN TableB
相信你們對inner join、outer join和cross join的區別一目瞭然了。
Java GC機制(重要程度:★★★★★)
主要從三個方面回答:GC是針對什麼對象進行回收(可達性分析法),何時開始GC(當新生代滿了會進行Minor GC,升到老年代的對象大於老年代剩餘空間時會進行Major GC),GC作什麼(新生代採用複製算法,老年代採用標記-清除或標記-整理算法),感受回答這些就差很少了,也能夠補充一下能夠調優的參數(-XX:newRatio,-Xms,-Xmx等等)。詳細的能夠看我另外一篇博客(Java中的垃圾回收機制)。
如何線程安全的使用HashMap(重要程度:★★★★★)
做爲Java程序員仍是常常和HashMap打交道的,因此HashMap的一些原理仍是搞搞清除比較好。這個問題感受主要就是問HashMap,HashTable,ConcurrentHashMap,sychronizedMap的原理和區別。具體的能夠看我另外一篇博客(如何線程安全的使用HashMap)。
HashMap是如何解決衝突的(重要程度:★★★★☆)
其實就是連接法,將索引值相同的元素存放到一個單鏈表裏。但爲了解決在頻繁衝突時HashMap性能下降的問題,Java 8中作了一個小優化,在衝突的元素個數超過設定的值(默認爲8)時,會使用平衡樹來替代鏈表存儲衝突的元素。具體的能夠看我另外一篇博客(Java 8中HashMap和LinkedHashMap如何解決衝突)。
Java建立對象有哪幾種(重要程度:★★★★☆)
這個問題還算好回答,大概有四種—new、工廠模式、反射和克隆,不過這個問題有可能衍生出關於設計模式,反射,深克隆,淺克隆等一系列問題。。。要作好準備~
參考資料:
設計模式Java版
Java反射詳解
深克隆與淺克隆的區別
註解(重要程度:★★★☆☆)
若是簡歷中有提到過曾自定義過註解,仍是瞭解清楚比較好。主要是瞭解在自定義註解時須要使用的兩個主要的元註解@Retention和@Target。@Retention用來聲明註解的保留策略,有CLASS,RUNTIME,SOURCE三種,分別表示註解保存在類文件,JVM運行時刻和源代碼中。@Target用來聲明註解能夠被添加到哪些類型的元素上,如類型,方法和域等。
參考資料:
Java註解
異常(重要程度:★★★☆☆)
一道筆試題,代碼以下,問返回值是什麼。
1 2 3 4 5 6 7 8 9 10 11 |
|
主要的考點就是catch中的return在finally以後執行 可是會將return的值放到一個地方存起來,因此finally中的ret=2會執行,但返回值是1。
參考資料:
深刻理解Java異常處理機制
Java異常處理
悲觀鎖和樂觀鎖區別,樂觀鎖適用於什麼狀況(重要程度:★★★★☆)
悲觀鎖,就是總以爲有刁民想害朕,每次訪問數據的時候都以爲會有別人修改它,因此每次拿數據時都會上鎖,確保在本身使用的過程當中不會被他人訪問。樂觀鎖就是很單純,心態好,因此每次拿數據的時候都不會上鎖,只是在更新數據的時候去判斷該數據是否被別人修改過。
大多數的關係數據庫寫入操做都是基於悲觀鎖,缺點在於若是持有鎖的客戶端運行的很慢,那麼等待解鎖的客戶端被阻塞的時間就越長。Redis的事務是基於樂觀鎖的機制,不會在執行WATCH命令時對數據進行加鎖,只是會在數據已經被其餘客戶端搶先修改了的狀況下,通知執行WATCH命令的客戶端。樂觀鎖適用於讀多寫少的狀況,由於在寫操做比較頻繁的時候,會不斷地retry,從而下降性能。
參考資料:
關於悲觀鎖和樂觀鎖的區別
樂觀鎖和悲觀鎖
單例模式找錯誤(重要程度:★★★★☆)
錯誤是沒有將構造函數私有化,單例仍是比較簡單的,把它的餓漢式和懶漢式的兩種實現方式看明白了就能夠了。
單例模式
__
關於Spring的問題主要就是圍繞着Ioc和AOP,它們真是Spring的核心啊。
Spring Bean的生命週期(重要程度:★★★★★)
就不寫我那麼low的回答了,直接看參考資料吧。
參考資料:
Spring Bean的生命週期
Top 10 Spring Interview Questions Answers J2EE
Spring中用到的設計模式(重要程度:★★★★★)
工廠模式:IOC容器
代理模式:AOP
策略模式:在spring採起動態代理時,根據代理的類有無實現接口有JDK和CGLIB兩種代理方式,就是採用策略模式實現的
單例模式:默認狀況下spring中的bean只存在一個實例
只知道這四個。。。。
參考資料:
Design Patterns Used in Java Spring Framework
講一講Spring IoC和AOP(重要程度:★★★★★)
IoC的核心是依賴反轉,將建立對象和對象之間的依賴管理交給IoC容器來作,完成對象之間的解耦。
AOP主要是利用代理模式,把許多接口都要用的又和接口自己主要的業務邏輯無關的部分抽出來,寫成一個切面,單獨維護,好比權限驗證。這樣能夠使接口符合「單一職責原則」,只關注主要的業務邏輯,也提升了代碼的重用性。
AOP的應用場景(重要程度:★★★★☆)
權限,日誌,處理異常,事務等等,我的理解就是把許多接口都要用的又和接口自己主要的業務邏輯無關的部分抽出來,寫成一個切面,單獨維護,好比權限驗證。這樣能夠使接口符合「單一職責原則」,只關注主要的業務邏輯,也提升了代碼的重用性。
Spring中編碼統一要如何作(重要程度:★★★☆☆)
配置一個攔截器就好了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Mysql索引的內部結構(重要程度:★★★★☆)
B+樹,三層,真實的數據存儲在葉子節點
參考資料:
MySQL索引原理及慢查詢優化
若是一個SQL執行時間比較長怎麼辦(重要程度:★★★★☆)
能夠利用pt-query-digest等工具分析慢查詢日誌,也能夠用explain查看SQL的執行計劃。具體可看個人另外一篇博客MySQL調優
若是一張表中有上千萬條數據應該怎麼作分頁(重要程度:★★★☆☆)
確定不能直接limit,offset,主要就是要想辦法避免在數據量大時掃描過多的記錄。具體可看個人另外一篇博客【譯】優化MySQL中的分頁
什麼樣的列適合加索引,若是一個列的值只有1和2,那麼它適合加索引麼(重要程度:★★★☆☆)
只有1和2不適合建索引
Redis提供哪幾種數據結構(重要程度:★★★★★)
一共有5種,字符串,散列,列表,集合,有序集合。
參考資料:
Redis中文官網
Redis支持集羣麼,從哪一個版本開始支持集羣的(重要程度:★★☆☆☆)
支持集羣,從3.0版本開始。固然面試時我也沒記住版本。。。
Redis集羣中,如何將一個對象映射到對應的緩存服務器(重要程度:★★★★☆)
通常就是hash%N,就是用對象的hash值對緩存服務器的個數取餘
接上個問題,緩存集羣中若是新增一臺服務器,怎麼才能不影響大部分緩存數據的命中?(重要程度:★★★★☆)
其實就是一致性Hash算法。之前有看過,惋惜面試的時候腦殼就空了,只記得一個環,果真仍是要實踐啊。
參考資料:
Consistent Hashing
五分鐘理解一致性哈希算法(consistent hashing)
項目中具體是怎樣使用Redis的(重要程度:★★★★☆)
根據實際狀況回答吧。。。。我是主要作權限控制時用到了Redis,將用戶Id和權限Code拼接在一塊兒做爲一個key,放到Redis的集合中,在驗證某一用戶是否有指定權限時,只需驗證集合中是否有用戶Id和權限Code拼接的key便可
判斷一個數字是否爲快樂數字(重要程度:★☆☆☆☆)
leetcode第202題
連接
給定一個亂序數組和一個目標數字 找到和爲這個數字的兩個數字 時間複雜度是多少(重要程度:★☆☆☆☆)
leetcode第一題
連接
如何判斷一個鏈表有沒有環(重要程度:★☆☆☆☆)
用快慢指針
刪除字符串中的空格 只留一個(重要程度:★☆☆☆☆)
這個比較簡單。。。。
二叉樹層序遍歷(重要程度:★★☆☆☆)
利用隊列就能夠了
地鐵票價是如何計算的(重要程度:★★☆☆☆)
不知道正確答案,感受是圖的最短路徑算法相關的。
爲何要用Elasticsearch(重要程度:★★★★☆)
其實對Es的瞭解仍是比較少的,由於沒作多久就去寫坑爹代理商了。我的以爲項目中用Es的緣由一是能夠作分詞,二是Es中採用的是倒排索引因此性能比較好,三是Es是個分佈式的搜索服務,對各個節點的配置仍是很簡單方便的
Elasticsearch中的數據來源是什麼,如何作同步(重要程度:★★★★☆)
數據是來自其餘部門的數據庫,會在一開始寫python腳本作全量更新,以後利用RabbitMQ作增量更新,就是數據更改以後,數據提供方將更改的數據插入到指定消息隊列,由對應的消費者索引到Es中
接上個問題,利用消息隊列是會對對方代碼形成侵入的,還有沒有別的方式(重要程度:★★★☆☆)
還能夠讀MySQL的binlog
如下題都是沒有正確答案的,不知道是想考思惟,仍是壓力面試,就只寫題目,不寫回答了。。。
畫一下心中房樹人的關係(重要程度:★☆☆☆☆)
給你一塊地建房如何規劃(重要程度:★☆☆☆☆)
估計二號線有幾輛車在運行(重要程度:★☆☆☆☆)
Thrift通訊協議(重要程度:★★★☆☆)
這個問題被問了兩遍,然而如今仍是不知道。。。什麼東西都不能停留在只會用的階段啊~
git相關(重要程度:★★★★★)
一些git相關的問題,好比如何作分支管理(git flow),rebase和merge的區別(merge操做會生成一個新的節點)等等。。。
如何學習一門新技術(重要程度:★★★☆☆)
google+官網+stackoverflow+github
比較愛逛的網站和愛看的書(重要程度:★★★☆☆)
根據實際狀況回答吧。。。
了不瞭解微服務(重要程度:★★☆☆☆)
簡單瞭解過。。。
參考資料:
基於微服務的軟件架構模式
針對簡歷中的項目問一些問題(重要程度:★★★★★)
就是根據簡歷上的項目問一些東西,好比權限控制是怎麼作的,有沒有碰到過比較難解決的問題之類的,不具體列舉了,只要簡歷上的內容是真實的基本都沒啥問題
爲何要離職(重要程度:★★★★★)
被問了n遍,挺很差回答的一個問題,畢竟不算實習期工做還沒滿一年,這個時候跳槽很容易讓人以爲不安穩。。。。
對公司還有什麼問題(重要程度:★★★★★)
基本每輪面試結束都會問的一個問題