2011-02-07 10 views
14

Nasza baza danych ma funkcję generowania numeru zamówienia. Odczytuje wartość z tabeli Ustawienia, zwiększa ją, a następnie zwraca nową wartość. Na przykład:CREATE FUNCTION error "Ta funkcja nie ma żadnego DETERMINISTYCZNEGO, BEZ SQL lub CZYTAJ DANYCH SQL"

CREATE FUNCTION NextOrderNumber() RETURNS INTEGER UNSIGNED NOT DETERMINISTIC 
BEGIN 
    DECLARE number INTEGER UNSIGNED; 
    UPDATE Settings SET IntegerValue=LAST_INSERT_ID(IntegerValue+1) WHERE KeyName='NextOrderNumber'; 
    SET number=LAST_INSERT_ID(); 
    return number; 
END 

Uwaga: Nie krytykuj tej funkcji Wiem, że ma wady, tylko dla zilustrowania.

Używamy tej funkcji w następujący sposób:

INSERT INTO Orders(OrderNumber, ...) 
SELECT NextOrderNumber(), ... 

Podczas rejestrowania binarnego jest włączona, CREATE FUNCTION daje ten błąd:

This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you might want to use the less safe log_bin_trust_function_creators variable)

Niezależnie od tego, co binlog_format jest ustawiony, to tak naprawdę problem powyższa funkcja? Zgodnie z moim odczytem odpowiedniego MySQL page nie widzę żadnego powodu, dla którego ta funkcja byłaby niezgodna z replikacją, z rejestrowaniem binarnym na poziomie ROW lub STATEMENT.

Jeśli funkcja jest bezpieczna, ustawienie globalnej wartości log_bin_trust_function_creators = 1 wywołuje u mnie niepokój. Nie chcę wyłączać tej kontroli dla wszystkich funkcji, tylko tej. Czy zamiast tego mogę po prostu zgłosić tę funkcję jako NO SQL, aby wyłączyć ostrzeżenie? Wypróbowałem to i zadziałało. Czy to spowoduje jakiś problem?

Odpowiedz

0

Pomyśl o tym, co się zapisuje w dzienniku binarnym.

Nie można zagwarantować, że zamówienie utworzone na wzorcu będzie miało taką samą sekwencję wygenerowaną dla niego, gdy transakcja będzie odtwarzana na urządzeniu podrzędnym lub, co jest o wiele bardziej prawdopodobne, przez innego wzorca w klastrze. na przykład

0) Node 1 and Node 2 are in sync, NextOrderNumber=100 
1) Node 1 receives insert statement wrt order from customer A and assigns 
    order number 100, changes its NextOrderNumber to 101 
2) Node 1 writes the settings update to the log 
3) Node 1 writes the insert statement to the log 
4) Node 2 processes for customer B, asigns order number 100 and increments 
5) Node 2 writes the settings update from to the log 
6) Node 2 writes the insert statement to the log 
7) Nodes 2 reads settings update from the log @2 
     - Its NextOrderNumber is now 102 
8) Node 2 reads insert from log @3, tries to apply it but it fails 
     due to duplicate key 
9) Node 1 reads the update @5 - Its nextOrderNumber is also now 102 
10) Node1 reads insert from log @6 - 
     but this fails due to duplicate key 

Teraz rozkazy 100 na 2 węzłów odnoszą się do różnych danych, a nie kolejność 101.

Jest powód, że nastąpiło wiele funkcjonalności dodawane do modyfikowania zachowań zmiennych AUTO_INCREMENT .

Jeśli zawiniesz wstawkę w procedurze - która pobiera wartość z generatora sekwencji, a następnie umieszcza ją w instrukcji wstawiania, natychmiastowy problem zostanie rozwiązany, jednak musisz pomyśleć o tym, jak uniknąć dwukrotnego przypisania tego samego numeru różne węzły bazy danych.

+0

symcbean - Rozumiem, że to podejście nie działa w klastrze. Nie pytam o ten scenariusz. Pytam o prostą konfigurację master-slave, w której wstawianie/aktualizowanie odbywa się tylko w jednym miejscu. – richb

24

Mam googled i jestem tutaj. Znalazłem sposób:

SET GLOBAL log_bin_trust_function_creators = 1; 

Ale uważaj, to może być niebezpieczne dla odzyskiwania danych lub replikacji ...

