2010-06-22 8 views
1

현재 응용 프로그램의 경우 데이터베이스 변수를 비즈니스 로직에서 멀리 유지하고 데이터베이스 코드를 쉽게 바꿀 수 있으므로 모든 데이터베이스 기능을 단일 클래스에 배치하기로 선택했습니다. 다른 DBMS. 그러나 최근에 내 데이터베이스 클래스는 상당히 커졌으며 (정보 편집 : 약 53k), 일반적으로 각 요청에 대해 구문 분석되어야하기 때문에 볼륨으로 인해이 파일을 구문 분석하는 속도가 걱정됩니다.PHP : 하나의 거대한 데이터베이스 클래스 또는 여러 개의 작은 클래스?

보통 한 번에 하나의 데이터베이스 호출 (예 : 사용자 시스템 호출, 자산 시스템 호출, 시스템 호출 시스템 호출, 세션 시스템 호출 등)이 한 가지 또는 두 가지 유형으로 이루어 지므로 하나의 옵션 필자는 작업을 일련의 데이터베이스 개체 "조각"으로 분해 한 다음 함수 요청을 기반으로 런타임에 동적으로로드하는 방법을 고려했습니다.

다른 한편으로 나는 이렇게하는 것이 (a) 메모리에서 많은 양의 병렬 실행 (즉, 각 슬라이스에 이제 쿼리 메서드, 독립 쿼리 로그 등이 있음) 기존의 모든 코드를 수정하여 새로운 작은 객체를 가리 키도록 강요하거나 (b) 이미 작성된 코드로 작업하기 위해이 기능을 백 해킹 (back-hack) 할 때 상대적인 성능 손실을 야기합니다 부모의 쿼리 함수뿐만 아니라 직접 메서드 액세스 대신 장소 전체에서 __call을 갑자기 사용하여 발생하는 실적 적중률)에 영향을줍니다.

이 시나리오에서 더 정확한 행동 방침은 무엇입니까?

EDIT 추가 정보 :이 파일은 읽기 쉽도록 확장 된 SQL 모델을 사용하므로 비뚤어진 것으로 간주 될 수 있지만 현재 약 2,350 줄의 파일이 약 53kb입니다 (아직 완료되지 않았습니다).

SELECT 
    foo, 
    bar, 
    baz 
FROM 
    someTable st 
    LEFT JOIN someOtherTable sot 
     ON st.id = sot.stId 
WHERE 
    cond > otherCond 

70 개 쿼리 기능, 나는이 개 놀랄만큼 비슷한 결과 세트가 필요한 경우 단순히 내가 각각의 시간이 필요하지 않는 것을 무시하고 같은 쿼리를 다시 사용할 수 있습니다 (약간의 중복과 함께, 몇 가지 독특한 작업을 수행하는 각각이 있습니다).

편집 : 예 기능 :

public function alarm_getActiveAlarmsByAsset($tier, $id) { 
    if ( !Redacted::checkNumber($tier, $id) 
     || $id < 0 
     || $tier > Redacted::ASSET_LOWEST_TIER 
     || $tier < Redacted::ASSET_TIER_CUSTOMER 
    ) { 
     return false; 
    } 

    $sql = " 
     SELECT 
      alarmId, 
      alarmTime, 
      server, 
      source, 
      reason, 
      train, 
      server_sites.siteId AS `siteId` 
     FROM 
      alarm_alarms 
    "; 

    $join = ''; 

    switch ($tier) { 
     case Redacted::ASSET_TIER_CUSTOMER: 
      $join = ' 
       LEFT JOIN red_campus 
        ON red_campus.campId = red_site.campId 
      '; 
     case Redacted::ASSET_TIER_CAMPUS: 
      $join = ' 
       LEFT JOIN red_site 
        ON red_site.siteId = server_sites.siteId 
      ' . $join; 
     case Redacted::ASSET_TIER_SITE: 
      $join = ' 
       LEFT JOIN server_sites 
        ON server_sites.servId = alarm_alarms.server 
      ' . $join; 
    } 
    $table = isset(self::$dbTierMap[$tier + 1]) ? self::$dbTierMap[$tier + 1]['table'] : 'server_sites'; 
    $field = isset(self::$dbTierMap[$tier + 1]) ? self::$dbTierMap[$tier + 1]['parent'] : 'site'; 
    $sql .= $join . " 
     WHERE 
       ackId IS NULL 
      AND {$table}.{$field}Id = {$id} 
    "; 

    $r = $this->query($sql); 

    if (!$r) { 
     return false; 
    } 

    $alarms = array(); 
    while ($alarm = mysql_fetch_assoc($r)) { 
     $alarms[] = $alarm; 
    } 
    return $alarms; 
} 
+0

