<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    Hibernate 緩存

    Posted on 2008-03-22 09:50 E.ven 閱讀(1593) 評論(0)  編輯  收藏

    hibernate緩存

    (一)hibernate數據緩存策略
      緩存是數據庫數據在內存中的臨時容器,它包含了庫表數據在內存中的拷貝,位于數據庫與數據訪問層之間。對于查詢操作相當頻繁的系統(論壇,新聞發布等),良好的緩存機制顯得尤為重要。
      ORM在進行數據讀取時,首先在緩存中查詢,避免了數據庫調用的性能開銷。
    ORM的數據緩存應包含下面幾個層次:
    1)事務級緩存   2)應用級緩存   3)分布式緩存
    具體針對Hibernate而言,采用兩級緩存策略,其過程描述:
    (1)條件查詢的時候,總是發出一條select * from table_name where …. 這樣的SQL語句查詢數據庫,一次獲得所有的數據對象。
    (2) 把獲得的所有數據對象根據ID放入到第二級緩存中。
    (3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,如果配置了二級緩存,那么從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
    (4) 刪除、更新、增加數據的時候,同時更新緩存。
    1. 一級緩存(session level)-數據庫事務級緩存
      1)根據主鍵id加載數據時。 Session.load(), Session.iterate()方法
        2)延遲加載時
        Session內部維護一個數據對象集合,包括了本Session內選取的、操作的數據對象。這稱為Session內部緩存,是Hibernate的第一級最快緩存,屬于Hibernate的既定行為,不需要進行配置(也沒有辦法配置 :-)。
        內部緩存正常情況下由hibernate自動維護,但也可人工干預:
                1) Session.evict (): 將某個特定對象從內部緩存中清除
                2)Session.clear(): 清空內部緩存
    2.二級緩存(SessionFactory level)-應用級緩存
        二級緩存由SessionFactory的所有session實例共享。
    3. 第三方緩存實現
        EHCache, OSCahe
    hibernate批量查詢引起的內存溢出問題
        批量查詢基本不適合使用現有的持久層技術來做,如CMP或hibernate,IBatis倒是可以.
        因為每次調用Session.save()方法時,當前session都會將對象納入到自身的內部緩存中。內部緩存不同于二級緩存,我們可以在二級緩存的配置中指定其最大容量。
    解決方案:
    1)在批處理情況下,關閉Hibernate緩存,如果關閉Hibernate緩存,那么和直接使用JDBC就沒有區別。
    2) 每隔一段時間清空Session內部緩存
      Session實現了異步write-behind,它允許Hibernate顯式地寫操作的批處理。 這里,我給出Hibernate如何實現批量插入的方法: 首先,我們設置一個合理的JDBC批處理大小,hibernate.jdbc.batch_size 20。 然后在一定間隔對Session進行flush()和clear()。
    Session session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    for ( int i=0; i<100000; i++ ) {
    Customer customer = new Customer(.....);
    session.save(customer);
    if ( i % 20 == 0 ) {
    //flush 插入數據和釋放內存:
    session.flush(); session.clear(); }
    }
    tx.commit();
    session.close();
      為了優化性能,可執行批量操作。在傳統的JDBC編程中,批量操作方式如下,將數個SQL操作批量提交:
    PrepareStatement ps=conn.prepareStatement("insert into users(name) values(?)");  
    for(int i=0;i<100000;i++){
      ps.setString(1, "user"+i);
      ps.addBatch();
    }
    int[] counts=ps.executeBatch
    在Hibernate中,可以設置hibernate.jdbc.batch_size 參數來指定每次提交的sql數量。
    hibernate2和hibernate3數據批量刪除機制分析
    1.hibernate2
        Transaction tx=session.beginTransaction();
        session.delete("from users");
        tx.commit();
      觀察日志輸出:
    select ... from users
    Hibernate:delete from users where id=?
    Hibernate:delete from users where id=?
    Hibernate:delete from users where id=?
    ...
        hibernate2版本會首先從數據庫中查詢出所有符合條件的記錄,再對此記錄循環刪除。如果記錄量過大,勢必引起內存溢出和刪除效率問題。ORM為什么要這么做呢?因為ORM為了自動維護內存狀態,必須知道用戶到底對哪些數據進行了操作。問題的解決方法:
    1)內存消耗
        批量刪除前首先從數據庫中查詢出所有符合條件的記錄,如果數據量過大,就會導致 OutOfMemoryError.
        可以采用Session .iterate或Query.iterate方法逐條獲取記錄,再執行delete操作。另外,hibernate2.16后的版本提供了基于游標的數據遍歷操作:
    Transaction tx=session.beginTransaction();
    String hql="from users";
    Query query=session.createQrery(hql);
    ScrollableResults sr=query.scroll();
    while(sr.next()){
    TUser user=(TUser)sr.get(0);
    session.delete();
    }
    tx.commit();
    2)循環刪除的效率問題
      由于hibernate在批量刪除操作過程中,需要反復調用delete SQL,存在性能問題。我們仍然可以通過調整hibernate.jdbc.batch_size參數來解決。
    2.hibernate3
      hibernate3 HQL中引入了 bulk delete/update操作, 即通過一條獨立的sql語句來完成數據的批量操作。
    Transaction tx=session.beginTransaction();
    String hql="delete TUser";
    Query query=session.createQrery(hql);
    int count=query.executeUpdate();
    tx.commit();
    觀察日志輸出:
    Hibernate:delete from TUser

    (二)ibatis數據緩存
      相對Hibernate 等封裝較為嚴密的ORM 實現而言(因為對數據對象的操作實現
    了較為嚴密的封裝,可以保證其作用范圍內的緩存同步,而ibatis 提供的是半封閉
    的封裝實現,因此對緩存的操作難以做到完全的自動化同步)。
      ibatis 的緩存機制使用必須特別謹慎。特別是flushOnExecute 的設定(見
    “ibatis配置”一節中的相關內容),需要考慮到所有可能引起實際數據與緩存數據
    不符的操作。如本模塊中其他Statement對數據的更新,其他模塊對數據的更新,甚
    至第三方系統對數據的更新。否則,臟數據的出現將為系統的正常運行造成極大隱患。
    如果不能完全確定數據更新操作的波及范圍,建議避免Cache的盲目使用。
    1.iBatis cache設置
    sqlmap-config.xml在<sqlMapConfig>里面加入
    <settings
      cacheModelsEnabled="true"
      enhancementEnabled="true"
      lazyLoadingEnabled="true" />
    maps.xml在<sqlMap>里面加入
    <cacheModel   id="userCache"   type="LRU"   readonly="true"   serialize="false">
        <flushInterval hours="24"/>
        <flushOnExecute statement="insertTest"/>
        <property name="size" value="1000" />
    </cacheModel>
    可以看到,Cache有如下幾個比較重要的屬性:readOnly,serialize,type

    readOnly
      readOnly值的是緩存中的數據對象是否只讀。這里的只讀并不是意味著數據對象一
    旦放入緩存中就無法再對數據進行修改。而是當數據對象發生變化的時候,如數據對
    象的某個屬性發生了變化,則此數據對象就將被從緩存中廢除,下次需要重新從數據
    庫讀取數據,構造新的數據對象。
    serialize
      如果需要全局的數據緩存,CacheModel的serialize屬性必須被設為true。否則數據緩存只對當前Session(可簡單理解為當前線程)有效,局部緩存對系統的整體性能提升有限。
    Cache Type:
      與hibernate類似,ibatis通過緩沖接口的插件式實現,提供了多種Cache的實現機制可供選擇:
    1. MEMORY
    2. LRU
    3. FIFO
    4. OSCACHE
    MEMORY類型Cache與WeakReference
        MEMORY 類型的Cache 實現,實際上是通過Java 對象引用進行。ibatis 中,其實現類
    為com.ibatis.db.sqlmap.cache.memory.MemoryCacheController,MemoryCacheController 內部,
    使用一個HashMap來保存當前需要緩存的數據對象的引用。
    LRU型Cache
        當Cache達到預先設定的最大容量時,ibatis會按照“最少使用”原則將使用頻率最少
    的對象從緩沖中清除。可配置的參數有:
    flushInterval:指定了多長時間清除緩存,上例中指定每24小時強行清空緩存區的所有內容。
    size
    FIFO型Cache
    先進先出型緩存,最先放入Cache中的數據將被最先廢除。
    OSCache
    (三)開源數據緩存策略OSCache
    可以解決的問題:
    1)信息系統中需要處理的基礎數據的內容短時間內是不會發生變化的,但是在一個相對長一些的時間里,它卻可能是動態增加或者減少的。
    2)統計報表是一個周期性的工作,可能是半個月、一個月或者更長的時間才會需要更新一次,然而統計報表通常是圖形顯示或者是生成pdf、word、excel等格式的文件,這些圖形內容、文件的生成通常需要消耗很多的系統資源,給系統運行造成很大的負擔。
        OSCache是OpenSymphony組織提供的一個J2EE架構中Web應用層的緩存技術實現組件。OSCache支持對部分頁面內容或者對頁面級的響應內容進行緩存,編程者可以根據不同的需求、不同的環境選擇不同的緩存級別。可以使用內存、硬盤空間、同時使用內存和硬盤或者提供自己的其他資源(需要自己提供適配器)作為緩存區。
    使用步驟:
    1. 下載、解壓縮OSCache
    請到OSCache的主頁http://www.opensymphony.com/oscache/download.html下載Oscache的最新版本,作者下載的是OSCache的最新穩定版本2.0。
    將下載后的。Zip文件解壓縮到c:\oscache(后面的章節中將使用%OSCache_Home%來表示這個目錄)目錄下
    2. 新建立一個web應用
    3. 將主要組件%OSCache_Home%\oscache.jar放入WEB-INF\lib目錄
    4. commons-logging.jar、commons-collections.jar的處理
    •      OSCache組件用Jakarta Commons Logging來處理日志信息,所以需要commons-logging.jar的支持,請將%OSCache_Home%\lib\core\commons-logging.jar放入classpath(通常意味著將這個文件放入WEB-INF\lib目錄)
    •      如果使用JDK1.3,請將%OSCache_Home%\lib\core\commons-collections.jar放入classpath,如果使用JDK1.4或者以上版本,則不需要了
    5. 將oscache.properties、oscache.tld放入WEB-INF\class目錄
    •      %OSCache_Home%\oscache.properties包含了對OSCache運行特征值的設置信息
    •      %OSCache_Home%\oscache.tld包含了OSCache提供的標簽庫的定義內容
    6. 修改web.xml文件
    在web.xml文件中增加下面的內容,增加對OSCache提供的taglib的支持:
    <taglib><taglib-uri>oscache</taglib-uri><taglib-location>/WEB-INF/classes/oscache.tld</taglib-location></taglib>
    7.最簡單的cache標簽用法
    使用默認的關鍵字來標識cache內容,超時時間是默認的3600秒
    <cache:cache><%//自己的JSP代碼內容%></cache:cache>
    8. 緩存單個文件
          在OSCache組件中提供了一個CacheFilter用于實現頁面級的緩存,主要用于對web應用中的某些動態頁面進行緩存,尤其是那些需要生成pdf格式文件/報表、圖片文件等的頁面,不僅減少了數據庫的交互、減少數據庫服務器的壓力,而且對于減少web服務器的性能消耗有很顯著的效果。
    修改web.xml,增加如下內容,確定對/testContent.jsp頁面進行緩存。
    <filter>     <filter-name>CacheFilter</filter-name><filter-class>com.opensymphony.oscache.web.filter.CacheFilter</filter-class></filter><filter-mapping><filter-name>CacheFilter</filter-name><!-對/testContent.jsp頁面內容進行緩存-->     <url-pattern>/testContent.jsp</url-pattern></filter-mapping>

    另一篇:
    很多人對二級緩存都不太了解,或者是有錯誤的認識,我一直想寫一篇文章介紹一下hibernate的二級緩存的,今天終于忍不住了。
    我的經驗主要來自hibernate2.1版本,基本原理和3.0、3.1是一樣的,請原諒我的頑固不化。
    hibernate的session提供了一級緩存,每個session,對同一個id進行兩次load,不會發送兩條sql給數據庫,但是session關閉的時候,一級緩存就失效了。
    二級緩存是SessionFactory級別的全局緩存,它底下可以使用不同的緩存類庫,比如ehcache、oscache等,需要設置hibernate.cache.provider_class,我們這里用ehcache,在2.1中就是
    hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider
    如果使用查詢緩存,加上
    hibernate.cache.use_query_cache=true
    緩存可以簡單的看成一個Map,通過key在緩存里面找value。
    Class的緩存
    對于一條記錄,也就是一個PO來說,是根據ID來找的,緩存的key就是ID,value是POJO。無論list,load還是iterate,只要讀出一個對象,都會填充緩存。但是list不會使用緩存,而iterate會先取數據庫select id出來,然后一個id一個id的load,如果在緩存里面有,就從緩存取,沒有的話就去數據庫load。假設是讀寫緩存,需要設置:
    <cache usage="read-write"/>
    如果你使用的二級緩存實現是ehcache的話,需要配置ehcache.xml
    <cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" />
    其中eternal表示緩存是不是永遠不超時,timeToLiveSeconds是緩存中每個元素(這里也就是一個POJO)的超時時間,如果eternal="false",超過指定的時間,這個元素就被移走了。timeToIdleSeconds是發呆時間,是可選的。當往緩存里面put的元素超過500個時,如果overflowToDisk="true",就會把緩存中的部分數據保存在硬盤上的臨時文件里面。
    每個需要緩存的class都要這樣配置。如果你沒有配置,hibernate會在啟動的時候警告你,然后使用defaultCache的配置,這樣多個class會共享一個配置。
    當某個ID通過hibernate修改時,hibernate會知道,于是移除緩存。
    這樣大家可能會想,同樣的查詢條件,第一次先list,第二次再iterate,就可以使用到緩存了。實際上這是很難的,因為你無法判斷什么時候是第一次,而且每次查詢的條件通常是不一樣的,假如數據庫里面有100條記錄,id從1到100,第一次list的時候出了前50個id,第二次iterate的時候卻查詢到30至70號id,那么30-50是從緩存里面取的,51到70是從數據庫取的,共發送1+20條sql。所以我一直認為iterate沒有什么用,總是會有1+N的問題。
    (題外話:有說法說大型查詢用list會把整個結果集裝入內存,很慢,而iterate只select id比較好,但是大型查詢總是要分頁查的,誰也不會真的把整個結果集裝進來,假如一頁20條的話,iterate共需要執行21條語句,list雖然選擇若干字段,比iterate第一條select id語句慢一些,但只有一條語句,不裝入整個結果集hibernate還會根據數據庫方言做優化,比如使用mysql的limit,整體看來應該還是list快。)
    如果想要對list或者iterate查詢的結果緩存,就要用到查詢緩存了
    查詢緩存
    首先需要配置hibernate.cache.use_query_cache=true
    如果用ehcache,配置ehcache.xml,注意hibernate3.0以后不是net.sf的包名了
    <cache name="net.sf.hibernate.cache.StandardQueryCache"
    maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600"
    timeToLiveSeconds="7200" overflowToDisk="true"/>
    <cache name="net.sf.hibernate.cache.UpdateTimestampsCache"
    maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/>
    然后
    query.setCacheable(true);//激活查詢緩存
    query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion,可選
    第二行指定要使用的cacheRegion是myCacheRegion,即你可以給每個查詢緩存做一個單獨的配置,使用setCacheRegion來做這個指定,需要在ehcache.xml里面配置它:
    <cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" />
    如果省略第二行,不設置cacheRegion的話,那么會使用上面提到的標準查詢緩存的配置,也就是net.sf.hibernate.cache.StandardQueryCache
    對于查詢緩存來說,緩存的key是根據hql生成的sql,再加上參數,分頁等信息(可以通過日志輸出看到,不過它的輸出不是很可讀,最好改一下它的代碼)。
    比如hql:
    from Cat c where c.name like ?
    生成大致如下的sql:
    select * from cat c where c.name like ?
    參數是"tiger%",那么查詢緩存的key*大約*是這樣的字符串(我是憑記憶寫的,并不精確,不過看了也該明白了):
    select * from cat c where c.name like ? , parameter:tiger%
    這樣,保證了同樣的查詢、同樣的參數等條件下具有一樣的key。
    現在說說緩存的value,如果是list方式的話,value在這里并不是整個結果集,而是查詢出來的這一串ID。也就是說,不管是list方法還是iterate方法,第一次查詢的時候,它們的查詢方式很它們平時的方式是一樣的,list執行一條sql,iterate執行1+N條,多出來的行為是它們填充了緩存。但是到同樣條件第二次查詢的時候,就都和iterate的行為一樣了,根據緩存的key去緩存里面查到了value,value是一串id,然后在到class的緩存里面去一個一個的load出來。這樣做是為了節約內存。
    可以看出來,查詢緩存需要打開相關類的class緩存。list和iterate方法第一次執行的時候,都是既填充查詢緩存又填充class緩存的。
    這里還有一個很容易被忽視的重要問題,即打開查詢緩存以后,即使是list方法也可能遇到1+N的問題!相同條件第一次list的時候,因為查詢緩存中找不到,不管class緩存是否存在數據,總是發送一條sql語句到數據庫獲取全部數據,然后填充查詢緩存和class緩存。但是第二次執行的時候,問題就來了,如果你的class緩存的超時時間比較短,現在class緩存都超時了,但是查詢緩存還在,那么list方法在獲取id串以后,將會一個一個去數據庫load!因此,class緩存的超時時間一定不能短于查詢緩存設置的超時時間!如果還設置了發呆時間的話,保證class緩存的發呆時間也大于查詢的緩存的生存時間。這里還有其他情況,比如class緩存被程序強制evict了,這種情況就請自己注意了。
    另外,如果hql查詢包含select字句,那么查詢緩存里面的value就是整個結果集了。
    當hibernate更新數據庫的時候,它怎么知道更新哪些查詢緩存呢?
    hibernate在一個地方維護每個表的最后更新時間,其實也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的緩存配置里面。
    當通過hibernate更新的時候,hibernate會知道這次更新影響了哪些表。然后它更新這些表的最后更新時間。每個緩存都有一個生成時間和這個緩存所查詢的表,當hibernate查詢一個緩存是否存在的時候,如果緩存存在,它還要取出緩存的生成時間和這個緩存所查詢的表,然后去查找這些表的最后更新時間,如果有一個表在生成時間后更新過了,那么這個緩存是無效的。
    可以看出,只要更新過一個表,那么凡是涉及到這個表的查詢緩存就失效了,因此查詢緩存的命中率可能會比較低。
    Collection緩存
    需要在hbm的collection里面設置
    <cache usage="read-write"/>
    假如class是Cat,collection叫children,那么ehcache里面配置
    <cache name="com.xxx.pojo.Cat.children"
    maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200"
    overflowToDisk="true" />
    Collection的緩存和前面查詢緩存的list一樣,也是只保持一串id,但它不會因為這個表更新過就失效,一個collection緩存僅在這個collection里面的元素有增刪時才失效。
    這樣有一個問題,如果你的collection是根據某個字段排序的,當其中一個元素更新了該字段時,導致順序改變時,collection緩存里面的順序沒有做更新。
    緩存策略
    只讀緩存(read-only):沒有什么好說的
    讀/寫緩存(read-write):程序可能要的更新數據
    不嚴格的讀/寫緩存(nonstrict-read-write):需要更新數據,但是兩個事務更新同一條記錄的可能性很小,性能比讀寫緩存好
    事務緩存(transactional):緩存支持事務,發生異常的時候,緩存也能夠回滾,只支持jta環境,這個我沒有怎么研究過
    讀寫緩存和不嚴格讀寫緩存在實現上的區別在于,讀寫緩存更新緩存的時候會把緩存里面的數據換成一個鎖,其他事務如果去取相應的緩存數據,發現被鎖住了,然后就直接取數據庫查詢。
    在hibernate2.1的ehcache實現中,如果鎖住部分緩存的事務發生了異常,那么緩存會一直被鎖住,直到60秒后超時。
    不嚴格讀寫緩存不鎖定緩存中的數據。
    使用二級緩存的前置條件
    你的hibernate程序對數據庫有獨占的寫訪問權,其他的進程更新了數據庫,hibernate是不可能知道的。你操作數據庫必需直接通過hibernate,如果你調用存儲過程,或者自己使用jdbc更新數據庫,hibernate也是不知道的。hibernate3.0的大批量更新和刪除是不更新二級緩存的,但是據說3.1已經解決了這個問題。
    這個限制相當的棘手,有時候hibernate做批量更新、刪除很慢,但是你卻不能自己寫jdbc來優化,很郁悶吧。
    SessionFactory也提供了移除緩存的方法,你一定要自己寫一些JDBC的話,可以調用這些方法移除緩存,這些方法是:
    void evict(Class persistentClass)
    Evict all entries from the second-level cache.
    void evict(Class persistentClass, Serializable id)
    Evict an entry from the second-level cache.
    void evictCollection(String roleName)
    Evict all entries from the second-level cache.
    void evictCollection(String roleName, Serializable id)
    Evict an entry from the second-level cache.
    void evictQueries()
    Evict any query result sets cached in the default query cache region.
    void evictQueries(String cacheRegion)
    Evict any query result sets cached in the named query cache region.
    不過我不建議這樣做,因為這樣很難維護。比如你現在用JDBC批量更新了某個表,有3個查詢緩存會用到這個表,用evictQueries(String cacheRegion)移除了3個查詢緩存,然后用evict(Class persistentClass)移除了class緩存,看上去好像完整了。不過哪天你添加了一個相關查詢緩存,可能會忘記更新這里的移除代碼。如果你的jdbc代碼到處都是,在你添加一個查詢緩存的時候,還知道其他什么地方也要做相應的改動嗎?
    ----------------------------------------------------
    總結:
    不要想當然的以為緩存一定能提高性能,僅僅在你能夠駕馭它并且條件合適的情況下才是這樣的。hibernate的二級緩存限制還是比較多的,不方便用jdbc可能會大大的降低更新性能。在不了解原理的情況下亂用,可能會有1+N的問題。不當的使用還可能導致讀出臟數據。
    如果受不了hibernate的諸多限制,那么還是自己在應用程序的層面上做緩存吧。
    在越高的層面上做緩存,效果就會越好。就好像盡管磁盤有緩存,數據庫還是要實現自己的緩存,盡管數據庫有緩存,咱們的應用程序還是要做緩存。因為底層的緩存它并不知道高層要用這些數據干什么,只能做的比較通用,而高層可以有針對性的實現緩存,所以在更高的級別上做緩存,效果也要好些吧。
    轉自:http://blog.ccidnet.com/blog-htm-do-showone-uid-44291-type-blog-itemid-125551.html


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

    posts - 27, comments - 15, trackbacks - 0, articles - 1

    Copyright © E.ven

    主站蜘蛛池模板: 久久久久久夜精品精品免费啦 | 亚洲人成人网站在线观看| 亚洲国产精品综合久久网各| 久久精品视频免费| 亚洲国产成人高清在线观看| 亚洲男人的天堂一区二区| 成人婷婷网色偷偷亚洲男人的天堂 | 久久精品国产影库免费看| 亚洲成A人片在线观看WWW| 国产免费一区二区三区在线观看| 亚洲精品午夜国产VA久久成人| 精品成人免费自拍视频| 亚洲AV成人片色在线观看| 免费无毒a网站在线观看| 97热久久免费频精品99| 亚洲精品成人网站在线播放| 国产在线观看麻豆91精品免费| 精品亚洲成A人无码成A在线观看| 成人黄18免费视频| 国产精品无码亚洲一区二区三区| 亚洲情侣偷拍精品| 午夜网站在线观看免费完整高清观看| 亚洲电影中文字幕| 成年女人毛片免费播放人| 国产亚洲午夜精品| 久久噜噜噜久久亚洲va久| 亚洲精品视频在线观看免费| 亚洲精品天堂成人片AV在线播放| 人妻无码久久一区二区三区免费| 亚洲一级大黄大色毛片| 免费女人18毛片a级毛片视频| 国产免费一级高清淫曰本片| 亚洲春黄在线观看| 亚洲av无码成人精品区在线播放| 久久99毛片免费观看不卡| 亚洲图片校园春色| 四虎影院在线免费播放| 亚洲人成毛片线播放| 中国xxxxx高清免费看视频| 亚洲国产日韩a在线播放| 亚洲精品无码久久久影院相关影片 |