+8

Nie odnosi się to w żaden sposób do pytania. – richb

+0

@richb ale działa. –

5

Jak na moje zaniżenie to spowodować problem podczas odzyskiwania danych lub replikacji

Ref: http://dev.mysql.com/doc/refman/5.0/en/stored-programs-logging.html

MySQL 5.0.6: Instrukcje, które tworzą zapisane procedury i instrukcje CALL są rejestrowane. Zapisane wywołania funkcji są rejestrowane, gdy występują w instrukcjach aktualizujących dane (ponieważ te instrukcje są rejestrowane).

Jednak wywołania funkcji nie są rejestrowane, gdy występują w instrukcjach takich jak SELECT, które nie zmieniają danych, nawet jeśli zmiana danych występuje w samej funkcji; może to powodować problemy.

W pewnych okolicznościach funkcje i procedury mogą mieć różne efekty, jeśli wykonywane są w różnym czasie lub na różnych maszynach (głównym i podrzędnym), a zatem mogą być niebezpieczne dla odzyskiwania lub replikacji danych.

E.g.

CREATE FUNCTION myfunc() RETURNS INT DETERMINISTIC 
BEGIN 
    INSERT INTO t (i) VALUES(1); 
    RETURN 0; 
END; 

SELECT myfunc(); 

Jeśli funkcja wywoływana jest przechowywana w takim oświadczeniem, jak SELECT że nie modyfikuje danych, wykonywanie funkcji nie są zapisywane do dziennika binarnego, nawet jeśli sama funkcja modyfikuje danych. To zachowanie rejestrowania może potencjalnie powodować problemy. Załóżmy, że funkcja myfunc() jest zdefiniowana jak powyżej.

+0

Nie sądzę, że to jest poprawne. Patrząc na [link] (https://dev.mysql.com/doc/refman/5.7/en/stored-programs-logging.html) zobaczysz _italics _... W przypadku funkcji zapisanych, zmiany wiersza wprowadzone w funkcji są zalogowany, a nie wywołanie funkcji. W przypadku wyzwalaczy rejestrowane są zmiany wierszy wprowadzone przez wyzwalacz. ... _italics_. Tak więc dla mnie jest niewytłumaczalne, dlaczego logbin_format = ROW nadal wydaje się blokować funkcje, które nie są oznaczone jako DETERMINISTYCZNE. –

-3

wykonać to tuż przed utworzeniem funkcję:

SET @@global.log_bin_trust_function_creators = 1; 

I dodać MODIFIES SQL DATA do zgłoszenia.

Również ... cóż, poprosiłeś o nie komentowanie samej funkcji, ale proponuję, abyś rzucił zmienną number i po prostu zrobił RETURN LAST_INSERT_ID().

+1

To nie działa. log_bin_trust_function_creators jest GLOBALNYM, a nie zmienną sesji. – richb

+0

Przepraszam, że to była literówka. Edytowałem swoją odpowiedź. –

+0

Huh? Nie wpływa to na zachowanie globalne? Tak to jest. Ustawiasz zmienną globalną. – richb

1

Istnieją dwa sposoby, aby rozwiązać ten problem:

wykonać następujące polecenia w konsoli MySQL:

SET GLOBAL log_bin_trust_function_creators = 1; 

Dodaj poniższe linie do pliku konfiguracyjnego mysql.ini:

log_bin_trust_function_creators = 1 

The ustawienie rozluźnia sprawdzanie funkcji niedeterministycznych. Niedeterministyczne funkcje są funkcjami, które modyfikują dane (to znaczy mają instrukcję aktualizacji, wstawiania lub usuwania). Aby uzyskać więcej informacji, zobacz tutaj.

Należy pamiętać, że jeśli rejestracja binarna NIE jest włączona, to ustawienie nie ma zastosowania.

+0

Widzę ten błąd, gdy wybieram tabelę z widoku. jakie może być rozwiązanie? – sat

0

Could I instead just flag the function as NO SQL to suppress the warning? I tried it and it worked. Will this cause any problem?

Według tego Mysql doc:

Assessment of the nature of a function is based on the “honesty” of the creator: MySQL does not check that a function declared DETERMINISTIC is free of statements that produce nondeterministic results.

Więc to zależy od ciebie. Jeśli jesteś pewien, że metoda nie spowoduje żadnego problemu ...