"다소 큰"크기가 크고 코드를 더 잘 재사용하여 내부적으로 클래스를 최적화 할 가능성이 없습니까? – deceze

+0

~ 53kb, ~ 2350 lines, 비록 내가 확장 된 SQL을 사용하면 linecount가 비뚤어져 보일 수 있지만, 포스트를 업데이트 할 것입니다. – Dereleased

+0

응용 프로그램이 모든 고유 쿼리에 대해 별도의 기능을 가지고 있음을 올바르게 알고 있습니까? – Mewp

답변

0

파싱 시간과 코드 품질에 대해 걱정이된다면 일부 컴파일러 캐시, 예 : APC .

큰 클래스가있는 경우 가독성을 위해 작은 클래스로 리팩토링해야 할 가능성이 높습니다. 수업에서 필요한 것을 쉽게 찾을 수 없다면 (예를 들어 그것을 수정하기 위해), 유지하기에는 너무 큰 것입니다.
리팩터링하기로 결정했다면 유지 관리 가능성과 코드 품질이 에서보다 훨씬 더 빠릅니다 (데이터베이스 I/O는 코드 실행보다 느립니다).

+0

53k 바이트가 라인이 아니지만, 당신이 무엇을 얻고 있는지 이해합니다. 이 시점에서 모든 것이 여전히 찾아 내고 다루기 쉽습니다. 내 IDE를 통해 모든 기능 진입 점에 대한 빠른 링크를 제공하고 이름에 접두사가 붙은 시스템을 기준으로 접두어가 붙습니다. APC에 관한 "코드 품질이 아닌"의견에 대해 궁금합니다. 우리는 바이트 코드 은닉처; 리팩터링을 시도하거나 다른 것을 수정하지 않고 APC를 반창고로 사용한다고 가정하십니까? – Dereleased

+0

예, 이해가 정확합니다.즉, 코드에 만족하고 속도를 원한다면 APC가 해결책이 될 수 있습니다. – Mewp

0

의 DB 방식을 알지 못하고 말할 수 있지만 항상 쉽게 여러 클래스 하나의 큰 것보다 (예를 들어 논리적 개체 당 하나 개의 클래스/DB 테이블)

을 유지하기 어렵다

데이터베이스 (및 모든 네트워크 요청)에 대한 요청이 항상 오래 걸리므로 구문 분석 시간에 대해 걱정할 필요가 없습니다.

+0

나는 거의 아무것도 걱정하지 않는다는 것을 확신합니다. 하지만 이것은 중간 크기의 앱부터 고 가용성의 대규모 앱에 이르기까지 나의 첫 걸음입니다. – Dereleased

+1

첫 번째 단계라면 조기 최적화가 모든 악의 근원임을 기억하십시오. – Mewp

1

일반적으로 많은 작은 클래스를 유지하는 것이 더 쉽습니다.(주로 PHP와 함께 사용) MySQL을위한 몇 가지 클래스 발전기를 사용할 수있다 :

sourceforge: php Class Generator - PCG

sourceforge: PHP Class Generator

은 아마 당신이 새로운 아이디어를 찾을 수 있습니다.

+4

