0
1

More than 1 year has passed since last update.

シーク法のgoでの実装

Last updated at Posted at 2023-02-05

背景

DBからページネーション用のデータのリストを取ってきたい機会がありました。今までDBを全然扱ってこなかったので、色々調べていると、offset法とシーク法が主な実装方法で、offset法はクソ遅いということがわかりました。そこで、シーク法をGoで実装したときのコードを共有したいと思います。
offset法とシーク法の比較についての資料は、以下の3つの資料がわかりやすかったです。

方法

シーク法は最後に取得したデータをもとに、次のデータからリストを取得する方法です。順番を決めているカラムがユニークな場合(primary keyなど)は、それ自身で比較して次のデータを見つければいいですが、そうでない場合は、ユニークなデータと一緒に最後のデータの次のデータを見つける必要があります。

実装

実装にはgormを使っています。

順番を決めているカラムがユニークな場合(id)

idを比較して、次のデータからデータを取得します。

unique.go
// orderはasc
// lastData.Idには前回取得時の最後のデータのidが格納されている
db.Limit(pageSize).Order("id asc").Where("id > ?", lastData.Id)

順番を決めているカラムがユニークではない場合(name)

まず、nameで比較します。ただ、nameのみでの比較では次のデータが一意に決められない可能性があるため(同名の場合など)、ユニークなidも合わせて比較します。

notUique.go
// orderはasc
// lastDataには前回取得時の最後のデータのnameとidが格納されている
db.Limit(pageSize).Order("name asc, id asc").
						Where("CASE WHEN name > ? THEN 1 WHEN name = ? THEN id > ? ELSE 0 END",
					    lastData.Name, lastData.Name, lastData.Id)

最後に取得したデータをフロントに返す時などは、並び順を決めるデータ(上の例のnameやid)を格納した構造体をエンコードしてstringに変換して渡せばいいと思います。逆に、フロントから受け取る時はデコードして、構造体(上の例のlastDataのような感じ)に格納すれば、複数の並び順に対応することができます。 

感想

offset法が遅い理由は最初のデータから検索するからですが、シーク法だと速い理由は何なんですかね?知っている方いたら教えてほしいです。
where句で検索する際は、すでにDB内部でindex化されているデータを検索するので、速いとか?

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1