Artikel ini membawa anda pengetahuan yang berkaitan tentang Redis Ia terutamanya memperkenalkan kemahiran penggunaan cache yang diedarkan dan cache setempat, termasuk pengenalan kepada jenis cache, pelbagai senario penggunaan dan Cara menggunakannya, dan akhirnya. kes praktikal akan diberikan. Mari kita lihat.
Pembelajaran yang disyorkan: Tutorial video Redis
Seperti yang kita sedia maklum, tujuan utama caching adalah untuk mempercepatkan akses dan melegakan tekanan pangkalan data. Cache yang paling biasa digunakan ialah cache yang diedarkan, seperti redis Apabila berhadapan dengan kebanyakan senario bersamaan atau situasi di mana trafik beberapa syarikat kecil dan sederhana tidak begitu tinggi, menggunakan redis pada asasnya boleh menyelesaikan masalah. Walau bagaimanapun, dalam kes trafik tinggi, anda mungkin perlu menggunakan cache setempat, seperti LoadingCache jambu batu dan sumber terbuka ReloadableCache Kuaishou.
Bahagian ini akan memperkenalkan senario penggunaan dan had redis, seperti LoadingCache jambu batu dan sumber terbuka Kuaishou ReloadableCache Melalui pengenalan dalam bahagian ini, anda boleh mengetahui caranya untuk menggunakannya. Cache yang manakah harus digunakan dalam senario perniagaan dan sebabnya.
Jika kita secara meluas bercakap tentang masa untuk menggunakan redis, maka ia secara semula jadi akan digunakan di tempat yang bilangan lawatan pengguna terlalu tinggi, sekali gus mempercepatkan akses dan mengurangkan tekanan pangkalan data. Jika dipecahkan, ia boleh dibahagikan kepada masalah nod tunggal dan masalah nod bukan tunggal.
Jika halaman mempunyai bilangan lawatan pengguna yang tinggi, tetapi mereka tidak mengakses sumber yang sama. Sebagai contoh, halaman butiran pengguna mempunyai bilangan lawatan yang agak tinggi, tetapi data setiap pengguna adalah berbeza Dalam kes ini, jelas bahawa hanya cache yang diedarkan boleh digunakan Jika redis digunakan, kuncinya adalah unik pengguna kunci, dan nilainya ialah maklumat pengguna.
Pecahan cache yang disebabkan oleh redis.
Tetapi satu perkara yang perlu diambil perhatian ialah masa tamat tempoh mesti ditetapkan, dan ia tidak boleh ditetapkan untuk tamat tempoh pada masa yang sama. Sebagai contoh, jika pengguna mempunyai halaman aktiviti, halaman aktiviti boleh melihat data anugerah pengguna semasa aktiviti Orang yang cuai boleh menetapkan titik masa tamat data pengguna ke penghujung aktiviti, yang akan
Isu tunggal (Panas)
Masalah nod tunggal merujuk kepada masalah konkurensi satu nod redis, kerana kunci yang sama akan jatuh pada nod yang sama kelompok redis, jadi jika kuncinya adalah Jika bilangan lawatan terlalu tinggi, maka nod redis ini akan mempunyai risiko serentak, dan kunci ini dipanggil kunci panas. Jika semua pengguna mengakses sumber yang sama, contohnya, halaman utama Apl Xiao Ai memaparkan kandungan yang sama kepada semua pengguna (peringkat awal), dan pelayan mengembalikan json besar yang sama kepada h5, jelas ia mesti digunakan untuk cache. Pertama, kami mempertimbangkan sama ada ia boleh digunakan untuk menggunakan redis Memandangkan redis mempunyai masalah titik tunggal, jika trafik terlalu besar, maka semua permintaan pengguna akan mencapai nod redis yang sama, dan adalah perlu untuk menilai sama ada nod boleh bertahan. aliran yang begitu besar. Peraturan kami ialah jika qps satu nod mencapai seribu tahap, masalah satu titik mesti diselesaikan (walaupun redis mendakwa dapat menahan qps seratus ribu tahap), cara yang paling biasa ialah menggunakan cache tempatan . Jelas sekali, trafik pada halaman utama apl Xiaoai adalah kurang daripada 100, jadi tiada masalah menggunakan redis. Senario penggunaan dan had LoadingCacheUntuk masalah kunci panas yang disebutkan di atas, pendekatan kami yang paling langsung ialah menggunakan cache setempat, seperti LoadingCache jambu batu yang paling anda kenali, tetapi gunakan cache tempatan Cache diperlukan untuk dapat menerima sejumlah data kotor, kerana jika anda mengemas kini halaman utama, cache tempatan tidak akan dikemas kini Ia hanya akan memuat semula cache mengikut dasar tamat tempoh tertentu, tetapi dalam kami senario ia baik sepenuhnya, kerana sebaik sahaja halaman utama ditolak di latar belakang, ia tidak akan diubah lagi. Walaupun ia berubah, tiada masalah Anda boleh menetapkan tamat tempoh tulis kepada setengah jam, dan muat semula cache selepas setengah jam Kami boleh menerima data kotor dalam tempoh yang singkat.Pecahan cache yang disebabkan oleh LoadingCache
Walaupun cache setempat sangat berkaitan dengan mesin, walaupun tahap kod ditulis untuk tamat tempoh dalam masa setengah jam, kerana setiap mesin Masa permulaan yang berbeza membawa kepada masa pemuatan cache yang berbeza dan masa tamat tempoh yang berbeza Oleh itu, semua permintaan pada mesin tidak akan meminta pangkalan data selepas cache tamat pada masa yang sama. Walau bagaimanapun, penembusan cache juga akan berlaku untuk satu mesin Jika terdapat 10 mesin, setiap satu dengan 1,000 qps, selagi satu cache tamat tempoh, 1,000 permintaan ini boleh memukul pangkalan data pada masa yang sama. Masalah seperti ini sebenarnya lebih mudah untuk diselesaikan, tetapi ia mudah untuk diabaikan, iaitu, apabila menyediakan LoadingCache, gunakan kaedah load-miss LoadingCache dan bukannya menilai terus cache.getIfPresent()== null dan kemudian meminta. db; yang pertama akan menambah mesin maya Kunci lapisan memastikan bahawa hanya satu permintaan pergi ke pangkalan data, dengan itu menyelesaikan masalah ini dengan sempurna. Walau bagaimanapun, jika terdapat keperluan masa nyata yang tinggi, seperti aktiviti yang kerap untuk satu tempoh masa, saya ingin memastikan halaman aktiviti boleh dikemas kini dalam hampir masa nyata, iaitu selepas operasi mengkonfigurasi maklumat aktiviti di latar belakang, ia perlu Memaparkan maklumat aktiviti yang dikonfigurasikan dalam hampir masa nyata di sebelah C, menggunakan LoadingCache pastinya tidak mencukupi.Untuk masalah masa nyata yang disebutkan di atas yang tidak dapat diselesaikan oleh LoadingCache, anda boleh mempertimbangkan untuk menggunakan ReloadableCache, iaitu rangka kerja cache tempatan yang bersumberkan Kuaishou ialah ia menyokong berbilang mesin pada masa yang sama Kemas kini cache Katakan kita mengubah suai maklumat halaman utama, dan kemudian permintaan itu memukul mesin A. Pada masa ini, ReloadableCache dimuat semula, dan kemudian ia akan menghantar pemberitahuan mesin yang mendengar nod zk yang sama akan mengemas kini cache selepas menerima pemberitahuan. Keperluan umum untuk menggunakan cache ini adalah untuk memuatkan keseluruhan jumlah data ke dalam cache setempat, jadi jika jumlah data terlalu besar, ia pasti akan memberi tekanan pada gc, dan ia tidak boleh digunakan dalam kes ini. Memandangkan halaman utama Xiao Ai mempunyai status, dan secara amnya hanya terdapat dua status dalam talian, anda boleh menggunakan ReloadableCache untuk memuatkan hanya halaman utama status dalam talian.
Pada ketika ini, tiga jenis cache pada asasnya telah diperkenalkan Berikut adalah ringkasan:
semasa menggunakan binaan Pada masa ini, anda boleh menggunakan get() secara langsung. Selain itu, disyorkan untuk menggunakan load-miss dan bukannya menyemak pangkalan data apabila getIfPresent==null, yang boleh menyebabkan kerosakan cache
V get(K key, Callable<? extends V> loader)
build(CacheLoader<? super K1, V1> loader)
LoadingCache<String, String> cache = CacheBuilder.newBuilder() .maximumSize(1000L) .expireAfterAccess(Duration.ofHours(1L)) // 多久不访问就过期 .expireAfterWrite(Duration.ofHours(1L)) // 多久这个key没修改就过期 .build(new CacheLoader<String, String>() { @Override public String load(String key) throws Exception { // 数据装载方式,一般就是loadDB return key + " world"; } }); String value = cache.get("hello"); // 返回hello world
<dependency> <groupId>com.github.phantomthief</groupId> <artifactId>zknotify-cache</artifactId> <version>0.1.22</version> </dependency>
Ketiga-tiga ini benar-benar masalah kekal dan anda benar-benar perlu mempertimbangkannya jika trafik adalah besar.
public interface ReloadableCache<T> extends Supplier<T> { /** * 获取缓存数据 */ @Override T get(); /** * 通知全局缓存更新 * 注意:如果本地缓存没有初始化,本方法并不会初始化本地缓存并重新加载 * * 如果需要初始化本地缓存,请先调用 {@link ReloadableCache#get()} */ void reload(); /** * 更新本地缓存的本地副本 * 注意:如果本地缓存没有初始化,本方法并不会初始化并刷新本地的缓存 * * 如果需要初始化本地缓存,请先调用 {@link ReloadableCache#get()} */ void reloadLocal(); }
Cache tempatan menggunakan kaedah terlepas beban
Pelaksanaan perniagaan
import com.google.common.collect.Lists; import org.apache.commons.collections4.CollectionUtils; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; /** * @Description 分布式加载缓存的rpc服务,如果部署了多台机器那么调用端最好使用id做一致性hash保证相同id的请求打到同一台机器。 **/ public abstract class AbstractCacheSetterService implements CacheSetterService { private final ConcurrentMap<String, CountDownLatch> loadCache = new ConcurrentHashMap<>(); private final Object lock = new Object(); @Override public void load(Collection<String> needLoadIds) { if (CollectionUtils.isEmpty(needLoadIds)) { return; } CountDownLatch latch; Collection<CountDownLatch> loadingLatchList; synchronized (lock) { loadingLatchList = excludeLoadingIds(needLoadIds); needLoadIds = Collections.unmodifiableCollection(needLoadIds); latch = saveLatch(needLoadIds); } System.out.println("needLoadIds:" + needLoadIds); try { if (CollectionUtils.isNotEmpty(needLoadIds)) { loadCache(needLoadIds); } } finally { release(needLoadIds, latch); block(loadingLatchList); } } /** * 加锁 * @param loadingLatchList 需要加锁的id对应的CountDownLatch */ protected void block(Collection<CountDownLatch> loadingLatchList) { if (CollectionUtils.isEmpty(loadingLatchList)) { return; } System.out.println("block:" + loadingLatchList); loadingLatchList.forEach(l -> { try { l.await(); } catch (InterruptedException e) { e.printStackTrace(); } }); } /** * 释放锁 * @param needLoadIds 需要释放锁的id集合 * @param latch 通过该CountDownLatch来释放锁 */ private void release(Collection<String> needLoadIds, CountDownLatch latch) { if (CollectionUtils.isEmpty(needLoadIds)) { return; } synchronized (lock) { needLoadIds.forEach(id -> loadCache.remove(id)); } if (latch != null) { latch.countDown(); } } /** * 加载缓存,比如根据id从db查询数据,然后设置到redis中 * @param needLoadIds 加载缓存的id集合 */ protected abstract void loadCache(Collection<String> needLoadIds); /** * 对需要加载缓存的id绑定CountDownLatch,后续相同的id请求来了从map中找到CountDownLatch,并且await,直到该线程加载完了缓存 * @param needLoadIds 能够正在去加载缓存的id集合 * @return 公用的CountDownLatch */ protected CountDownLatch saveLatch(Collection<String> needLoadIds) { if (CollectionUtils.isEmpty(needLoadIds)) { return null; } CountDownLatch latch = new CountDownLatch(1); needLoadIds.forEach(loadId -> loadCache.put(loadId, latch)); System.out.println("loadCache:" + loadCache); return latch; } /** * 哪些id正在加载数据,此时持有相同id的线程需要等待 * @param ids 需要加载缓存的id集合 * @return 正在加载的id所对应的CountDownLatch集合 */ private Collection<CountDownLatch> excludeLoadingIds(Collection<String> ids) { List<CountDownLatch> loadingLatchList = Lists.newArrayList(); Iterator<String> iterator = ids.iterator(); while (iterator.hasNext()) { String id = iterator.next(); CountDownLatch latch = loadCache.get(id); if (latch != null) { loadingLatchList.add(latch); iterator.remove(); } } System.out.println("loadingLatchList:" + loadingLatchList); return loadingLatchList; } }
import java.util.Collection; public class BizCacheSetterRpcService extends AbstractCacheSetterService { @Override protected void loadCache(Collection<String> needLoadIds) { // 读取db进行处理 // 设置缓存 } }
Ini bermakna sebilangan besar kegagalan cache telah mencecah db Sudah tentu, mereka mesti menjadi jenis cache perniagaan masalah dengan penulisan kod. Anda boleh memecahkan masa tamat ketidaksahihan cache dan jangan biarkan ia gagal secara berpusat.
Pembelajaran yang disyorkan: Tutorial video Redis
Atas ialah kandungan terperinci Perkongsian teknik konkurensi tinggi menggunakan Redis dan cache tempatan. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!