laowu 1 час назад
Родитель
Сommit
18e04defa4
2 измененных файлов с 158 добавлено и 37 удалено
  1. 107 37
      app/Services/RecordPlatformData.php
  2. 51 0
      tests/Unit/RecordPlatformDataServiceTest.php

+ 107 - 37
app/Services/RecordPlatformData.php

@@ -6,20 +6,22 @@ namespace App\Services;
 
 use App\Facade\TableName;
 use Carbon\Carbon;
+use Illuminate\Database\Query\Expression;
 use Illuminate\Support\Facades\DB;
 
 class RecordPlatformData
 {
     // 注册留存 $date = Y-m-d
-    public function Retain($time1,$time2,$field)
+    public function Retain($time1, $time2, $field)
     {
         //$time1 = Carbon::parse($time1)->format('Y-m-d');
         $time2 = $time2->format('Ymd');
 
         $RecordPlatformDataModel = new \App\Models\RecordPlatformData();
-        $count = DB::connection('read')->table(TableName::QPAccountsDB().'AccountsInfo as ai')
+        $count = DB::connection('read')
+            ->table($this->withNoLock(TableName::QPAccountsDB() . 'AccountsInfo as ai'))
             ->whereExists(function ($query) use ($time2) {
-                $query->from(TableName::QPRecordDB() . 'RecordUserGamePlay as rl')
+                $query->from($this->withNoLock(TableName::QPRecordDB() . 'RecordUserGamePlay as rl'))
                     ->select('rl.UserID')
 //                    ->whereRaw("rl.UserID=ai.UserID and DateID=CONVERT(VARCHAR(100),DATEADD(dd,$dateNum,'$date'),112)");
                     ->whereRaw("rl.UserID=ai.UserID and DateID=$time2");
@@ -37,19 +39,37 @@ class RecordPlatformData
 
         foreach ($GroupChannelList as $GroupChannel) {
 
-            $firstChannel = $RecordPlatformDataModel->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])->first();
+            $firstChannel = $RecordPlatformDataModel
+                ->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])
+                ->lock('with(nolock)')
+                ->first();
             if ($firstChannel) {
-                $RecordPlatformDataModel->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])->update([$field=>$GroupChannel[$field]]);
-            }else{
-                $RecordPlatformDataModel->insert(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1,$field=>$GroupChannel[$field]]);
+                $RecordPlatformDataModel
+                    ->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])
+                    ->update([$field => $GroupChannel[$field]]);
+            } else {
+                $RecordPlatformDataModel->insert([
+                    'Channel' => $GroupChannel['Channel'],
+                    'DateID' => $time1,
+                    $field => $GroupChannel[$field],
+                ]);
             }
         }
 
-        $firstAllChannel = $RecordPlatformDataModel->where(['Channel' => -1, 'DateID' => $time1])->first();
+        $firstAllChannel = $RecordPlatformDataModel
+            ->where(['Channel' => -1, 'DateID' => $time1])
+            ->lock('with(nolock)')
+            ->first();
         if ($firstAllChannel) {
-            $RecordPlatformDataModel->where(['Channel' => -1, 'DateID' => $time1])->update([$field=>$AllChannelList]);
-        }else{
-            $RecordPlatformDataModel->insert(['Channel' => -1, 'DateID' => $time1,$field=>$AllChannelList]);
+            $RecordPlatformDataModel
+                ->where(['Channel' => -1, 'DateID' => $time1])
+                ->update([$field => $AllChannelList]);
+        } else {
+            $RecordPlatformDataModel->insert([
+                'Channel' => -1,
+                'DateID' => $time1,
+                $field => $AllChannelList,
+            ]);
         }
 
         return true;
@@ -57,16 +77,17 @@ class RecordPlatformData
     }
 
     // 活跃留存 $date = Y-m-d
