在没Hadoop 、GP 前提下怎么进行实时数据统计。
最近着手个项目,整体数据量有5亿多,每个月增量9000w。应用场景是Oltp 根据用户id直接计算各种维度值。
因为是Oltp 场景,直接根据用户id %2000分(方便后续横向扩展),有些喜欢扯分区表的或者顺序分表的请复习下数据库原理以及硬件原理。
分完表oltp 访问速度上了几个level。但是牵涉到一个实时统计的问题,需要对2000张表进行实时统计。因暂时没gp、hadoop 这种分布式数据库环境,以及怎么解决Oltp 到分布式数据库之间实时同步的问题。
想了个恶心的办法。对2000张表开启cdc 变更,记录时间段发生的变更userid,写了个多线程脚本实时根据这些userid 去更新数据。基本做到了实时统计,数据时间间隔差10分钟左右。
明年计划结构化数据先通过Gp计算,需要写个小程序来满足Cdc 变更到Gp的实时同步。
顺便附带 多线程统计脚本,还是powershell 写的。
#region hostinfo $hostinfos=[System.Collections.ArrayList]@() [void] $hostinfos.add('192.168.1.1') [void] $hostinfos.add('1433') [void] $hostinfos.add( $ClientSqlAccount) [void] $hostinfos.add($ClientSqlPassWord) [void] $hostinfos.add('db') #endregion #region 生成2000张表 $tables=[System.Collections.ArrayList]@() <# foreach($s in 0..1999) { switch([void] $s) { {$s -lt 10 }{ [void] $tables.add('Tab'+'000'+ $s.ToString());} {$s -ge 10 -and $s -lt 100 }{ [void] $tables.add(('Tab'+'00'+ $s.ToString())); } {$s -ge 100 -and $s-lt 1000 }{ [void] $tables.add(('Tab'+'0'+ $s.ToString())); } {$s -ge 1000 }{ [void] ($tables.add(('Tab'+ $s.ToString()))); } } } #> #endregion $ClientSqlAccount=$hostinfos[2]; $ClientSqlPassWord=$hostinfos[3]; $ClientDB=$hostinfos[4]; $log='d:' $SqlServer=$hostinfos[0] ; $Port=$hostinfos[1] ; $SqlString="Data Source="+$SqlServer+","+$Port+";uid="+$ClientSqlAccount+";Password="+$ClientSqlPassWord; $SqlConn = [System.Data.SqlClient.SqlConnection] $SqlString; $SqlConn.Open() ; $SqlConn.ChangeDatabase($ClientDB); $CC = $SqlConn.CreateCommand(); $CC.CommandTimeout = 0; $CC.CommandText='select tabname from Cdc_Change_userid where isdelete=0 group by tabname ' $Reader = $CC.ExecuteReader(); while ($Reader.read()) { [void] $tables.add($Reader.GetString(0)); } $SqlConn.Close(); #region Get SqlserverObjectScriptBlock $SBbillcellphone={ param($hostinfos,$sqlcmd) Function Sqler_BillCellPhones {param( [array] $hostinfos ,[string] $sqlcmd ) try { $ClientSqlAccount=$hostinfos[2]; $ClientSqlPassWord=$hostinfos[3]; $ClientDB=$hostinfos[4]; $log='d:' $SqlServer=$hostinfos[0] ; $Port=$hostinfos[1] ; $SqlString="Data Source="+$SqlServer+","+$Port+";uid="+$ClientSqlAccount+";Password="+$ClientSqlPassWord; $SqlConn = [System.Data.SqlClient.SqlConnection] $SqlString; $SqlConn.Open() ; $SqlConn.ChangeDatabase($ClientDB); $CC = $SqlConn.CreateCommand(); $CC.CommandTimeout = 0; $CC.CommandText=$sqlcmd $CC.ExecuteScalar(); $SqlConn.Close(); } catch { $day=(Get-Date -Format "yyyyMMdd").tostring(); $return='Error'; ( 'Sqler_BillCellPhones : '+((Get-Date).tostring())+' '+ $SqlServer+','+$Port +' '+$_.Exception.Message )|Out-File -FilePath "$log\tab_$day.log" -Append -Force } } Sqler_BillCellPhones $hostinfos $sqlcmd } $throttleLimit=5 $sqlcmd='exec csp_billcellphone_Score ''@tabname'''; $SessionState = [system.management.automation.runspaces.initialsessionstate]::CreateDefault() $Pool = [runspacefactory]::CreateRunspacePool(1, $throttleLimit, $SessionState, $Host) $Pool.Open() $threads = @() $handles = foreach($table in $tables) { $sqlcmd='exec csp_billcellphone_Score ''@tabname'''; $sqlcmd=$sqlcmd-replace '@tabname',$table $powershell = [powershell]::Create().AddScript($SBbillcellphone).AddArgument($hostinfos).AddArgument($sqlcmd) $powershell.RunspacePool = $Pool $powershell.BeginInvoke() $threads += $powershell } do { $i = 0 $done = $true foreach ($handle in $handles) { if ($handle -ne $null) { if ($handle.IsCompleted) { $threads[$i].EndInvoke($handle) $threads[$i].Dispose() $handles[$i] = $null } else { $done = $false } } $i++ } if (-not $done) { Start-Sleep -second 1 } } until ($done) Remove-Variable -Name handles, threads,powershell; [System.GC]::Collect(); [System.GC]::WaitForPendingFinalizers()
练一技,修百艺,而成于自然.