Maison  >  Article  >  développement back-end  >  Exemple de configuration de séparation en lecture-écriture de la base de données Yii2

Exemple de configuration de séparation en lecture-écriture de la base de données Yii2

高洛峰
高洛峰original
2017-02-15 15:39:071177parcourir

Pour commencer à utiliser la base de données, vous devez d'abord configurer le composant de connexion à la base de données. Ceci est réalisé en ajoutant le composant db à la configuration de l'application (l'application Web "de base" est config/web.PHP (Data Source). Name) est le nom de la source de données, utilisé pour spécifier les informations de la base de données :

return [
  // ...
  'components' => [
    // ...
    'db' => [
      'class' => 'yii\db\Connection',
      'dsn' => 'mysql:host=localhost;dbname=mydatabase', // MySQL, MariaDB
      //'dsn' => 'sqlite:/path/to/database/file', // SQLite
      //'dsn' => 'pgsql:host=localhost;port=5432;dbname=mydatabase', // PostgreSQL
      //'dsn' => 'cubrid:dbname=demodb;host=localhost;port=33000', // CUBRID
      //'dsn' => 'sqlsrv:Server=localhost;Database=mydatabase', // MS SQL Server, sqlsrv driver
      //'dsn' => 'dblib:host=localhost;dbname=mydatabase', // MS SQL Server, dblib driver
      //'dsn' => 'mssql:host=localhost;dbname=mydatabase', // MS SQL Server, mssql driver
      //'dsn' => 'oci:dbname=//localhost:1521/mydatabase', // Oracle
      'username' => 'root', //数据库用户名
      'password' => '', //数据库密码
      'charset' => 'utf8',
    ],
  ],
  // ...
];

Veuillez vous référer au manuel PHP pour plus d'informations sur le. Format DSN. Après avoir configuré le composant de connexion, il est accessible en utilisant la syntaxe suivante :

$connection = \Yii::$app->db;

Veuillez vous référer à [[yiidbConnection]] pour une liste des propriétés configurables . Si vous souhaitez vous connecter à la base de données via ODBC, vous devez configurer l'attribut [[yiidbConnection::driverName]], par exemple :

'db' => [
  'class' => 'yii\db\Connection',
  'driverName' => 'mysql',
  'dsn' => 'odbc:Driver={MySQL};Server=localhost;Database=test',
  'username' => 'root',
  'password' => '',
],

Remarque : Si vous devez les utiliser en même temps. Plusieurs composants de connexion peuvent être définis pour plusieurs bases de données :

return [
  // ...
  'components' => [
    // ...
    'db' => [
      'class' => 'yii\db\Connection',
      'dsn' => 'mysql:host=localhost;dbname=mydatabase', 
      'username' => 'root',
      'password' => '',
      'charset' => 'utf8',
    ],
    'secondDb' => [
      'class' => 'yii\db\Connection',
      'dsn' => 'sqlite:/path/to/database/file', 
    ],
  ],
  // ...
];

est utilisé dans le code du manières suivantes :

$primaryConnection = \Yii::$app->db;
$secondaryConnection = \Yii::$app->secondDb;

Si vous ne souhaitez pas définir la connexion à la base de données comme composant global de l'application, vous pouvez l'initialiser directement dans le code :

$connection = new \yii\db\Connection([
  'dsn' => $dsn,
   'username' => $username,
   'password' => $password,
]);
$connection->open();

Petite astuce : Si vous devez effectuer des requêtes SQL supplémentaires après avoir créé la connexion, vous pouvez ajouter le code suivant au fichier de configuration de l'application :

return [
  // ...
  'components' => [
    // ...
    'db' => [
      'class' => 'yii\db\Connection',
      // ...
      'on afterOpen' => function($event) {
        $event->sender->createCommand("SET time_zone = 'UTC'")->execute();
      }
    ],
  ],
  // ...
];

Requête de base SQL

Une fois que vous avez une instance de connexion, vous pouvez exécuter une requête SQL via [[yiidbCommand]].

Requête SELECT

La requête renvoie plusieurs lignes :

$command = $connection->createCommand('SELECT * FROM post');
$posts = $command->queryAll();

Renvoie une seule ligne :

$command = $connection->createCommand('SELECT * FROM post WHERE id=1');
$post = $command->queryOne();

Requête d'une valeur unique sur plusieurs lignes :

$command = $connection->createCommand('SELECT title FROM post');
$titles = $command->queryColumn();

Requête valeur scalaire/valeur calculée :

$command = $connection->createCommand('SELECT COUNT(*) FROM post');
$postCount = $command->queryScalar();

UPDATE, INSERT, DELETE update, insert and delete, etc.

Si vous exécutez SQL, aucune donnée ne sera renvoyée Vous pouvez utiliser la méthode d'exécution dans la commande :

$command = $connection->createCommand('UPDATE post SET status=1 WHERE id=1');
$command->execute();

Vous pouvez utiliser les méthodes d'insertion, de mise à jour et de suppression , qui générera du SQL approprié en fonction des paramètres et les exécutera.

// INSERT
$connection->createCommand()->insert('user', [
  'name' => 'Sam',
  'age' => 30,
])->execute();

// INSERT 一次插入多行
$connection->createCommand()->batchInsert('user', ['name', 'age'], [
  ['Tom', 30],
  ['Jane', 20],
  ['Linda', 25],
])->execute();

// UPDATE
$connection->createCommand()->update('user', ['status' => 1], 'age > 30')->execute();

// DELETE
$connection->createCommand()->delete('user', 'status = 0')->execute();

Noms de table et de colonne référencés

.

La plupart du temps, la syntaxe suivante est utilisée pour citer en toute sécurité les noms de tables et de colonnes :

$sql = "SELECT COUNT([[$column]]) FROM {{table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();

Le code ci-dessus [[$column]] sera converti pour faire référence au nom de colonne approprié, et {{table}} Il est converti en référence au nom de table approprié. Le nom de la table a une variable spéciale {{%Y}} Si un préfixe de table est défini, utilisez cette variante pour ajouter automatiquement un préfixe avant le nom de la table :

$sql = "SELECT COUNT([[$column]]) FROM {{%$table}}";
$rowCount = $connection->createCommand($sql)->queryScalar();

Si le préfixe de la table est défini dans le fichier de configuration comme suit, le code ci-dessus interrogera les résultats dans la table tbl_table :

return [
  // ...
  'components' => [
    // ...
    'db' => [
      // ...
      'tablePrefix' => 'tbl_',
    ],
  ],
];

Citer manuellement le nom de la table et le nom de la colonne. Une autre option consiste à utiliser [[yiidbConnection::quoteTableName()]] et [[yiidbConnection::quoteColumnName()]] :

$column = $connection->quoteColumnName($column);
$table = $connection->quoteTableName($table);
$sql = "SELECT COUNT($column) FROM $table";
$rowCount = $connection->createCommand($sql)->queryScalar();

Instructions de prétraitement

Les instructions de prétraitement peuvent être utilisées pour transmettre en toute sécurité les paramètres de requête. Tout d'abord, vous devez utiliser : espace réservé, puis lier la variable. à l'espace réservé correspondant :

$command = $connection->createCommand('SELECT * FROM post WHERE id=:id');
$command->bindValue(':id', $_GET['id']);
$post = $command->query();

Une autre utilisation consiste à préparer une instruction préparée une fois et à exécuter plusieurs requêtes :

$command = $connection->createCommand('DELETE FROM post WHERE id=:id');
$command->bindParam(':id', $id);

$id = 1;
$command->execute();

$id = 2;
$command->execute();

Astuce, il est plus efficace de lier les variables avant l'exécution, puis de changer la valeur de la variable à chaque exécution (généralement utilisée dans les boucles).


Transactions

Lorsque vous devez exécuter plusieurs requêtes associées de manière séquentielle, vous pouvez les encapsuler dans une transaction pour protéger la cohérence des données. Yii fournit une interface simple pour implémenter les opérations de transaction SQL. déclaration comme suit :

$transaction = $connection->beginTransaction();
try {
  $connection->createCommand($sql1)->execute();
   $connection->createCommand($sql2)->execute();
  // ... 执行其他 SQL 语句 ...
  $transaction->commit();
} catch(Exception $e) {
  $transaction->rollBack();
}

Nous démarrons une transaction via [[yiidbConnection::beginTransaction()|beginTransaction()]] et interceptons l'exception via try catch. Lorsque l'exécution est réussie, soumettez la transaction et terminez-la via [[yiidbTransaction::commit()|commit()]]. ]] pour annuler la transaction.