-    public function activeRetain($time1,$time2,$field)
+    public function activeRetain($time1, $time2, $field)
     {
         $time2 = $time2->format('Ymd');
         $RecordPlatformDataModel = new \App\Models\RecordPlatformData();
 
 
-        $count = DB::connection('read')->table(TableName::QPRecordDB().'RecordUserLogin as rul')
-            ->join(TableName::QPAccountsDB().'AccountsInfo as ai','rul.UserID','ai.UserID')
-            ->whereExists(function ($query) use ( $time2) {
-                $query->from(TableName::QPRecordDB() . 'RecordUserLogin as rl')
+        $count = DB::connection('read')
+            ->table($this->withNoLock(TableName::QPRecordDB() . 'RecordUserLogin as rul'))
+            ->join($this->withNoLock(TableName::QPAccountsDB() . 'AccountsInfo as ai'), 'rul.UserID', 'ai.UserID')
+            ->whereExists(function ($query) use ($time2) {
+                $query->from($this->withNoLock(TableName::QPRecordDB() . 'RecordUserLogin as rl'))
                     ->select('rl.UserID')
                     ->whereRaw("rl.UserID=rul.UserID and DateID=$time2");
             })
@@ -82,19 +103,37 @@ class RecordPlatformData
 
         foreach ($GroupChannelList as $GroupChannel) {
 
-            $firstChannel = $RecordPlatformDataModel->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])->first();
+            $firstChannel = $RecordPlatformDataModel
+                ->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])
+                ->lock('with(nolock)')
+                ->first();
             if ($firstChannel) {
-                $RecordPlatformDataModel->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])->update([$field=>$GroupChannel[$field]]);
-            }else{
-                $RecordPlatformDataModel->insert(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1,$field=>$GroupChannel[$field]]);
+                $RecordPlatformDataModel
+                    ->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $time1])
+                    ->update([$field => $GroupChannel[$field]]);
+            } else {
+                $RecordPlatformDataModel->insert([
+                    'Channel' => $GroupChannel['Channel'],
+                    'DateID' => $time1,
+                    $field => $GroupChannel[$field],
+                ]);
             }
         }
 
-        $firstAllChannel = $RecordPlatformDataModel->where(['Channel' => -1, 'DateID' => $time1])->first();
+        $firstAllChannel = $RecordPlatformDataModel
+            ->where(['Channel' => -1, 'DateID' => $time1])
+            ->lock('with(nolock)')
+            ->first();
         if ($firstAllChannel) {
-            $RecordPlatformDataModel->where(['Channel' => -1, 'DateID' => $time1])->update([$field=>$AllChannelList]);
-        }else{
-            $RecordPlatformDataModel->insert(['Channel' => -1, 'DateID' => $time1,$field=>$AllChannelList]);
+            $RecordPlatformDataModel
+                ->where(['Channel' => -1, 'DateID' => $time1])
+                ->update([$field => $AllChannelList]);
+        } else {
+            $RecordPlatformDataModel->insert([
+                'Channel' => -1,
+                'DateID' => $time1,
+                $field => $AllChannelList,
+            ]);
         }
 
         return true;
@@ -109,20 +148,21 @@ class RecordPlatformData
      * @param $field   // 查找/修改 的字段
      * @return bool
      */
-    public function payRetain($time1,$time2,$field)
+    public function payRetain($time1, $time2, $field)
     {
         $time2 = $time2->format('Ymd');
         $RecordPlatformDataModel = new \App\Models\RecordPlatformData();
         $dateID = $time1;
 
-        $count = DB::connection('read')->table(TableName::QPRecordDB().'RecordUserDataStatisticsNew as record')
-            ->join(TableName::QPAccountsDB().'AccountsInfo as ai','record.UserID','ai.UserID')
+        $count = DB::connection('read')
+            ->table($this->withNoLock(TableName::QPRecordDB() . 'RecordUserDataStatisticsNew as record'))
+            ->join($this->withNoLock(TableName::QPAccountsDB() . 'AccountsInfo as ai'), 'record.UserID', 'ai.UserID')
             ->whereExists(function ($query) use ($time2) {
-                $query->from(TableName::QPRecordDB() . 'RecordUserGamePlay as rl')
+                $query->from($this->withNoLock(TableName::QPRecordDB() . 'RecordUserGamePlay as rl'))
                     ->select('rl.UserID')
                     ->whereRaw("rl.UserID=record.UserID and DateID=$time2");
             })
-            ->where('Recharge','>',0)
+            ->where('Recharge', '>', 0)
             ->where('DateID', $time1)
             ->where([
                 ['ai.RegisterDate', '>=', Carbon::createFromFormat('Ymd', $time1)->format('Y-m-d')],
@@ -140,22 +180,52 @@ class RecordPlatformData
 
         foreach ($GroupChannelList as $GroupChannel) {
 
-            $firstChannel = $RecordPlatformDataModel->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $dateID])->first();
+            $firstChannel = $RecordPlatformDataModel
+                ->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $dateID])
+                ->lock('with(nolock)')
+                ->first();
             if ($firstChannel) {
-                $RecordPlatformDataModel->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $dateID])->update([$field=>$GroupChannel[$field]]);
-            }else{
-                $RecordPlatformDataModel->insert(['Channel' => $GroupChannel['Channel'], 'DateID' => $dateID,$field=>$GroupChannel[$field]]);
+                $RecordPlatformDataModel
+                    ->where(['Channel' => $GroupChannel['Channel'], 'DateID' => $dateID])
+                    ->update([$field => $GroupChannel[$field]]);
+            } else {
+                $RecordPlatformDataModel->insert([
+                    'Channel' => $GroupChannel['Channel'],
+                    'DateID' => $dateID,
+                    $field => $GroupChannel[$field],
+                ]);
             }
         }
 
