EatSmartシステム部ブログ

ウェブサイトの開発や運営に関する情報です。

もぐナビのクチコミ一覧からもぐナビニュースへ

イートスマートの新人エンジニアが、「もぐナビ」での開発を振り返り、学んだ内容をまとめていきます。 今回は、キャッシュとシーケンシャルスキャンについて考える機会となりました。

今回の実装の内容

弊社サイト「もぐナビ」でトラフィックの大きいユーザーさんのクチコミ一覧から、もぐナビのニュース枠を設置し、そこからもぐナビニュースへ誘導しようという経緯で実装しました。
※もぐナビニュースについて(コンビニ商品を中心に新作や話題性のあるものを紹介しています。) f:id:eatsmart:20190621125606p:plain

実装例(関連するコードのみ抜粋)

CacheTopBean.java---キャッシュの中身に関するクラス

public class CacheTopBean implements CacheInterface {
   private NewsItemDto newsItem = new NewsItemDto();

   public NewsItemDto getNewsItem() {
        return NewsItem;
    }
  public void setNewsItem(NewsItemDto newsItem) {
        this.newsItem = newsItem;
    }
 ※NewsItemDtoには、表示に必要なニュースタイトル、遷移先url、イメージ画像、掲載日などをフィールドにもっています。
}

TopManager.java---キャッシュ作成に関わるクラス

public class TopManager implements ReloadableCacheManager {

    private static TopManager instance = new TopManager();

    private TopManager() {
    }
        public static TopManager getInstance() {
        return instance;
    }
public CacheTopBean createCacheTopBean(Calendar date) throws ServerException  {
    CacheTopBean ctb = new CacheTopBean();
 
  NewsItemDto newsItem = new NewsItemDto();
  //DBから該当のデータを抽出し、newItemに代入します。
    newsItem = jp.mognavi.news.NewsItemDao.getInstance().getNewsItemDto();
    //生成したキャッシュに目的のニュース情報のインスタンスをセットします。
    ctb.setNewsItem(newsItem);

    return ctb;
        }
}

NewsItemDao.java---ニュースのDB操作に関するクラス

public  NewsItemDto getNewsItemDto() {
        return  getSingleResult(
                "SELECT v.guid, v.title as title ,v.contents, v.category, v.pub_date, t.title as ptitle, v.image, v.update_date "
                        + "FROM v_news_item v LEFT JOIN t_news_item_pickup t ON v.guid = t.guid "
                        + "WHERE v.del_kbn = ? " + "AND v.pub_date > now() - interval '2 days' "
                        + EXCLUDE_PR_NEWS(PR商品を除外するsql)
                        + INCLUDE_NEWS_CATEGORY(特定のカテゴリに絞るsql)
                        + "ORDER BY random() LIMIT ?",
                NewsItemDto.class,
                KbnConstant.DEL_KBN_VALID, 1);
    }
※詳細を省きますが、ざっくりと公開から48時間以内で、特定のカテゴリに絞りランダムに1件取得しています。

ListAction.Java---クチコミのリスト表示に関するクラス

public class BlogItemDao extends DaoBase {
public class ListAction extends GenericBaseAction<...Bean> {
   
    CacheTopBean ctb = TopManager.getInstance().getCacheTopBean();
        //キャッシュにセットしたnewsItemをアクションの方でセットします。
        request.setAttribute("newsItem", ctb.getNewsItem());
        return success();
  }
}
上記の"newsItem"をビュー側で利用します。

実装のポイントについて

最初は、データベースから取得してきた値をニュース表示のアクションにセットするという方法で実装していましたが、それだとページの読み込み速度に影響する可能性があったため、キャッシュに保存することでその問題を回避するようにしました。
次に、sqlの中で使用したrandom関数ですが、シーケンシャルスキャンが発生するため対象データが多い場合にはクエリの実行速度に影響します。今回のケースでは、公開から48時間という制約が大きく、母数がかなり限定的されるため特段問題にしませんでした。母数が大きい場合には、一旦キャッシュに100件保存し、キャッシュからランダムに1件抽出する方法などが考えられます。

シーケンシャルスキャン参考

以上になりますが、実装内容は基本的なものになりますが随所で学びポイントや工夫の機会がありました。それらを次のタスクに繋げていければと思います。