當前位置:首頁 » 硬碟大全 » 有分頁的介面如何緩存好
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

有分頁的介面如何緩存好

發布時間: 2023-04-29 18:59:27

Ⅰ 基於redis做緩存分頁

在實際業務中我們會將一些熱空兄數據緩存到redis裡面,這時候數據量比較大的話,我們就要對這些熱數據進行分頁,分頁的方式有2種:

第一:從redis拿出所有數據後,再做內存分頁(不推薦),熱點數據小的時候可以這樣做,性能相差不是很大,但是當數據量大的時候,分頁期間就會佔用大量內存,或撐爆;

第二:基於redis的數據結構做緩存分頁,這里又分2種

①:基於redis的list數據結構,直接通過list的數據結構,用range方法可以進行分頁,在數據量大的時候,性能也很可觀,但是當存在介面高並發訪問時,這個list可能會無限延長,且裡面的數據會存在很多重復,這就會影響到正常的業務(不是很推薦);

②:基於redis的ZSet數據結構,通過Zset這個有序集合我們也可以做分頁,同樣也是用range方法,但是這里比較麻煩的是在初始化數據的時候Zset必須存放TypedTuple類型的數據,這個類型是一個value和score的鍵值對,具體可以查網路,這個score的生成比較麻煩我這邊測試時用的是當前數據在這個list的位置,然後Zset是根據這個score值來排序的,默認是從小到大;用這個的好處是,即使在高並發情況下Zset中也不會存在重復數據從而影響正常的業務;而且分頁效率也和list結構差不多;

③:用hash和Zset來一起實現;這個是問了一個朋友和得知的,Zset中存儲有序灶高的id欄位,通過分頁後拿到id,然後再用id去hash中取,感覺應該效率相差不大的,只是中間多了層從hash結構取,還需要維護又一個hash;(為何這樣做我也不清楚);

貼一張我斗辯襲測試list和ZSet的結果圖

Ⅱ java 緩存分頁

可以查詢的時候就查五條呀,這樣不用每次查出所有的,而是根據當前是第幾頁來查詢當前的幾條。

Ⅲ php 分頁查詢怎麼redis緩存

對於有分頁條件的緩存,我們也可以按照不同的分頁條件來緩存多個key,比如分頁查詢產品列表,page=1&limit=10和page=1&limit=5這兩次請求可以這樣緩存查詢結果

proctList:page:1:limit:10

proctList:page:1:limit:5
這個是一種常見方案,但是存在著一些問題:

緩存的value存在冗餘,proctList:page:1:limit:10緩存的內容其實是包括了proctList:page:1:limit:5中的內容(緩存兩個key的時候,數據未發生變化的情況下)

僅僅是改變了查詢條件的分頁條件,就會導致緩存未命中,降低了緩存的命中率

為了保證數據一致性,需要清理緩存的時候,很難處理,redis的keys命令對性能影響很大,會導致redis很大的延遲,生產環境一般來說禁止該命令。自己手動拼緩存key,你可能根本不知道拼到哪一個page為止。

放棄數據一致性,通過設置失效時間來自動失效,可能會出現查詢第一頁命中了緩存,查詢第二頁的時候未命中緩存,但此時數據已經發生了改變,導致第二頁查詢返回的和第一頁相同的結果。
以上,在分頁條件下這樣使用常規方案總感覺有諸多困擾,諸多麻煩,那是不是就應該放棄使用緩存?
基於SortedSet的分頁查詢緩存方案
首先想到的解決方法是使用@see ListOperations<K, V>不再根據分頁條件使用多個key,而是使用一個key,也不分頁將全部的數據緩存到redis中,然後按照分頁條件使用range(key,start,limit)獲取分頁的結果,這個會導致一個問題,當緩存失效時,並發的寫緩存會導致出現重復數據
所以想到通過使用set來處理並發時的重復數據,@see ZSetOperations<K, V>
代碼邏輯如下:

range(key,start,limit)按照分頁條件獲取緩存,命中則直接返回

緩存未命中,查詢(沒有分頁條件)資料庫或是調用(沒有分頁)底層介面

add(key,valueScoreMap<value,score>)寫入緩存,expire設置緩存時間

