В нашей онлайн-среде распространенной ситуацией является необходимость разделения главного и подчиненного устройств. Каковы преимущества разделения «главный-подчиненный» и как им соответствовать, не является предметом нашего обучения. Но что вам нужно знать, так это то, что Laravel и все современные фреймворки могут легко настроить разделение главных и подчиненных устройств. Кроме того, нам нужно вернуться к построителю запросов, чтобы увидеть, как генерируется ассемблерный синтаксис нашего собственного оператора SQL.
На самом деле конфигурация очень проста, давайте сначала кратко рассмотрим. После этого мы покопаемся в исходном коде, чтобы увидеть, как он пишет в главную библиотеку и читает из подчиненной библиотеки.
'mysql2' => [
'driver' => 'mysql',
'read' => [
'host'=>[
'192.168.56.101'
]
],
'write' => [
'host'=>[
env('DB_HOST', '127.0.0.1'),
]
],
'url' => env('DATABASE_URL'),
// 'host' => env('DB_HOST', '127.0.0.1'),
'port' => env('DB_PORT', '3306'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'sticky' => true,
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
Вот измененный файл config/database.php. Как видите, отличие от исходной конфигурации в том, что мы закомментировали исходные хосты, а затем добавили чтение и запись. В этих двух атрибутах хосты можно указывать в виде массива. Таким образом, наши операторы запроса и операторы добавления, удаления и изменения разделены. Оператор запроса будет следовать конфигурации чтения, а другие операторы будут следовать конфигурации записи. В то же время мы также добавили еще один стикер и установили для него значение true. Его функция заключается в том, что в том же запросе, если выполняются операции добавления, удаления и изменения, последующий запрос также будет использовать запись, которая является запросом основной базы данных. Это также связано с тем, что в некоторых компаниях нам необходимо запрашивать данные сразу после обработки данных. Задержка между главным и подчиненным устройствами может привести к тому, что запрошенные подчиненные данные будут неверными (это очень часто встречается в реальном бизнесе). Таким образом, если сразу после операции добавления, удаления и изменения возникает запрос, наш текущий процесс запроса все равно продолжит запрашивать основную базу данных.
Далее мы определяем два маршрута для тестирования.
Route::get('ms/test/insert', function(){
\Illuminate\Support\Facades\DB::connection('mysql2')->table('db_test')->insert(['name'=>'Lily', 'sex'=>2]);
dd( \Illuminate\Support\Facades\DB::connection('mysql2')->table('db_test')->get()->toArray());
});
Route::get('ms/test/list', function(){
dd( \Illuminate\Support\Facades\DB::connection('mysql2')->table('db_test')->get()->toArray());
});
После выполнения первого маршрута мы увидим вновь добавленные данные в данных, напечатанных dd(). Затем запросите второй маршрут, и вы обнаружите, что данные остались прежними и новые данные не были добавлены. Поскольку мы не настроили синхронизацию «главный-подчиненный» в MySQL, это также сделано для облегчения отладки и просмотра. Очевидно, что оператор запроса второго маршрута отправляется в другую базу данных.
Что касается того, как добиться разделения чтения и записи, мы рассмотрим метод select() собственного запроса. Найдите метод select() в laravel/framework/src/Illuminate/Database/Connection.php, и вы увидите, что у него также есть третий параметр.
public function select($query, $bindings = [], $useReadPdo = true)
{
return $this->run($query, $bindings, function ($query, $bindings) use ($useReadPdo) {
if ($this->pretending()) {
return [];
}
// For select statements, we'll simply execute the query and return an array
// of the database result set. Each element in the array will be a single
// row from the database table, and will either be an array or objects.
$statement = $this->prepared(
$this->getPdoForSelect($useReadPdo)->prepare($query)
);
$this->bindValues($statement, $this->prepareBindings($bindings));
$statement->execute();
return $statement->fetchAll();
});
}
protected function getPdoForSelect($useReadPdo = true)
{
return $useReadPdo ? $this->getReadPdo() : $this->getPdo();
}
Параметр $useReadPdo по умолчанию имеет истинное значение. Внутри тела метода метод getPdoForSelect() использует этот параметр. Продолжим смотреть вниз.
public function getReadPdo()
{
if ($this->transactions > 0) {
return $this->getPdo();
}
if ($this->recordsModified && $this->getConfig('sticky')) {
return $this->getPdo();
}
if ($this->readPdo instanceof Closure) {
return $this->readPdo = call_user_func($this->readPdo);
}
return $this->readPdo ?: $this->getPdo();
}
// $this->readPdo laravel/framework/src/Illuminate/Database/Connectors/ConnectionFactory.php createPdoResolverWithHosts
В этом методе больше делать нечего. Главное — использовать. call_user_func() позвонить этому this->readPdo метод, вы можете увидеть это здесь
public function setReadPdo($pdo)
{
$this->readPdo = $pdo;
return $this;
}
Затем мы отслеживаем резервную копию и переходим непосредственно к классу фабрики соединений в laravel/framework/src/Illuminate/Database/Connectors/ConnectionFactory.php и обнаруживаем, что метод setReadPdo() вызывается в методе createReadWriteConnection().
public function make(array $config, $name = null)
{
$config = $this->parseConfig($config, $name);
if (isset($config['read'])) {
return $this->createReadWriteConnection($config);
}
return $this->createSingleConnection($config);
}
protected function createReadWriteConnection(array $config)
{
$connection = $this->createSingleConnection($this->getWriteConfig($config));
return $connection->setReadPdo($this->createReadPdo($config));
}
protected function createReadWriteConnection(array $config)
{
$connection = $this->createSingleConnection($this->getWriteConfig($config));
return $connection->setReadPdo($this->createReadPdo($config));
}
protected function createReadPdo(array $config)
{
return $this->createPdoResolver($this->getReadConfig($config));
}
protected function createPdoResolver(array $config)
{
return array_key_exists('host', $config)
? $this->createPdoResolverWithHosts($config)
: $this->createPdoResolverWithoutHosts($config);
}
protected function getReadConfig(array $config)
{
return $this->mergeReadWriteConfig(
$config, $this->getReadWriteConfig($config, 'read')
);
}
protected function getReadWriteConfig(array $config, $type)
{
return isset($config[$type][0])
? Arr::random($config[$type])
: $config[$type];
}
Очевидно, что при создании соединения тело метода make() вызывает метод createReadWriteConnection() в зависимости от того, прочитала ли конфигурационный файл конфигурацию. Затем, следуя опубликованному мной коду, вы можете увидеть, что если есть конфигурация чтения, то основное соединение будет создано с использованием конфигурации записи, а затем будет вызван метод setReadPdo() основного соединения и будет создано подчиненное соединение. на основе конфигурации чтения базы данных. Главный объект — это наш объект соединения для записи, а объект соединения для чтения — его дочерний объект.
В методе createPdoResolver() мы видим использование метода createPdoResolverWithHosts(), найденного выше, который генерирует функцию обратного вызова. Теперь каждый должен знать правду. Если вы еще не разобрались, то можете самостоятельно поставить точки останова и выполнить отладку. Ведь расположение кода и файлов указано.
Отсюда мы видим, что Laravel определяет, использовать ли подключение к подчиненной библиотеке для запроса на основе параметров. Я раньше видел исходный код других фреймворков. Будь то Yii или TP, это оценивается на основе того, имеет ли оператор запроса. ВЫБЕРИТЕ символ. Также очень интересно поискать по базе данных. Вы можете изучить ее самостоятельно.
После разговора о соединениях давайте вернемся и поговорим об очень важной вещи в соединениях с базой данных, а именно о том, как генерируются операторы SQL. Здесь используется высокий словарь синтаксиса. На самом деле, простое понимание заключается в том, как построитель запросов генерирует операторы SQL. Излишне говорить, что в случае собственного запроса мы можем просто написать оператор SQL самостоятельно и позволить PDO его выполнить. Однако построитель запросов и Eloquent ORM верхнего уровня завершают запрос к базе данных после создания объектов в объектно-ориентированной цепочке, как упоминалось ранее. Среди них должен быть процесс генерации операторов SQL, о чем мы и узнаем дальше.
На самом деле мы построитель запросов Я видел это в той статье Laravel как создать SQL утверждение, помните то, которое мы анализировали update() Метод? Если вы не помните, вы можете вернуться и посмотреть. 【Laravelряд4.2】построитель запросовhttps://mp.weixin.qq.com/s/vUImsLTpEtELgdCTWI6k2A . в исполнении update() При работе мы наконец вошли laravel/framework/src/Illuminate/Database/Query/Grammars/Grammar.php в этом объекте. Судя по названию, это грамматика объект. Этот объект будет отвечать за склеивание реального SQL заявление. Например, позвольте мне взглянуть еще раз insert() наконец прибыл compileInsert() метод.
public function compileInsert(Builder $query, array $values)
{
// Essentially we will force every insert to be treated as a batch insert which
// simply makes creating the SQL easier for us since we can utilize the same
// basic routine regardless of an amount of records given to us to insert.
$table = $this->wrapTable($query->from);
if (empty($values)) {
return "insert into {$table} default values";
}
if (! is_array(reset($values))) {
$values = [$values];
}
$columns = $this->columnize(array_keys(reset($values)));
// We need to build a list of parameter place-holders of values that are bound
// to the query. Each insert should have the exact same amount of parameter
// bindings so we will loop through the record and parameterize them all.
$parameters = collect($values)->map(function ($record) {
return '('.$this->parameterize($record).')';
})->implode(', ');
return "insert into $table ($columns) values $parameters";
}
Вот что наконец вернулось SQL Оператор будет передан на соединение, то есть laravel/framework/src/Illuminate/Database/Connection.php в insert() метод для выполнения. Это тот, который мы впервые научились использовать из-за Собственника. запрос называется изметод. Далее давайте посмотрим. get() метод, то есть получить набор результатов запроса изметод.существовать Builder в, получить() Метод вызовет runSelect() метод, этот метод вызовет другой toSql() Метод заключается в получении исходного изметода запроса запроса.
public function toSql()
{
return $this->grammar->compileSelect($this);
}
Как видите, toSql() Вызывается повторно в объекте грамматики compileSelect() метод.
public function compileSelect(Builder $query)
{
if ($query->unions && $query->aggregate) {
return $this->compileUnionAggregate($query);
}
// If the query does not have any columns set, we'll set the columns to the
// * character to just get all of the columns from the database. Then we
// can build the query and concatenate all the pieces together as one.
$original = $query->columns;
if (is_null($query->columns)) {
$query->columns = ['*'];
}
// To compile the query, we'll spin through each component of the query and
// see if that component exists. If it does we'll just call the compiler
// function for the component which is responsible for making the SQL.
$sql = trim($this->concatenate(
$this->compileComponents($query))
);
if ($query->unions) {
$sql = $this->wrapUnion($sql).' '.$this->compileUnions($query);
}
$query->columns = $original;
return $sql;
}
Среди них основные SELECT Соединение предложений происходит в compileComponents() Завершено в,Давайте продолжим и займемся этимметод.
protected function compileComponents(Builder $query)
{
$sql = [];
foreach ($this->selectComponents as $component) {
if (isset($query->$component)) {
$method = 'compile'.ucfirst($component);
$sql[$component] = $this->$method($query, $query->$component);
}
}
return $sql;
}
Кажется, немного неясно? Что делает этот цикл? Фактически, из кода мы видим, что он пересекает локальное свойство. selectComponents и вызывает себя на основе содержимого этого атрибута. Давайте проверим. selectComponents Атрибут обнаружит, что это серия предварительной информации для имен методов.
protected $selectComponents = [
'aggregate',
'columns',
'from',
'joins',
'wheres',
'groups',
'havings',
'orders',
'limit',
'offset',
'lock',
];
Результатом сращивания в цикле является compileAggregate() 、compileColumns() .... Мы все можем найти эту серию методов, эту кучу методов, существующую в настоящее время в этом файле грамматики. Каждый метод требует передачи дополнительных параметров. query->
protected function concatenate($segments)
{
return implode(' ', array_filter($segments, function ($value) {
return (string) $value !== '';
}));
}
Что вы хотите знать о том, где находятся условия и операторы соединения в этих методах compileWheres() и compileJoins(). Я не буду публиковать здесь код, остальное вам предстоит узнать самостоятельно!
Сегодняшний контент на самом деле относительно простой. Laravel Содержимое базы данных сосредоточено на том, что было изучено ранее. Модель и построитель запросов начальство. Что касается баз данных master-slave, то они, как правило, широко используются в средних и крупных бизнес-проектах, и принцип их реализации на самом деле не сложен. и генерация грамматики Здесь мы в основном рассматриваем генерацию оператора запроса. грамматики, по сравнению с добавлениями, удалениями и изменениями, оператор запроса сохраняется в существовании. where/join/order by/group by и другие функции, поэтому будет сложнее. Конечно, на самом деле все сложнее. ещесуществоватьв конструкторе,после всегосуществоватьгенерация грамматики Это фактически финальный этап сборки. Заинтересованные студенты могут изучить более углубленно. Builder Вышеуказанные функции и методы реализованы в объекте. Полагаю, что после данной серии исследований содержимое этого файла вам уже не незнакомо, а также считаю, что оставшийся контент вы сможете самостоятельно проанализировать самостоятельно. Позже мы выучим еще две простые изибаза. контент, связанный с данными, а именно настройки атрибутов транзакций и PDO, и Redis Простой в использовании.
Справочная документация:
https://learnku.com/docs/laravel/8.x/database/9400#e05dce