- 投稿日:2020-08-01T22:34:49+09:00
MySQLとPostgreSQLでのtimezoneの取り扱いの違い
環境
- MySQL 5.6
- PostgreSQL 12
- Grafana 7.0.0
- Windows
- DB,クライアントのタイムゾーンはAsia/Tokyo
内容
DATE型カラム(hogedate)に'2020-07-20'が入っている場合
MySQLでそのカラムをUNIX_TIMESTAMP(hogedate)すると2020-07-19 15:00:00のUNIX TIMESTAMPが得られる。
PostgreSQLでそのカラムをEXTRACT(EPOCH FROM hogedate)すると、2020-07-20 00:00:00のUNIX TIMESTAMPが得られる。PostgreSQLは全体的にtimezoneを考慮しない方針の様子
SELECT EXTRACT(EPOCH FROM timestamp '2020-07-20 00:00:00')
=> 1595203200 (2020-07-20 00:00:00UTCのUNIX時間)
SELECT EXTRACT(EPOCH FROM timestamp with time zone '2020-07-20 00:00:00')
=> 1595170800 (2020-07-19 15:00:00UTCのUNIX時間)試してないけど、普通にテーブルに入れて試しても同じ結果になるだろう。
日本国内に閉じたシステムであれば、 無理に考慮してくれない方が扱いやすい。調べた経緯
GrafanaでMySQL内に入っている日時データを取得してグラフ表示しているが、どうも数が合わない。
DATEカラムを時間軸にするようなグラフを書く場合、$_timeGroupAlias(hogedatecol, '1d', 0)などとGrafanaマクロを使ってると、UNIX_TIMESTAMP(hogedatecol)が取られるが、この場合は、9時間前のUNIX時間が返ってしまう。
例)カラムの値が'2020-07-20'だと、MySQLでその値をUNIXTIMESTAMP()すると、'2020-07-19 15:00:00'のUNIX時間が帰ってくる。そのあとで、上記例だと1日で丸めるために、86400(1日の秒数)で割って、掛け戻してるため、2020-7-19で丸められてしまう。。。対応としてはどうするべきなのだろうか?
- Grafanaのマクロを使用しない
=> 基本これかな。面倒だけど。
- DBのタイムゾーンの設定をしない
=> 今回は扱う必要がないのでこれでもいいけど、DBがUTC、クライアントがAsia/Tokyoの場合の動きを確認?
- PostgreSQLを使う
=> 今回はお遊びツールなのでこれでもいいけど、ちょっと今から返るの面倒だな。
プロダクトはMySQL/MariaDBなのでDBを2つ入れるのは非力マシンなので避けたいし。
- その他
- 投稿日:2020-08-01T20:11:45+09:00
【MySQL練習問題】特定のカラムでグルーピングを行い、それぞれのグループの代表レコードを選ぶ
問
従業員への給与を管理するための
salaries
テーブルがあります。
emp_no salary from_date to_date 10001 60117 1986-06-26 1987-06-26 10001 62102 1987-06-26 1988-06-25 ⋮ ⋮ ⋮ ⋮ 10002 65828 1996-08-03 1997-08-03 10002 65909 1997-08-03 1998-08-03 ⋮ ⋮ ⋮ ⋮ 499999 74327 2000-11-29 2001-11-29 499999 77303 2001-11-29 9999-01-01 それぞれのレコードにおいて、IDが
emp_no
の従業員に対してfrom_date
からto_date
の期間に付与した給与をsalary
で表しています。(テーブルのスキーマとレコードは datacharmer/test_db からお借りしました。)
さて、データベーススペシャリストであるあなたのもとに次のような依頼がきました。
従業員1人ごとに、その人への給与が最大だった期間を抽出してほしい。 もし、最大の期間が複数ある場合は、全て抽出してほしい。MySQLのクエリのみを用いてこの依頼に応えるには、どうすればよいでしょうか?
誤答
誤答1. GROUP BYとMAXで給与が最大のレコードを抽出する
emp_no
でGROUP BY
し、それぞれのグループの最大値をMAX
で出すと考えて、次のようなクエリはどうでしょう?SELECT `emp_no`, MAX(`salary`) AS `max_salary`, `from_date`, `to_date` FROM `salaries` GROUP BY `emp_no` ;残念ながら、これは誤答です。
そもそも、MySQL 5.7.5かそれ以降を使っている場合このクエリはエラーになります。
エラーになるのは、 ONLY_FULL_GROUP_BY というsql_mode
がデフォルトで有効になっているためです。
この設定では、GROUP BY
でグルーピングの基準として指定していないカラムをSELECT
内に含める場合、SUM
やMAX
などの集約関数を使う必要があるのです。
上のクエリの場合、from_date
とto_date
がグルーピングの基準でないにも関わらず、単独で登場していますね。もちろん、一時的に
sql_mode
からONLY_FULL_GROUP_BY
を取り除けばエラーは出なくなります:SET SESSION sql_mode=( SELECT REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '') );ただ、エラーが出なくなっただけで、誤答であることには変わりません。
依頼に次のような要件があったことを覚えていますか?もし、最大の期間が複数ある場合は、全て抽出してほしい。そうですね。
GROUP BY
を使ってしまうと、emp_no
が同じレコードはすべて1つのレコードに集約されてしまうのです。では、仮に1つしか抽出できなくてもよいとしましょう。依然として誤答です。
根本的な問題は、このクエリで取れる
from_date
とto_date
が正しいものであるという保証、つまり、給与が最大のレコードに対応する期間であるという保証がどこにもないということです。「え、
MAX
で取得したレコードのfrom_date
とto_date
が入るんじゃないの?」MySQLのリファレンスマニュアル によると、
GROUP BY
でグルーピングの対象になっていないカラムを単独でSELECT
しようとした場合、グループ内のどのレコードからSELECT
されるかは「不確定」なのです。実際に僕の手元で実行した結果、
SELECT
されたfrom_date
とto_date
は、給与が最大のレコードのものではなく、グループ内で一番最初に出現したレコードのものでした。クエリを一見すれば正答に思えるものの、
GROUP BY
の挙動を正しく理解することで避けられる誤答です。誤答2. ORDER BYしたサブクエリをGROUP BY
まずサブクエリで
salaries
を降順に並び替えて、そのサブクエリをemp_no
でGROUP BY
すると考えて、次のようなクエリはどうでしょう?SELECT * FROM ( SELECT * FROM `salaries` ORDER BY `salary` DESC ) AS `ordered_salaries` GROUP BY `emp_no` ;残念ながら、これも誤答です。
誤答1と同様に、MySQL 5.7.5かそれ以降を使っている場合
ONLY_FULL_GROUP_BY
を無効にする必要があるので、その前提で続けます。サブクエリで
salary
でORDER BY
をしておけば、外側のGROUP BY
がそれぞれのemp_no
ごとに最初に出現したレコード、
つまり、給与が最大のレコードをグループ内の代表レコードとしてくれるはず、という淡い期待が裏切られる形となりました。誤答1と同様、複数の期間が該当する場合にも1つしか抽出できない上、結果に含まれる
from_date
とto_date
が正しい保証がありません。
それどころか、salary
が最大であるという保証すらありません。実際に僕の手元で実行した結果、
SELECT
されたのはORDER BY
する前のテーブルでグループ内で一番最初に出現したレコードでした。正答
正答1. サブクエリで取得した最大を使ってINNER JOIN
まずサブクエリで従業員ごとの最大の給与を取得し、その結果と元のテーブルを
INNER JOIN
することで、元のテーブルから給与が最大でないレコードを除外することができます。SELECT `salaries`.* FROM `salaries` INNER JOIN ( SELECT `emp_no`, MAX(`salary`) AS `max_salary` FROM `salaries` GROUP BY `emp_no` ) AS `max_salaries` ON `salaries`.`emp_no` = `max_salaries`.`emp_no` AND `salaries`.`salary` = `max_salary` ;まず、内側のクエリで
emp_no
とその従業員の最大の給与を1:1で対応させるテーブルを作っています。
このテーブルとsalaries
テーブルをINNER JOIN
するのですが、その際の条件が2つありますね。まず1つ目の条件では、
salaries
テーブルのそれぞれのレコードについて、作成した対応テーブルから同じemp_no
を持つレコードを結合しています。2つ目の条件を
AND
で合わせることで、salary
がemp_no
に対応するmax_salary
に一致する場合のみ結合の対象となるようになります。
結果として、それぞれのemp_no
で最大の給与でないレコードは結合の対象外となり、結合結果には給与が最大であるレコードだけが残ります。複数レコードが
max_salary
に一致する場合、その全てが結合の対象になるため、誤答であったような取り残しの心配もありませんね。正答2. 自己結合を使う
自己結合を使うことで、さらにエレガントで実行効率の良いクエリに書き換えられます。
SELECT `left`.* FROM `salaries` AS `left` LEFT OUTER JOIN `salaries` AS `right` ON `left`.`emp_no` = `right`.`emp_no` AND `left`.`salary` < `right`.`salary` WHERE `right`.`salary` IS NULL ;サブクエリがなくなったのでお上品な雰囲気になりましたが、同時にやや近寄りがたい雰囲気も出ています。解読してみましょう。
まず1つめの条件では、
salaries
テーブル内でemp_no
が同じレコード同士の組み合わせが結合の結果となります。
仮にemp_no = 10001
のレコードが5件あったとすると、それに対応するレコードは結合結果には5 × 5 = 25
件含まれる、という具合ですね。これに2つ目の条件を
AND
で合わせることで、結合元のレコードのsalary
が結合先のレコードのsalary
より少ない場合のみ結合が成功するという条件が加わります。
よって、salary
が最大であるレコードは結合相手が見つからないため、結合に失敗します。
ここで、結合の種類がLEFT OUTER JOIN
であるため、この場合right
のカラムがすべてNULL
であるという扱いになります。最後に、
WHERE
を使って結合に失敗したレコードのみ抽出しています。
結合に失敗したレコードというのは、つまりsalary
が最大であるレコードのことなので、給与が最大のレコードのみが残ります。正答1と比べると直感的とは言い難いですが、サブクエリが無い分実行効率の向上が期待できます。
レコード数2,844,047件のsalaries
テーブルで試したところ、正答1が平均で565 msかかったところ、正答2は平均で8 msでした。
パフォーマンスは総レコード数、グループ内の平均レコード数、インデックスの有無等に影響するため一概にどちらが良いとは言えないですが、クエリ最適化をする際のために頭の片隅に置くといいでしょう。参考
- mysql - Get records with max value for each group of grouped SQL results - Stack Overflow
- このaxiac氏の解答が本記事作成のきっかけになりました。ありがとうございます。
- 投稿日:2020-08-01T16:59:29+09:00
MySQLのカラム名に "key" は使えない
何度読み返しても正しい文法のSQL文でSyntax Errorが出続けて困ったときは、
どこかに予約語が含まれていないか確認しましょう強力な助っ人
私を救ってくれたオンラインの文法チェッカーです
EverSQL SQL Query Syntax Check & Validator私を悩ませていたのは
INSERT INTO table (id, key, name) VALUES (1, 'A', 'abc'), (2, 'B', 'def'), (3, 'C', 'ghi')みたいなSQL文なのですが、ちゃんとprettyした上で
key
をハイライトして教えてくれて本当に有能です
- 投稿日:2020-08-01T14:55:12+09:00
docker-composeで、MySQLの設定ファイル変更を適用させる
何をするのか
docker-composeでMySQLの設定ファイル(my.conf)の中身を更新したが、作り直したコンテナに反映されなかったため、これを反映させる。
環境
- Laravel
- MySQL8.0
- docker-compose version 1.26.2
こちらを参考にDockerでLaravelの環境構築をしていました。
【初心者向け】20分でLaravel開発環境を爆速構築するDockerハンズオン問題が起きた背景
Laravelでは、MySQLのバージョンが8の場合にデータベース接続がうまくできない不具合があります。
MySQLのバージョンが8.0.4以降の場合、デフォルトの認証方式がcaching_sha2_passwordになっており、PHPのMySQL接続ライブラリがcaching_sha2_passwordに対応していないため、MySQLに接続できない(ログインできない)というエラーが発生していました。
引用:Laravel+MySQL8.0環境でマイグレートできない場合の原因と対処法
このため、「認証方法を変更」を行いました。
ただ、↓のリンクに「すでに認証方法が対応された」と書かれていたのであえて変更する必要もないと思いMySQLの設定の以下をコメントアウトしました。(これが間違いだった。)
MySQL8.0 認証方式を変更する(Laravel5) - Qiitamy.cnf# MySQL8 caching_sha2_password to mysql_native_password default-authentication-plugin = mysql_native_passwordそのまま
docker-compose up -d —build
してマイグレーションしたらところエラー発生
Illuminate\Database\QueryException SQLSTATE[HY000] [2054] The server requested authentication method unknown to the client (SQL: select * from information_schema.tables where table_schema = routine-share and table_name = migrations and table_type = 'BASE TABLE') at vendor/laravel/framework/src/Illuminate/Database/Connection.php:671 667| // If an exception occurs when attempting to run a query, we'll format the error 668| // message to include the bindings with SQL, which will make this exception a 669| // lot more helpful to the developer instead of just the database's errors. 670| catch (Exception $e) { > 671| throw new QueryException( 672| $query, $this->prepareBindings($bindings), $e 673| ); 674| } 675| +34 vendor frames 35 artisan:37 Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))それならば、さっきのコメントアウトを戻せば大丈夫だろうと思い
docker-compose down
してから
先程のmy.cnfの認証方法変更部分のコメントアウトを戻しました。改めて
docker-compose up -d —build
をします。が、マイグレーションできず、同じエラー画面になります。
解決方法
どうやらデータベース(volumes)がマウントされていると設定が変更されないようでした。
一度データベース(volumes)を削除すればOKなので、、、
docker volume pruneしたところ、マイグレーションまで問題なくできました。
参考にしてもらえたらうれしいです。
以下参考:
DockerのMySQLの環境変数系の設定が反映されないのは余計なデータベースをマウントしているから - QiitaLaravel+MySQL8.0環境でマイグレートできない場合の原因と対処法
Docker: where is docker volume located for this compose file
- 投稿日:2020-08-01T14:42:25+09:00
仮想DBサーバ構築(Mac編)+MySQLインストール+Sequel Proから接続
手持ちのMac端末上に仮想のDBサーバ(ubuntu16.04)を構築し、MySQLをインストールして、更にMac上からSequel Proを通して接続する方法を備忘録+参照用に記載します。
■環境
- Mac mini (Late 2014) macOS Catalina バージョン 10.15.6
- Oracle VM VirtualBox 6.1.12
- Vagrant 2.2.9
■構築する仮想マシン(VM)
- Ubuntu 16.04.6 LTS (GNU/Linux 4.4.0-186-generic x86_64)
- mysql Ver 14.14 Distrib 5.7.31, for Linux (x86_64)
■必要なソフトウェアのインストール
Macに下記のソフトウェアをインストールしておく。
Vagrant
https://www.vagrantup.com/downloads.html
ダウンロードしたdmgイメージを展開し、vagrant.pkg
をダブルクリックするとインストーラが起動するので、指示に従ってインストールを完了する。VirtualBox
http://www.oracle.com/technetwork/server-storage/virtualbox/downloads/index.html?ssSourceSiteId=otnjp
ダウンロードしたdmgイメージを展開し、VirtualBox.pkg
をダブルクリックするとインストーラが起動するので、指示に従ってインストールを完了する。Sequel Pro
https://sequelpro.com/download
ダウンロードしたdmgイメージを展開し、アプリケーションフォルダに移動する。これらのソフトウェアがインストール済みの前提で手順を書いていきます。そもそもこいつらってなんなの?っていうのはここでは詳しく書きませんので、別の記事を参照してください。
■仮想DBサーバ(Ubuntu16.04)の構築
今後仮想環境を複数作成する可能性を考慮して、仮想環境を括る”Vagrant”をHomeディレクトリ直下に作成し、その下に今回作成する仮想環境のディレクトリを作成
$ mkdir -p ~/Vagrant/mysql_ubuntu1604cdコマンドで移動
$ cd ~/Vagrant/mysql_ubuntu1604Vagrantfile の作成
$ vagrant init bento/ubuntu-16.04Vagrantfileの編集
作成されたVagrantfileをテキストエディタで開き、35行目、以下の部分のコメントアウトを外すconfig.vm.network "private_network", ip: "192.168.33.10"VMの起動
$ vagrant upVMへSSH接続
$ vagrant ssh下記のようなコマンドが返ってきたら、無事VMにSSH接続できています。
■Ubuntuの日本語化
$ sudo apt-get -y install language-pack-ja-base language-pack-ja ibus-mozc$ localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja" // パスワードを入力 vagrantの初期パスワードは"vagrant"$ source /etc/default/locale$ echo $LANG ja_JP.UTF-8■Ubuntuの諸々アップデート
sudo apt-get update && sudo apt-get -y upgrade && sudo apt-get -y dist-upgrade && sudo apt-get -y autoremove && sudo apt-get -y autoclean途中で次のような画面になったら、「install the package maintainer’s version」を選択する。
sudo init 6 // 再起動■MySQLインストール
再起動実行後、SSH接続が切れるので再度
vagrant ssh
して、次はMySQLのインストールを進めていきます。sudo apt-get install mysql-serverrootユーザーのパスワードを設定するよう求められるので、パスワードを設定。
上記Enter後、パスワードの確認を求められるので設定したパスワードを再度入力。■よく使うコマンド
MySQLバージョン確認
$ mysql --version mysql Ver 14.14 Distrib 5.7.31, for Linux (x86_64) using EditLine wrapperMySQLにrootユーザでログイン
$ mysql -u root -p // パスワードを入力MySQLの状態確認
$ mysqladmin ping -u root -p Enter password: mysqld is alive■Sequel Pro 接続情報の設定
VagrantでSSH接続中であればexitしておく。exitしてもVMは止めない限り裏でずっと動いているのでDBサーバを使う作業が終わったら
vagrant halt
しておくのを忘れずに。vagrantの詳しい使い方はここでは触れないので別の記事を参照してください。Sequel Proを起動して、接続情報を画像のように入力。
※パスワードはMySQLのインストール時に設定したパスワード。
SSHパスワードはvagrantユーザーのパスワード。過去にWindows10でも似たようなことやってるのでWindowsの人はこっちを見てください。
https://qiita.com/ekCraft/items/3c802f684824e5765515
- 投稿日:2020-08-01T14:34:36+09:00
EC2のデプロイに関わるエラーを確認するコマンド一覧
この記事の使い方
EC2、RDS(MySQL)、Rails、nginx、pumaを使用してデプロイする際のお供に。
何を隠そうこれは筆者のためのメモでもあります。笑
Amazon linuxとAmazon linux2でコマンドが違っていたりして混乱するので。使用するAMI
Amazon linux2
Nginx関連
$ sudo systemctl start nginx.service #起動 $ sudo systemctl status nginx.service #ステータス確認 $ tail -f log/nginx.error.log #エラーログ確認 $ sudo nginx -t #設定ファイル内の間違いを教えてくれるpuma関連
$ bundle exec pumactl start #起動MySQL関連
$ sudo systemctl start mysqld.service #起動 $ sudo systemctl status mysqld.service #ステータス確認見てくださりありがとうございました。
この記事がデプロイの助けになれば幸いです。
- 投稿日:2020-08-01T10:28:12+09:00
DockerでLaravel+Apache+MySQLの開発環境を構築する
https://note.com/pocke_techblog/n/n8af813848fa0
上記の記事通りにすすめて、DockerでLaravel+Apache+MySQLの開発環境を構築しましたが、パーミションのエラーが出て解決に手こずったので記録に残しておきます。
作業環境
OS:Windows 10 Pro ver.2004
Docker:version 19.03.12, build 48a66213feDockerのインストール
Dockerのインストール方法については別記事にて解説しているので、まだインストールしていないのであれば参考にしてみてください。
Windows 10 Pro : Dockerをインストールするサンプルコードを準備する
フォルダ構成
フォルダ構成は以下の通りです。
. ├── docker/ │ ├── app/ │ │ ├ Dockerfile │ │ ├ php.ini # PHP設定用のファイル │ │ └ 000-default.conf # Apacheの設定ファイル │ └── db/ │ ├ data/ # MySQLのデータを保存しておくディレクトリ │ └ my.cnf # MySQLの設定ファイル │ ├── src/ # Laravelのソースを格納するディレクトリ └── docker-compose.ymlソースコード
Dockerfile
# どんなdockerイメージを利用して構築をするか FROM php:7.4-apache # 設定ファイルをdockerコンテナ内のPHP、Apacheに読み込ませる ADD php.ini /usr/local/etc/php/ ADD 000-default.conf /etc/apache2/sites-enabled/ # Composerのインストール RUN cd /usr/bin && curl -s http://getcomposer.org/installer | php && ln -s /usr/bin/composer.phar /usr/bin/composer # ミドルウェアインストール RUN apt-get update \ && apt-get install -y \ git \ zip \ unzip \ vim \ libpng-dev \ libpq-dev \ && docker-php-ext-install pdo_mysql # Laravelで必要になるmodRewriteを有効化する RUN mv /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled RUN /bin/sh -c a2enmod rewritephp.ini
docker/app/php.ini[Date] date.timezone = "Asia/Tokyo" [mbstring] mbstring.internal_encoding = "UTF-8" mbstring.language = "Japanese"docker/app/000-default.conf<VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot /var/www/html/laravelapp/public ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined <Directory /var/www/html/laravelapp/public> AllowOverride All </Directory> </VirtualHost>my.cnf
docker/db/my.cnf[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci [client] default-character-set=utf8mb4docker-compose.yml
docker-compose.yml# Compose fileのバージョン指定 version: '3' # どんなコンテナを立ち上げるか services: # laravelを動かすコンテナ app: # どのポートを開いて繋ぐか。下記はコンテナの80番ポートを開いて、ホストの8000番につなぐ ports: - "8000:80" # 先ほど作ったDockerfileを使って、コンテナをビルドするという指定 build: ./docker/app # コンテナの名前を指定 container_name: laravel_app # コンテナとホスト側のディレクトリを同期する場所の指定。laravelのソースが入る予定の場所 volumes: - ./src:/var/www/html # MySQLを動かすコンテナ db: # Docker HubからMySQL5.7の公式イメージをダウンロードしてくる指定 image: mysql:5.7 container_name: laravel_db # コンテナ内の環境変数を指定。環境変数を渡すとビルド時に設定してくれるDockerイメージがあるので、利用の際はDocker Hubのサイトで確認すると良い environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: laravel_db MYSQL_USER: laravel_user MYSQL_PASSWORD: laravel_pass TZ: 'Asia/Tokyo' # 起動時のコマンド command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci # ディレクトリ同期。設定ファイルとMySQLのデータが保存される場所を同期している。コンテナは基本的に起動時に変更されてもコンテナ自体が止まるとデータが消えてしまうため、保存しておきたいものはホストマシンと同期しておく必要がある。 volumes: - ./docker/db/data:/var/lib/mysql - ./docker/db/my.cnf:/etc/mysql/conf.d/my.cnf ports: - 3306:3306開発環境を構築する
Docker Composeでコンテナを立ち上げる
次のコマンドを実行して、Docker Composeでコンテナを立ち上げます。
$ docker-compose build $ docker-compose upLaravelのプロジェクトを作成する
上記とは別の画面を立ち上げて次のコマンドを実行して、Laravelのプロジェクトを作成します。
$ docker exec -it laravel_app bash $ composer create-project laravel/laravel --prefer-dist laravelappこれで
http://localhost:8000
にアクセスするとLaravelの画面が立ち上がるはずですが、パーミッションのエラーが起きてしまいました。パーミッションのエラーを解決する
最初に以下のようなエラーメッセージが出ました。
The stream or file "/var/www/html/laravelapp/storage/logs/laravel.log" could not be opened in append mode: failed to open stream: Permission denied
ここのサイトを参考に以下のコマンドを実行しました。
$ chmod 777 /var/www/html/laravelapp/storage/logs/laravel.log $ chmod 775 /var/www/html/laravelapp/storage/logs/すると以下のようにエラーメッセージの文言が変わりました。
file_put_contents(/var/www/html/laravelapp/storage/framework/sessions/oI2ffP9BUcWC0llE0NEoFU09eqQyXTosSZZeq4BV): failed to open stream: Permission denied
ここのサイトを参考に以下のコマンドを実行しました。
chmod -R gu+w /var/www/html/laravelapp/storage chmod -R guo+w /var/www/html/laravelapp/storage cd ./laravelapp php artisan cache:clearこれでパーミッションのエラーが解決して、Laravelの画面が表示されました。