제 생각에는 ** 코드 생성기를 사용하지 마십시오. 좋은 라이브러리를 사용하는 것이 코드를 생성하는 것보다 낫습니다. 기계가 생성 한 코드는 읽을 수있는 경우에도 빠르게 유지 보수 할 수 없게됩니다. 또한, [DRY] (http://en.wikipedia.org/wiki/Don%27t_repeat_yourself)가 심각하게 손상됩니다. – Mewp

4

내가 아는 한, 데이터베이스 클래스는 기본적으로 가능한 전체 쿼리를 보유하고 있으며, 전체 응용 프로그램에서 만들어진 하드 코드 된 것입니까?

데이터베이스 계층 추상화를 달성하는 더 좋은 방법은 쿼리를 내부적으로 일관된 형식으로 추상화하고 모델과 데이터베이스 어댑터를 사용하여이를 실제 SQL로 변환하는 것입니다. 예 : 특정 테이블에

$customer = $Customer->find(array(
    'fields' => array('name', 'id'), 
    'condition' => array('ssn' => $ssn) 
)); 

고객 클래스 맵 및 다른 컬럼에 스키마 매핑 할 수 있습니다 필요한 경우 :

class Customer extends BaseModel { 
    public $table = 'xe_a3_cstmr'; 
    public $schema = array(
     'name' => 'ze_42_nm', 
     … 
    ); 
} 

BASEMODEL은 어떤 데이터베이스에 따라 실제 SQL에 이러한 추상화 된 쿼리를 설정하거나 해제 할 수

SELECT `xe_a3_cstmr`.`ze_42_nm`, `xe_a3_cstmr`.`…` FROM `xe_a3_cstmr` 
WHERE `xe_a3_cstmr`.`ssn` = 123235; 

데이터베이스 층은 앱에서 확인해야합니다 모든 새 쿼리 기하 급수적으로 증가하지 않습니다이 방법은, 여전히 maintaing하면서 유연성 : 그것은 이야기 할 필요가 쿼리를 다른 데이터베이스에 매핑합니다. 이를 ORM이라고합니다. 이것은 실제로 훨씬 더 쉬운 추상화를 제공한다는 것은 말할 것도 없습니다. 하드 코딩 된 모든 쿼리를 다시 작성해야하는 대신 다른 데이터베이스 어댑터 만 작성하면됩니다.

메모에 Mewp가 제안 했으므로 시작할 수있는 좋은 곳은 Doctrine입니다.

+0

더 정확하게는 빌드해야 할 수도있는 각 유형의 쿼리가 들어 있습니다. 때로는 단순한 정적 인 쿼리이며 다른 시간 쿼리는 관련 정보를 기반으로 구성됩니다. 예를 들어 0에서 3까지의 계층에 존재할 수있는 계층 적 자산 의미에서 동일한 함수가 자산 쿼리를 생성하는 데 사용됩니다 당면한 테이블의 의미 론적 지식에 기초한 모든 수준에서; 내 게시물에 예제를 편집 해 보겠습니다. – Dereleased

+0

지금은 바이트 코드 캐싱 메커니즘을 사용할 것입니다. 효율성을 높이기 위해 쿼리를 튜닝하는데 많은 시간을 소비했기 때문에 쿼리 생성기에 내 쿼리를 전달하는 것에 조심스럽게 일했습니다. 나는 이런 종류의 시스템을 조사 할 것이고 미래의 날짜와 시간에 그것을 포함하는 것을 고려할 것입니다. 그러나 지금 당장 먹고있는 가장 큰 두려움은 원시 성능입니다. 나는 새로운 추상화 레이어를 추가하는 것이 올바른 방법이라고 생각하지 않습니다. 이것을 해결하기 위해. – Dereleased

+0

@Dereleased 충분히 그렇듯이 항상 그렇듯이 성능과 유연성 (그리고 유지 보수성) 사이에는 절충점이 있습니다. ORM은 엄청난 유연성을 제공하지만 물론 성능이 저하 될 수 있습니다. 그것이 당신의 주요 관심사라면, 당신 자신의 질의를 만드는 것이 실제로 더 나은 선택 일 수 있습니다. 어쨌든 "일상적인 쿼리"는 성능에 큰 타격을 입지 않을 것이며 하드 케이스에 대한 전문화 된 수작업 쿼리를 계속 유지할 수 있습니다. 쿼리 생성 프로세스 자체가 적절한 캐싱으로 빨라질 수 있습니다. – deceze

관련 문제