-        $firstAllChannel = $RecordPlatformDataModel->where(['Channel' => -1, 'DateID' => $dateID])->first();
+        $firstAllChannel = $RecordPlatformDataModel
+            ->where(['Channel' => -1, 'DateID' => $dateID])
+            ->lock('with(nolock)')
+            ->first();
         if ($firstAllChannel) {
-            $RecordPlatformDataModel->where(['Channel' => -1, 'DateID' => $dateID])->update([$field=>$AllChannelList]);
-        }else{
-            $RecordPlatformDataModel->insert(['Channel' => -1, 'DateID' => $dateID,$field=>$AllChannelList]);
+            $RecordPlatformDataModel
+                ->where(['Channel' => -1, 'DateID' => $dateID])
+                ->update([$field => $AllChannelList]);
+        } else {
+            $RecordPlatformDataModel->insert([
+                'Channel' => -1,
+                'DateID' => $dateID,
+                $field => $AllChannelList,
+            ]);
         }
 
         return true;
 
     }
+
+    /**
+     * Build SQL Server table expression with NOLOCK hint for read-heavy statistics queries.
+     *
+     * @param string $table
+     * @return Expression
+     */
+    private function withNoLock($table)
+    {
+        return DB::raw($table . ' WITH (NOLOCK)');
+    }
+
 }

+ 51 - 0
tests/Unit/RecordPlatformDataServiceTest.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Tests\Unit;
+
+use App\Services\RecordPlatformData;
+use ReflectionClass;
+use Tests\TestCase;
+
+class RecordPlatformDataServiceTest extends TestCase
+{
+    public function testStatisticsQueriesUseNolockTableExpressions()
+    {
+        $source = $this->getServiceSource();
+
+        $this->assertContains(
+            '->table($this->withNoLock(TableName::QPAccountsDB() . \'AccountsInfo as ai\'))',
+            $source
+        );
+        $this->assertContains(
+            '->table($this->withNoLock(TableName::QPRecordDB() . \'RecordUserLogin as rul\'))',
+            $source
+        );
+        $this->assertContains(
+            '->table($this->withNoLock(TableName::QPRecordDB() . \'RecordUserDataStatisticsNew as record\'))',
+            $source
+        );
+        $this->assertContains(
+            '->from($this->withNoLock(TableName::QPRecordDB() . \'RecordUserGamePlay as rl\'))',
+            $source
+        );
+        $this->assertContains(
+            '->from($this->withNoLock(TableName::QPRecordDB() . \'RecordUserLogin as rl\'))',
+            $source
+        );
+        $this->assertContains(
+            '->join($this->withNoLock(TableName::QPAccountsDB() . \'AccountsInfo as ai\')',
+            $source
+        );
+        $this->assertNotContains(
+            "->join(TableName::QPAccountsDB().'AccountsInfo as ai'",
+            $source
+        );
+    }
+
+    private function getServiceSource()
+    {
+        $reflection = new ReflectionClass(RecordPlatformData::class);
+
+        return file_get_contents($reflection->getFileName());
+    }
+}