Vous pouvez également imbriquer plusieurs transactions si nécessaire : ​​

// 外部事务
$transaction1 = $connection->beginTransaction();
try {
  $connection->createCommand($sql1)->execute();

  // 内部事务
  $transaction2 = $connection->beginTransaction();
  try {
    $connection->createCommand($sql2)->execute();
    $transaction2->commit();
  } catch (Exception $e) {
    $transaction2->rollBack();
  }

  $transaction1->commit();
} catch (Exception $e) {
  $transaction1->rollBack();
}

Notez que la base de données que vous utilisez doit prendre en charge les points de sauvegarde pour s'exécuter correctement. Le code ci-dessus fonctionne dans toutes les données relationnelles. Il peut être exécuté dans les deux, mais la sécurité ne peut être garantie qu'en prenant en charge les points de sauvegarde.


Yii prend également en charge la définition de niveaux d'isolement pour les transactions. Lors de l'exécution d'une transaction, le niveau d'isolement par défaut de la base de données sera utilisé. Vous pouvez également spécifier le niveau d'isolement pour les éléments suivants. niveaux d'isolement couramment utilisés Niveau

[[yiidbTransaction::READ_UNCOMMITTED]] - Permet de lire des données modifiées non validées, ce qui peut entraîner des lectures sales, des lectures non répétables et des lectures fantômes