當需要清理緩存時,直接刪除key,如果是因為數據新增和刪除,可以add(key,value,score)或remove(key,value)

redis中會按照score分值升序排列map中的數據,一般的,score分值是sql語句的order by filedA的filedA的值,這樣能保證數據一致性
但是這種方式也存在一定問題:

這個key緩存的value確實是熱數據,但可能只有少數數據被頻繁使用其餘的可能根本就未被使用,比如數據有100頁,實際可能只會用到前10頁,這也會導致緩存空間的浪費,如果使用了redis虛擬內存,也會有一定影響

sql查詢由原來的分頁查詢變成了不分頁查詢,緩存失效後,系統的處理能力較之前會有下降,尤其是對於大表.

Ⅳ java 當查詢有很多條數據時用什麼緩存比較好,或者說怎麼來提高他的性能

分頁查詢啊,頁面最大條數可以限制。還有就是優化SQL,別全表ORDER BY,那樣要慢死。

Ⅳ 關於datalist分頁緩存

分頁的一個目的就是每次載入一部分數據,你一下在把數據都讀到內存中分頁的優勢不久沒有了嗎,如果數據量小當然可以或許放在緩存中比每次查詢還要快,但數據量要大你的伺服器就承受不住了

Ⅵ redis 怎麼緩存用戶列表,做到可以分頁展示

redis是類似key_value形式的快速緩存服務。類型較豐富,可以保存對象、列表等,支持的操作也很豐富,屬於內存資料庫,且可以把內存中的數據及時或定時的寫入到磁碟。可設置過期自動刪除,速度快,易於使用。

Ⅶ 數據量太大,分頁查詢變慢,有什麼優化查詢的方法嗎

下面以關系資料庫系統Informix為例,介紹改善用戶查詢計劃的方法。

1.合理使用索引

索引是資料庫中重要的數據結構,它的根本目的就是為了提高查詢效率。現在大多數的資料庫產品都採用IBM最先提出的ISAM索引結構。索引的使用要恰到好處,其使用原則如下:

●在經常進行連接,但是沒有指定為外鍵的列上建立索引,而不經常連接的欄位則由優化器自動生成索引。

●在頻繁進行排序或分組(即進行group by或order by操作)的列上建立索引。

●在條件表達式中經常用到的不同值較多的列上建立檢索,在不同值少的列上不要建立索引。比如在雇員表的「性別」列上只有「男」與「女」兩個不同值,因此就無必要建立索引。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。

●如果待排序的列有多個,可以在這些列上建立復合索引(compound index)。

●使用系統工具。如Informix資料庫有一個tbcheck工具,可以在可疑的索引上進行檢查。在一些資料庫伺服器上,索引可能失效或者因為頻繁操作而使得讀取效率降低,如果一個使用索引的查詢不明不白地慢下來,可以試著用tbcheck工具檢查索引的完整性,必要時進行修復。另外,當資料庫表更新大量數據後,刪除並重建索引可以提高查詢速度。

2.避免或簡化排序

應當簡化或避免對大型表進行重復的排序。當能夠利用索引自動以適當的次序產生輸出時,優化器就避免了排序的步驟。以下是一些影響因素:

●索引中不包括一個或幾個待排序的列;

●group by或order by子句中列的次序與索引的次序不一樣;

●排序的列來自不同的表。

為了避免不必要的排序,就要正確地增建索引,合理地合並資料庫表(盡管有時可能影響表的規范化,但相對於效率的提高是值得的)。如果排序不可避免,那麼應當試圖簡化它,如縮小排序的列的范圍等。

3.消除對大型錶行數據的順序存取

在嵌套查詢中,對表的順序存取對查詢效率可能產生致命的影響。比如採用順序存取策略,一個嵌套3層的查詢,如果每層都查詢1000行,那麼這個查詢就要查詢10億行數據。避免這種情況的主要方法就是對連接的列進行索引。例如,兩個表:學生表(學號、姓名、年齡……)和選課表(學號、課程號、成績)。如果兩個表要做連接,就要在「學號」這個連接欄位上建立索引。

還可以使用並集來避免順序存取。盡管在所有的檢查列上都有索引,但某些形式的where子句強迫優化器使用順序存取。下面的查詢將強迫對orders表執行順序操作:

SELECT * FROM orders WHERE (customer_num=104 AND order_num>1001) OR order_num=1008

雖然在customer_num和order_num上建有索引,但是在上面的語句中優化器還是使用順序存取路徑掃描整個表。因為這個語句要檢索的是分離的行的集合,所以應該改為如下語句:

SELECT * FROM orders WHERE customer_num=104 AND order_num>1001

UNION

SELECT * FROM orders WHERE order_num=1008

這樣就能利用索引路徑處理查詢。

4.避免相關子查詢

一個列的標簽同時在主查詢和where子句中的查詢中出現,那麼很可能當主查詢中的列值改變之後,子查詢必須重新查詢一次。查詢嵌套層次越多,效率越低,因此應當盡量避免子查詢。如果子查詢不可避免,那麼要在子查詢中過濾掉盡可能多的行。

5.避免困難的正規表達式

MATCHES和LIKE關鍵字支持通配符匹配,技術上叫正規表達式。但這種匹配特別耗費時間。例如:SELECT * FROM customer WHERE zipcode LIKE 「98_ _ _」

即使在zipcode欄位上建立了索引,在這種情況下也還是採用順序掃描的方式。如果把語句改為SELECT * FROM customer WHERE zipcode >「98000」,在執行查詢時就會利用索引來查詢,顯然會大大提高速度。

另外,還要避免非開始的子串。例如語句:SELECT * FROM customer WHERE zipcode[2,3]>「80」,在where子句中採用了非開始子串,因而這個語句也不會使用索引。

6.使用臨時表加速查詢

把表的一個子集進行排序並創建臨時表,有時能加速查詢。它有助於避免多重排序操作,而且在其他方面還能簡化優化器的工作。例如:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

AND cust.postcode>「98000」

ORDER BY cust.name

如果這個查詢要被執行多次而不止一次,可以把所有未付款的客戶找出來放在一個臨時文件中,並按客戶的名字進行排序:

SELECT cust.name,rcvbles.balance,……other columns

FROM cust,rcvbles

WHERE cust.customer_id = rcvlbes.customer_id

AND rcvblls.balance>0

ORDER BY cust.name

INTO TEMP cust_with_balance

然後以下面的方式在臨時表中查詢:

SELECT * FROM cust_with_balance

WHERE postcode>「98000」

臨時表中的行要比主表中的行少,而且物理順序就是所要求的順序,減少了磁碟I/O,所以查詢工作量可以得到大幅減少。

注意:臨時表創建後不會反映主表的修改。在主表中數據頻繁修改的情況下,注意不要丟失數據。

7.用排序來取代非順序存取

非順序磁碟存取是最慢的操作,表現在磁碟存取臂的來回移動。SQL語句隱藏了這一情況,使得我們在寫應用程序時很容易寫出要求存取大量非順序頁的查詢。

有些時候,用資料庫的排序能力來替代非順序的存取能改進查詢。

Ⅷ vue頁面緩存(keepAlive)

同人博客搬遷~~~~(播客主頁: https://www.cnblogs.com/epines/ )

頁面緩存在頁面中長期會使用到,可以更快速的在頁面切換期間的資源獲取

主要是用keep-alive實現

在vue項目中,相關的寫法比較多,還有一些注意點需要仔細

第一種方式

在App.vue中

添加標簽

  <keep-alive>

      <router-view />

  </keep-alive>

這會就是所有的頁面都會被緩存

這里做了兩個頁面的相互跳轉,分別寫了一個輸入框,在輸入內容後,跳轉時,輸入的內容因為緩存的原因會被保留

 如果存在某些頁面需要緩存,那麼可以通過keep-alive的屬性去處理

其中就是include和exclude

include:名稱匹配的組件才會被緩存,其中可以寫字元串或正則表達式

exclude:名稱匹配的組件不會被緩存,其中同樣是字元串或正則表達式

這里的名稱是指組件的名稱

<script>

export default {

  name: 'HelloWorld'

}

</script>

     第二種方式:

     在路由中進行設置通過添加meta,route/index.js     

export default new Router({

  routes: [{

      path: '/',

      name: 'HelloWorld',

      component: HelloWorld,

      meta: {

        keepAlive: true // 該路由會被緩存

      }

    },

    {

      path: '/ss',

      name: 'ss',

      component: Ss,

meta: {        keepAlive:false // 該路由不會被緩存,不需要緩存的時候該屬性可以不用寫      }

}]

})

這時候頁面還需要通過該屬性進行判斷是否緩存

在App.vue  

  <keep-alive>

      <router-view v-if="$route.meta.keepAlive">

      </router-view>

    </keep-alive>

    <router-view v-if="!$route.meta.keepAlive">

    </router-view>

這樣寫有個優點就是,需要緩存不需要緩存的name可以隨便寫,不需要做什麼規律性的去寫出一個合適的正則去匹配上,就會更加靈活些

 常見的應用場景可以是,列表到詳情頁,從詳情頁返回到列表頁,如果說列表頁沒有做緩存,在單頁面下,會直接返回列表首頁(存在分頁的情況),就不能直接返回至之前離開的列表頁,所以這個地方在列表頁添加頁面緩存後,可以做到返回至之前離開的列表頁

 沒有緩存的時候,返回列表:

 有緩存的時候,返回列表

所以從某些程度上來講,即增加了頁面的響應速度,又增加了用戶體驗,總體來說,還是比較實用的

Ⅸ 緩存分頁合適嗎

一種是使用本地緩存、另一種是分級緩存。這里談一談原設計的缺陷,分級緩存中我提出來通過確定兩個不同size的緩存塊來緩存兩種級別的數據,這里帶來一些問題:size的大小難以確定、為了避免邊界問題大緩存數據包含了小緩存數據這就帶來了緩存數據的冗餘(這背離了我們設計的初衷)。針對這些問題我們又在原有基礎上結合了應用場景的特殊性修改分級緩存為分頁緩存(因為對數據列表的訪問往往都是伴隨分頁需求的),將資料庫中原始數據中較常使用部分按照固定大小的頁進行緩存,服務端根據客戶端分頁的數據請求到相應的緩存頁內查找數據進行填充。採用分頁緩存一方面解決了緩存數據冗餘的問題,也不用關注分級的邊界,雖然相比分級緩存,分頁的內容要零散一些,但是總體上而言靈活性要更高。這里談談為什麼採用固定大小頁進行緩存而不是按照客戶端分頁請求來緩存結果?如果服務端根據客戶端分頁請求進行緩存這種耦合關系會導致緩存命中率的下、降性能降低,特別是多類型客戶端就更糟糕了。按照固定大小頁進行緩存類似與MVC模式中將處理邏輯與顯示邏輯解耦的思想,服務端的緩存不要依賴客戶端,一方面提高了緩存命中率同時也為緩存清理提供了遍歷。 下面是我使用IL動態生成的一個Demo反編譯後的代碼(這里針對了同時啟用本地緩存和分頁緩存的情況,還支持分頁緩存無本地緩存、僅進行memcache緩存,這里就不加贅述了),可讀性不高不想看直接pass吧。 C#代碼 public override ListObject GetList(int num5, int num6, int num1, int num4) { ListObject local; int num = num1; int num2 = num4; int num3 = ((num1 - 1) * num4) % 100; num1 = (((num1 - 1) * num4) / 100) + 1;//計算緩存頁對應的頁碼和頁大小 num4 = 100; if (num1 > 10)//不在緩存頁內直接進行資料庫查詢 { return base.GetList(num5, num6, num, num2); } ListObject obj3 = new ListObject(); do { string str = string.Concat(new object[] { "DemoCachekeyName", "|", num5, "|", num6, "|", num1, "|", num4 });//根據客戶端分頁請求計算出對應的cachekey local = CacheManager.get_Instance().GetLocal(str) as ListObject;//LocalCache的訪問 if (local == null) { DateTime time; local = CacheManager.get_Instance().Get(str) as ListObject;//訪問memcache if (local == null) { local = base.GetList(num5, num6, num1, num4); if (local != null) { time = DateTime.Now.AddSeconds(3600.0); CacheManager.get_Instance().Set(str, local, time); } } if (local != null) { time = DateTime.Now.AddSeconds(100.0); CacheManager.get_Instance().SetLocal(str, local, time); } } num1++; } while (((num1 (obj3, num2, num3, local));//填充結果集 if (obj3.totalCount == 0) { return null; } return obj3; } public override ListObject GetList(int num5, int num6, int num1, int num4){ ListObject local; int num = num1; int num2 = num4; int num3 = ((num1 - 1) * num4) % 100; num1 = (((num1 - 1) * num4) / 100) + 1;//計算緩存頁對應的頁碼和頁大小 num4 = 100; if (num1 > 10)//不在緩存頁內直接進行資料庫查詢 { return base.GetList(num5, num6, num, num2); } ListObject obj3 = new ListObject(); do { string str = string.Concat(new object[] { "DemoCachekeyName", "|", num5, "|", num6, "|", num1, "|", num4 });//根據客戶端分頁請求計算出對應的cachekey local = CacheManager.get_Instance().GetLocal(str) as ListObject;//LocalCache的訪問 if (local == null) { DateTime time; local = CacheManager.get_Instance().Get(str) as ListObject;//訪問memcache if (local == null) { local = base.GetList(num5, num6, num1, num4); if (local != null) { time = DateTime.Now.AddSeconds(3600.0); CacheManager.get_Instance().Set(str, local, time); } } if (local != null) { time = DateTime.Now.AddSeconds(100.0); CacheManager.get_Instance().SetLocal(str, local, time); } } num1++; } while (((num1 (obj3, num2, num3, local));//填充結果集 if (obj3.totalCount == 0) { return null; } return obj3;}(下次再完善這里的IL代碼的流程圖,一直想在緩存結果中再織入進一些過濾操作思前想後沒想到如何在不污染原有介面的前提下實現,正在努力中...) PS:關於IL代碼編寫:IL代碼因為是一種中間代碼可讀性不是很高,所以進行IL編碼其實還是有一點難度的(學習IL編碼可以參看《IL Emit學習之旅》一問)。我簡單談談我在編寫IL代碼中遇到的一些小問題和自己總結的一些技巧。 1.先編寫c#代碼的demo,再參照其IL指令,先完成代碼框架,在進一步編碼。在IL編碼前可以先寫一個目標生成的動態代碼,再通過參照其IL代碼進編碼,先用IL寫出的主體邏輯(即if else、for、while等),再進一步完善。這樣逐步編碼查錯和編碼效率都相對高一點。 2.什麼時候用「_S」,IL代碼中為了縮減指令長度對於某些同一操作提供了兩種指令實現,比如無條件跳轉有Br、Br_S,有時候使用Br_S跳轉目標地址會被截斷導致程序出錯。我個人覺得可以先在可能出現這類情況的地方使用不帶「_S」的指令,待動態代碼生成後查看其IL代碼,再對指令進行優化。 關於泛型函數的反射: IL代碼中常常需要調用函數,這就需要使用到反射(第一次生成動態代碼時的反射對整體的性能影響還是可以接受的)。泛型函數的反射還是稍稍有些繞的: C#代碼 //目標函數public static bool FillResult(...) MethodInfo fillResult= typeof(PageFormatUtil).GetMethod("FillResult"); fillResult=fillResult.MakeGenericMethod(info.ReturnParameter.ParameterType.GetGenericArguments()[0]);//info.ReturnParameter.ParameterType.GetGenericArguments()獲取函數返回結果中泛型參數的信息,MakeGenericMethod之後才是真正完成了泛型函數的反射 //目標函數public static bool FillResult(...)MethodInfo fillResult= typeof(PageFormatUtil).GetMethod("FillResult");fillResult=fillResult.MakeGenericMethod(info.ReturnParameter.ParameterType.GetGenericArguments()[0]);//info.ReturnParameter.ParameterType.GetGenericArguments()獲取函數返回結果中泛型參數的信息,MakeGenericMethod之後才是真正完成了泛型函數的反射C#代碼 //public class ListObject{...} reflectType=typeof(ListObject).MakeGenericType(info.ReturnParameter.ParameterType.GetGenericArguments()[0]); reflectConstruct=reflectType.GetConstructor(new Type[]{}); //public class ListObject{...}reflectType=typeof(ListObject).MakeGenericType(info.ReturnParameter.ParameterType.GetGenericArguments()[0]);reflectConstruct=reflectType.GetConstructor(new Type[]{});緩存分頁合適嗎