この
記事数が
最近は
集計方 法を ざっくり振り返る
PV のPV 機能を
その
しかし、
現在は
Redis の
pageviews:${NODE_ENV}:${slug}
の形式で キーを 保存している - インクリメント時は
IP アドレスを ハッシュ化して 保存し、 n 秒間重複して 計測されないように チェックしている - n 秒経過すると、
その キーバリューは 消滅する
キーに
多い 順に 取得する 方 法
PV の人気記事を
- キーに
紐づく データを 全て 取得 ( mget
)する sort
で昇順に 並び替える
これだけ。
const getAllPageViewsSorted = async () => { const prefix = `pageviews:${env.NODE_ENV}:`; const keys = postMetadataForEdge.map(({ slug }) => `${prefix}${slug}`);
if (keys.length === 0) { return []; }
const values = await redis.mget<number[]>(...keys);
const pageViews = keys.map((key, index) => ({ slug: key.replace(prefix, ""), views: values[index] ?? 0, }));
pageViews.sort((a, b) => b.views - a.views);
return pageViews;};
当初はscan
コマンドをmget
に
PV は
API が 走らないように する
毎回PV は
import { unstable_cache } from "next/cache";
export const getCachedAllPageViewsSorted = unstable_cache( async () => getAllPageViewsSorted(), undefined, { revalidate: 60 * 60, },);
キャッシュにはunstable_cache
をunstable
(不安定)と
unstable_cache
を
キャッシュされた
話は
表示
記事一覧をPV が
const popularPostCount = 6;
export const PopularPosts = async ({ className }: { className?: string }) => { const allPageViewsSorted = await getCachedAllPageViewsSorted(); const popularPosts = allPageViewsSorted .slice(0, popularPostCount) .map(({ slug }) => getPostBySlug(slug) as Post);
return ( <div className={cn( "grid grid-cols-2 gap-3 sm:grid-cols-3 xl:gap-4", className, )} > {popularPosts.map((post, index) => ( <div key={post._id} className="relative"> <ArticleCard post={post} /> <RankNumber rank={index + 1} /> </div> ))} </div> );};
上記のslug
UI は

Data Cache の

ごに
さい人気記事を