[ [ yiidbTransaction::READ_COMMITTED]] - Permet la lecture après la validation des transactions simultanées, ce qui peut éviter des lectures sales, qui peuvent conduire à des lectures répétées et des lectures fantômes.


[[yiidbTransaction::REPEATABLE_READ]] - Plusieurs lectures du même champ ont des résultats cohérents, ce qui peut conduire à des lectures fantômes.


[[yiidbTransaction::SERIALIZABLE]] - Obéit entièrement au principe ACID pour garantir qu'aucune lecture sale, non répétable ou fantôme ne se produise.


Vous pouvez utiliser les constantes ci-dessus ou utiliser une commande de chaîne et exécuter la commande dans la base de données correspondante pour définir le niveau d'isolement. Par exemple, la commande efficace pour postgres est SERIALIZABLE READ ONLY DEFERRABLE.

注意:某些数据库只能针对连接来设置事务隔离级别,所以你必须要为连接明确制定隔离级别.目前受影响的数据库:MSSQL SQLite

注意:SQLite 只支持两种事务隔离级别,所以你只能设置READ UNCOMMITTED 和 SERIALIZABLE.使用其他隔离级别会抛出异常.

注意:PostgreSQL 不允许在事务开始前设置隔离级别,所以你不能在事务开始时指定隔离级别.你可以在事务开始之后调用[[yii\db\Transaction::setIsolationLevel()]] 来设置.

数据库复制和读写分离

很多数据库支持数据库复制 database replication来提高可用性和响应速度. 在数据库复制中,数据总是从主服务器 到 从服务器. 所有的插入和更新等写操作在主服务器执行,而读操作在从服务器执行.

通过配置[[yii\db\Connection]]可以实现数据库复制和读写分离.

[
  'class' => 'yii\db\Connection',

  // 配置主服务器
  'dsn' => 'dsn for master server',
  'username' => 'master',
  'password' => '',

  // 配置从服务器
  'slaveConfig' => [
    'username' => 'slave',
    'password' => '',
    'attributes' => [
      // use a smaller connection timeout
      PDO::ATTR_TIMEOUT => 10,
    ],
  ],

  // 配置从服务器组
  'slaves' => [
    ['dsn' => 'dsn for slave server 1'],
    ['dsn' => 'dsn for slave server 2'],
    ['dsn' => 'dsn for slave server 3'],
    ['dsn' => 'dsn for slave server 4'],
  ],
]

以上的配置实现了一主多从的结构,从服务器用以执行读查询,主服务器执行写入查询,读写分离的功能由后台代码自动完成.调用者无须关心.例如:

// 使用以上配置创建数据库连接对象
$db = Yii::createObject($config);

// 通过从服务器执行查询操作
$rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();

// 通过主服务器执行更新操作
$db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute();

注意:通过[[yii\db\Command::execute()]] 执行的查询被认为是写操作,所有使用[[yii\db\Command]]来执行的其他查询方法被认为是读操作.你可以通过$db->slave得到当前正在使用能够的从服务器.

Connection组件支持从服务器的负载均衡和故障转移,当第一次执行读查询时,会随即选择一个从服务器进行连接,如果连接失败则又选择另一个,如果所有从服务器都不可用,则会连接主服务器。你可以配置[[yii\db\Connection::serverStatusCache|server status cache]]来记住那些不能连接的从服务器,使Yii 在一段时间[[yii\db\Connection::serverRetryInterval].内不会重复尝试连接那些根本不可用的从服务器.

注意:在上述配置中,每个从服务器连接超时时间被指定为10s. 如果在10s内不能连接,则被认为该服务器已经挂掉.你也可以自定义超时参数.
你也可以配置多主多从的结构,例如:

[
  'class' => 'yii\db\Connection',

  // 配置主服务器
  'masterConfig' => [
    'username' => 'master',
    'password' => '',
    'attributes' => [
      // use a smaller connection timeout
      PDO::ATTR_TIMEOUT => 10,
    ],
  ],

  // 配置主服务器组
  'masters' => [
    ['dsn' => 'dsn for master server 1'],
    ['dsn' => 'dsn for master server 2'],
  ],

  // 配置从服务器
  'slaveConfig' => [
    'username' => 'slave',
    'password' => '',
    'attributes' => [
      // use a smaller connection timeout
      PDO::ATTR_TIMEOUT => 10,
    ],
  ],

  // 配置从服务器组
  'slaves' => [
    ['dsn' => 'dsn for slave server 1'],
    ['dsn' => 'dsn for slave server 2'],
    ['dsn' => 'dsn for slave server 3'],
    ['dsn' => 'dsn for slave server 4'],
  ],
]

上述配置制定了2个主服务器和4个从服务器.Connection组件也支持主服务器的负载均衡和故障转移,与从服务器不同的是,如果所有主服务器都不可用,则会抛出异常.

注意:当你使用[[yii\db\Connection::masters|masters]]来配置一个或多个主服务器时,Connection中关于数据库连接的其他属性(例如:dsn, username, password)都会被忽略.

事务默认使用主服务器的连接,并且在事务执行中的所有操作都会使用主服务器的连接,例如:

// 在主服务器连接上开始事务
$transaction = $db->beginTransaction();

try {
  // 所有的查询都在主服务器上执行
  $rows = $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();
  $db->createCommand("UPDATE user SET username='demo' WHERE id=1")->execute();

  $transaction->commit();
} catch(\Exception $e) {
  $transaction->rollBack();
  throw $e;
}

如果你想在从服务器上执行事务操作则必须要明确地指定,比如:

$transaction = $db->slave->beginTransaction();

有时你想强制使用主服务器来执行读查询,你可以调用seMaster()方法.

$rows = $db->useMaster(function ($db) {
  return $db->createCommand('SELECT * FROM user LIMIT 10')->queryAll();
});

你也可以设置$db->enableSlaves 为false来使所有查询都在主服务器上执行.

操作数据库模式

获得模式信息

你可以通过 [[yii\db\Schema]]实例来获取Schema信息:

$schema = $connection->getSchema();

该实例包括一系列方法来检索数据库多方面的信息:

$tables = $schema->getTableNames();

更多信息请参考[[yii\db\Schema]]

修改模式

除了基础的 SQL 查询,[[yii\db\Command]]还包括一系列方法来修改数据库模式:

  • 创建/重命名/删除/清空表

  • 增加/重命名/删除/修改字段

  • 增加/删除主键

  • 增加/删除外键

  • 创建/删除索引

使用示例:

// 创建表
$connection->createCommand()->createTable('post', [
  'id' => 'pk',
  'title' => 'string',
  'text' => 'text',
]);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHP中文网。

更多yii2 数据库读写分离配置示例相关文章请关注PHP中文网!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn