Страницы

Поиск по вопросам

среда, 12 декабря 2018 г.

Влияние кол-ва запросов в транзакции на производительность

Нужно ли отправлять изменения в БД одной транзакцией, или каждый INSERT, UPDATE, DELETE лучше выполнять отдельной транзакцией? Что лучше при выполнении 10 несвязанных запросов (не нужно откатывать предыдущий запрос при ошибке последующего), отправить их одной транзакцией, или каждый по отдельности? Какой вариант лучше для производительности приложения и субд?


Ответ

В общем случае ни первый ни второй вариант не являются оптимальными с точки зрения производительности.
Если нужно удалить(вставить/поменять) действительно большое количество строк, в первом случае мы получаем большие расходы на открытие, закрытие большого количества транзакций.
Во втором случае имеем слишком огромную транзакцию, которая тоже под конец работы будет долго закрываться, да ещё и рискуем упасть, т.к. размеры транзакции ограничены(видел такую ошибку всего на нескольких миллионах строк).
Как-то писал скрипт удаления "ненужных" данных. Сталкивался с обеими проблемами. Скрипт запускался на выходных, и удалял несколько десятков миллионов строк. Т.е. проблемы блокировок и прочего не волновали, в приоритете была скорость. Чтобы наглядно продемонстрировать, написал небольшой скрипт. MS SQL Server
Кратко что он делает:
T_TABLE - таблица с данными T_RESULT - таблица с результатами эксперимента P_INSERT_ROWS @Count - вставляет в таблицу T_TABLE @Count строк P_DALATE_ROWS @Count, @Pack - удаляет из таблицы T_TABLE все строки в транзакциях по @Pack штук, и записывает затраченное время в таблицу T_RESULT
Финальный скрипт запускает в цикле вставку 1 000 000 строк, и их удаления пачками по 1, 10 .. 1 000 000 штук. И затем выводит содержимое T_RESULT
Скрипт:
USE tempdb; GO IF OBJECT_ID('P_INSERT_ROWS', 'P') IS NOT NULL DROP PROC P_INSERT_ROWS; IF OBJECT_ID('P_DELETE_ROWS', 'P') IS NOT NULL DROP PROC P_DELETE_ROWS; IF OBJECT_ID('T_TABLE', 'U') IS NOT NULL DROP TABLE T_TABLE; IF OBJECT_ID('T_RESULT', 'U') IS NOT NULL DROP TABLE T_RESULT; GO CREATE TABLE T_TABLE ( id INT IDENTITY(1,1), Number INT, String NVARCHAR(4000) ) CREATE INDEX IN_T_TABLE_NUMBER ON T_TABLE(Number ASC) CREATE TABLE T_RESULT ( execute_time DATETIME, Cnt INT, Pack INT ) GO CREATE PROC P_INSERT_ROWS @Count INT AS ;WITH CTE AS( SELECT 1 N UNION ALL SELECT N+1 FROM CTE WHERE N<@Count ) INSERT T_TABLE SELECT N, LEFT(REPLICATE(N, 100),4000) FROM CTE OPTION(MAXRECURSION 0) GO CREATE PROC P_DELETE_ROWS @Count INT, @Pack INT AS DECLARE @TTT DATETIME = GETDATE(); DECLARE @NumberStart INT = 1 WHILE @NumberStart < @Count BEGIN BEGIN TRAN; DELETE FROM T_TABLE WHERE Number BETWEEN @NumberStart AND @NumberStart + @Pack - 1 OPTION(RECOMPILE); COMMIT TRAN; SET @NumberStart += @Pack; END; INSERT T_RESULT SELECT GETDATE()-@TTT execute_time, @Count Cnt, @Pack Pack GO SET NOCOUNT ON; DECLARE @Count INT = 1000000, @Pack INT = 1 WHILE @Pack <= @Count BEGIN EXEC P_INSERT_ROWS @Count EXEC P_DELETE_ROWS @Count, @Pack SET @Pack *= 10; END SELECT * FROM T_RESULT GO
Содержимое T_RESULT
execute_time Cnt Pack 1900-01-01 00:15:05.183 1000000 1 1900-01-01 00:01:56.003 1000000 10 1900-01-01 00:00:32.200 1000000 100 1900-01-01 00:00:22.370 1000000 1000 1900-01-01 00:00:21.940 1000000 10000 1900-01-01 00:00:22.663 1000000 100000 1900-01-01 00:01:10.327 1000000 1000000
Как видим, лучший результат по времени выполнения имеем, когда удаляется в одной транзакции пачками по 10000 штук, т.е. это намного эффективнее, чем удалять по одной записи в транзакции, и эффективнее, чем удалить все строки в одной транзакции. Эксперимент не строгий, запуски влияют друг на друга, но всё равно показательно.
UPD: Добавлю, что на практике, транзакции в реальной жизни не часто бывают большими. Кроме запросов вида DELETE FROM T, пожалуй.:) В этом случае да, чаще всего если есть возможность обернуть несколько действий в одну транзакцию - лучше обернуть.

Регулярное выражение для поиска мобильного телефона в тексте

Подскажите регулярное выражение для поиска номера мобильного телефона в тексте.
Номера телефонов вводятся разными пользователями и в совершенно непредсказуемом формате, например могут участвовать -+()_ пробелы и еще не известно на что хватит фантазии.
Примеры телефонов:
8 900 000-00-00
+7 (900) 000 00-00
+7 9001112233
89001112233
8(900) 111-2233
и т.д


Ответ

Думаю, примерно так:
/(\+7|8)[- _]*\(?[- _]*(\d{3}[- _]*\)?([- _]*\d){7}|\d\d[- _]*\d\d[- _]*\)?([- _]*\d){6})/g
console.log( "8 900 000-00-00\ +7 (900) 000 00-00\ +7 9001112233\ 89001112233\ 8(900) 111-2233\ 8 (1234) 12 12 12\ 8 (12-34) 12 12 12\ 8 ( 123 ) 412 12 12".match( /(\+7|8)[- _]*\(?[- _]*(\d{3}[- _]*\)?([- _]*\d){7}|\d\d[- _]*\d\d[- _]*\)?([- _]*\d){6})/g )) .as-console-wrapper.as-console-wrapper { max-height: 100vh }

Кодирование текста в произвольный двоичный код и обратно. Пример: “А” <-> “01100011”

Мне нужно преобразовать строку, например "А", в строку двоичного кода, например "01100011", потом обработать немного двоичный код, пропустив его по функции "Исключающее ИЛИ" с другим двоичным кодом, например "01100011" с "11000010", тогда выйдет "10100001", и обратно перевести получившийся код в читаемую строку, например в "Т".
При этом, я хочу использовать русскую, английскую и украинскую раскладку и цифры, со знаками припинания. Не подскажете, как это сделать на Python 3?
Вопрос состоит в том как в Python преобразовать строку в двоичный код и обратно используя любой алгоритм кодировки?


Ответ

Чтобы произвольный текст превратить в "01"-строки (биты):
def text_to_bits(text, encoding='utf-8', errors='surrogatepass'): bits = bin(int.from_bytes(text.encode(encoding, errors), 'big'))[2:] return bits.zfill(8 * ((len(bits) + 7) // 8))
И обратно:
def text_from_bits(bits, encoding='utf-8', errors='surrogatepass'): n = int(bits, 2) return n.to_bytes((n.bit_length() + 7) // 8, 'big').decode(encoding, errors) or '\0'
Пример:
>>> text_to_bits("мiр"))) '1101000010111100011010011101000110000000' >>> text_from_bits(_) мiр
См. Convert binary to ASCII and vice versa

Чтобы выполнить XOR над двумя строками, нет необходимости биты в виде ascii-строк выражать:
from itertools import cycle
def xor(message, key): return bytes(a^b for a, b in zip(message, cycle(key)))
Достаточно текст в байты закодировать, используя .encode() метод:
>>> key = b'key' >>> xor('hello world'.encode(), key) b'\x03\x00\x15\x07
Y\x1c
\x0b\x07\x01' >>> xor(_, key).decode() # и обратно 'hello world'
См. Перевести с PHP на python: XOR с ключём для строки
Для больших строк, можно numpy использовать:
import numpy as np # pip install numpy
def slow_xor(aa, bb): a = np.frombuffer(aa, dtype=np.byte) b = np.frombuffer(bb, dtype=np.byte) return np.bitwise_xor(a, b).tostring()
Пример:
>>> slow_xor(b'hello...', 'миръ'.encode()) b'\xb8\xd9\xbc\xd4\xbe\xae\xff\xa4'
Если вы хотите ускорить эту функцию, см. Simple Python Challenge: Fastest Bitwise XOR on Data Buffers

Правильное переопределение Dispose(bool disposing)?

Имеется класс, реализующий паттерн Disposable
internal class SomeDisposableClass : IDisposable { private readonly System.IO.Stream _managedResource;
private bool _disposed;
public SomeDisposableClass(System.IO.Stream managedResource) { _managedResource = managedResource; }
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
protected virtual void Dispose(bool disposing) { if(_disposed) { return; }
if(disposing) { _managedResource?.Dispose(); }
_disposed = true; } }
А есть класс, наследующийся от него:
internal sealed class DerivedDisposableClass : SomeDisposableClass { private readonly System.IO.Stream _anotherResource;
public DerivedDisposableClass(System.IO.Stream managedResource, System.IO.Stream anotherResource) : base(managedResource) { _anotherResource = anotherResource; }
protected override void Dispose(bool disposing) { _anotherResource?.Dispose();
base.Dispose(disposing); } }
Вопрос: Нужно ли в классе-наследнике вводить флаг _disposed? Если да, то какой от этого профит?
И ещё: Может кто-нибудь на пальцах объяснить, какова польза от GC.SuppressFinalize(this) в классе-родителе (при условии, что наследник будет иметь финализатор)?


Ответ

Флаг _disposed в вашем варианте наследнику нужен потому что как ему иначе определить что его уже нет?
Чтобы не заводить отдельный флаг на каждого наследника - можно немного изменить родителя.
Вариант 1. Вынесите проверку флага наружу, в невиртуальный Dispose(). В таком случае наследник будет просто знать что переопределенный Dispose(bool) будет вызван только один раз и проверка будет не нужна.
Вариант 2. Сделайте защищенное (protected) свойство IsDisposed в базовом классе.
От GC.SuppressFinalize(this) большая польза - он отменяет финализатор, что позволяет сборщику мусора собрать ваш объект за одну попытку а не за две.

Тем не менее, полный паттерн Disposable давно уже устарел. Гораздо красивее выглядит альтернативный подход, при котором считается недопустимым владение одновременно и управляемым и неуправляемым ресурсом в одном классе.
Недостатки классического паттерна Disposable:
он создает избыточную нагрузку на память потому что "тяжелые" объекты с финализаторами не могут быть собраны GC пока финализатор не отработает или не будет отменен; он вызывает утечку ресурсов при выгрузке домена приложений (а это частое явление в том же ASP.NET).
Согласно более современному подходу, обычные классы в принципе не могут владеть неуправляемыми ресурсами - а потому им не нужен ни финализатор, ни метод Dispose(bool)
Для неуправляемых же ресурсов полагается делать управляемые обертки, желательно - на основе SafeHandle, но можно и на основе CriticalFinalizerObject
Более подробно этот подход расписал VladD в вопросе Как и когда нужно имплементировать IDisposable?

C#: наблюдение за переменной другого класса

Классический пример: класс Lamp , описывающий электрическую лампочку, с состояниями On (bool on = true) и Off (bool on = false). Я хочу отслеживать значения этой переменной из другого класса (например, LightingControl), чтобы этот класс реагировал на изменения состояния лампочки. Из класса Lamp не могут быть вызываны никакие методы других классов, так как класс Lamp является моделью непрограммируемого электрического прибора. Как это реализовать?
Заготовка кода для ответа:
class Lamp(){
public bool on;
public Lamp(bool onOrOffSignal){ this.on = onOrOffSignal; }
// ... }
class LightingControl(){
Lamp lamp = new Lamp();
// ...
// это реагирование должно происходить в режиме реального времни if (Lamp.on == true){ emergencyLighting.turnOff(); } else { emergencyLighting.turnOn(); }
}


Ответ

INotifyPropertyChanged, он должен быть известен вам по экспериментам с WPF:
class Program { static void Main(string[] args) { Lamp lamp = new Lamp(); LightingControl lc = new LightingControl(lamp); lamp.IsOn = true; lamp.IsOn = false; Console.ReadKey(); } }
class Lamp : INotifyPropertyChanged { bool isOn; public bool IsOn { get => isOn; set { isOn = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsOn))); } }
public event PropertyChangedEventHandler PropertyChanged; }
class LightingControl { Lamp _lamp;
public LightingControl(Lamp lamp) { _lamp = lamp; lamp.PropertyChanged += OnLampPropertyChanged; }
private void OnLampPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == nameof(Lamp.IsOn)) { Console.WriteLine($"Lamp {(_lamp.IsOn ? "On" : "Off")}!"); } } }
Связанный вопрос: Как организовать взаимодействие объекта с его наблюдателем

Падает производительность при использования предиката для гибкой сортировки в LINQ запросе

Пытаюсь сделать LINQ запрос в котором можно менять поле (одно) по которому нужно производить сортировку. Использование переменной предиката позволяет это
public void GetMyData(string sortFieldName) { Func orderPredicate = null; switch (sortFieldName) { case "FIELD_STRING": orderPredicate = x => x.FIELD_STRING; case "FIELD_INT": orderPredicate = x => x.FIELD_INT; case "FIELD_DATE": orderPredicate = x => x.FIELD_DATE; default: orderPredicate = x => x.FIELD_DATE; }
var queryResult = db.MyEntity.OrderBy(orderPredicate);
//...some logic
}
Но резко (в десятки раз) падает производительность по сравнению с прямым заданием условия сортировки. Насколько понимаю, должно помочь Expression. Меняю тип переменной
Expression> orderPredicate = null;
Но в рантайме выдаёт исключение
Не удалось привести тип "System.DateTime" к типу "System.Object". LINQ to Entities поддерживает только приведение типов-примитивов моделей EDM или типов перечисления.
Как же быть? Ведь, если я буду использовать прямое задание условия
public void GetMyData(string sortFieldName) { var query = db.MyEntity; var orderedQuery = query.OrderBy(x => x.FIELD_DATE); switch (sortFieldName) { case "FIELD_STRING": orderedQuery = query.OrderBy(x => x.FIELD_STRING); break; case "FIELD_INT": orderedQuery = query.OrderBy(x => x.FIELD_INT); break; case "FIELD_DATE": orderedQuery = query.OrderBy(x => x.FIELD_DATE); break; }
//...some logic
}
, мне будет трудно усложнить запрос дополнительными инструкциями Where, Include и т.д.


Ответ

Проблема в том, что когда вы пишите вот так:
Expression> orderPredicate; orderPredicate = x => x.FIELD_DATE;
Компилятор делает вот так:
var _param = Expression.Parameter(typeof(MyEntity), "x"); orderPredicate = Expression.Lambda>( Expression.Convert( Expression.Property(_param, "FIELD_DATE"), typeof(object) ), _param );
Вот на этот самый Expression.Convert EF и ругается. Для того чтобы не было преобразования - возвращаемый тип делегата обязан совпадать с типом свойства, что в свою очередь означает что общую переменную orderPredicate вы использовать не можете.

Теперь как строить такие запросы. А строятся они очень просто - через накопление IQueryable
IQueryable q = db.MyEntity; switch (sortFieldName) { case "FIELD_STRING": q = q.OrderBy(x => x.FIELD_STRING); case "FIELD_INT": q = q.OrderBy(x => x.FIELD_INT); case "FIELD_DATE": q = q.OrderBy(x => x.FIELD_DATE); default: q = q.OrderBy(x => x.FIELD_DATE); }
Никакого усложнения тут нет, полученный запрос можно точно также дополнять другими условиями:
q = q.Where(x => x.Foo > 42);

PS поскольку у вас в параметре sortFieldName передается имя свойства, построение выражения можно еще немного упростить. Да, класс Queryable не дает нам вызвать OrderBy с неизвестным заранее именем свойства - но никто не мешает "раскрыть" этот метод и работать непосредственно с IQueryable
IQueryable q = db.MyEntity;
var entityParam = Expression.Parameter(typeof(MyEntity)); var propExpr = Expression.Property(entityParam, sortFieldName) q = q.Provider.CreateQuery( Expression.Call(typeof(Queryable), "OrderBy", new [] { entityParam.Type, propExpr.Type }, // Типы-параметры для метода Queryable.OrderBy<,> q.Expression, Expression.Quote(Expression.Lambda(propExpr, entityParam)) ) );
Также вместо того чтобы делать это вручную - можно использовать библиотеку System.Linq.Dynamic, скачав ее из nuget:
q = q.OrderBy(sortFieldName);

Почему operator++ должен возвращать const T?

const T T::operator++(int) { T tmp = *this; ++(*this); return tmp; }
Рассмотрим такой вариант реализации постфиксного оператора инкрементирования. Почему требуется возвращать именно const T, а не T? Такой вариант реализуется для типа int, а нужно ли мне реализовывать этот оператор для своего пользовательского типа аналогично?
Я придерживался негласного правила, что при разработке классов (типов) в случае если непонятно (или еще не сформулировано) требуемое поведение то следует организовать поведение, какое предоставляет int. int возвращает const int. А нужно ли и мне так?


Ответ

Нет необходимости делать возвращаемый тип константым, чтобы избежать двойного инкремента ++++. Достаточно объявить оператор инкремента только для lvalue объектов (добавить & в сигнатуру):
struct T { T operator++(int) & { return *this; } };
int main() { T t;
t++; // ok t++++; // ошибка }