Logitech DinovoEdge unter (K)Ubuntu „zum laufen bringen“

Eine kurze Anleitung, wie man die Medientasten der DinovoEdge unter Linux/X11 und KDE/XFCE und Gnome zum laufen bringt.

Wenn man eine DinovoEdge an einen mit (K)Ubuntu betrieben wird feststellen, dass die Tastatur und das TouchPad zwar funktionieren aber die netten Zusatztasten nicht. Das hat zwei Gründe:

Bei ersterem muss man abwarten, bei letzerem kann man Abhilfe schaffen. Dazu braucht man einen „KeyLogger“ wie xev. Führt man diesen auf der Konsole aus, kann mich sich anschauen, welche Tasten welche KeyCodes auslösen.

Hat man erstmal dadurch eine Liste von KeyCodes zusammengestellt muss man diese auf die „Medientasten“ mappen. Dazu gibt es zwei Möglichkeiten.

  • Eine eigenes Tastaturlayout erstellen und dem X11 über die xorg.conf zuweisen. Diese Variante ist die „sauberste“ aber auch die mit dem meisten „bloat“. Wer es dennoch durchziehen will, sollte hier mal nachlesen.
  • Oder man kann mittels Xmodmap das aktuelle Tastaturlayout erweitern. Das ist unter Ubuntu besonders einfach, da dies automatisch vom System durchgeführt werden kann.

Um letzteres umzusetzen muss man einfach in seinem Home-Verzeichniss eine Datei mit dem namen .Xmodmap anlegen. Diese wird automatisch nach dem Login geladen und „bearbeitet“ das aktuelle Tastaturlayout. Für die DinovoEdge reicht diese Datei.

keycode 225 = XF86Search
keycode 163 = XF86Mail
keycode 180 = XF86HomePage
keycode 173 = XF86AudioPrev
keycode 174 = XF86AudioStop
keycode 172 = XF86AudioPlay
keycode 171 = XF86AudioNext
keycode 121 = XF86AudioMute
keycode 179 = XF86AudioRecord
keycode 150 = XF86PowerDown

Die Syntax sollte selbst erklärend sein. Wenn man andere Tasten zuordnen möchte, kann man die datei /usr/share/X11/XKeysymDB konsultieren, dort sind alle „möglichen“ Zuordnung hinterlegt.Hat man die Datei angelegt, kann man mittels folgendem Befehl, die Änderungen „im System Bekannt machen“

xmodmap .Xmodmap 

Danach kann man diese Tasten in Fenstermanager seiner Wahl funktionen zuweisen. Nach einem Neustart/Login wird diese Änderung automatisch angewendet.

SQL Execution Plan Caching

Bei einem TSQL-Server kann es von Zeit zu Zeit zu „seltsamen“ Performance-Einbrüchen kommen. Dieser Posting beschäftige sich mit einem der Gründe: SQL – Execution Plan Caching.

Bei modernen Datenbanken wird die Schicht der Datenabfrage von der Schicht der Datenbeschaffung getrennt. Hatte früher die Formulierung eines SQL-Befehls Einfluss auf die Art wie das DBMS die Daten beschafft, ist der gleicher Befehl heute eher als „Wunsch“ welche Daten zu selektieren sind. Wie diese schlussendlich beschafft werden, bleibt dem DBMS überlassen. Der Sprung von SQL-Statement auf tatsächliche I/O-Operationen wird gemeinhin als „Execution Plan“ bezeichnet.

Ein solcher ExecutionPlan wird für jede Abfrage erzeugt die an das DBMS gestellt wird. Bei der Erstellungen eines solchen Plans werden alle Informationen die dem DBMS zur Verfügung stehen (Statistiken, Datenbankstruktur, etc) für die Optimierung der Datenabfrage genutzt. Es werden Suchalgorithmen und Daten-Aggregations–Funktionen ausgewählt. Anschließend wird der ExecutionPlan ausgeführt und die Abfrage beantwortet. Um bei Wiederholung einer Abfrage Zeit zu sparen, wird ein einmal erzeugter ExecutionPlan gecached und später wiederverwendet.

Um zu sehen welche ExecutionPlans gerade im Cache vorgehalten werden, kann folgender Befehl genutzt werden.

SELECT [cp].[refcounts]
, [cp].[usecounts]
, [cp].[objtype]
, [st].[dbid]
, [st].[objectid]
, [st].[text]
, [qp].[query_plan]
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_sql_text ( cp.plan_handle ) st
CROSS APPLY sys.dm_exec_query_plan ( cp.plan_handle ) qp ;

Wie bei allen Cache und Statistik-Funktionen, gibt es eine Kehrseite der Medaille. Es kann passieren, dass all die schöne Optimierung ins Lehre läuft oder ein Plan verwendet wird, der für die aktuelle Abfrage völlig ungeeignet ist. Wie schon bei dem TOP-Problem dreht sich auch hier alles darum, dass das DBMS den „falschen“ ExecutionPlan benutzt.

„Falsch“ bedeutet in diesem Zusammenhang, dass I/O – Operatoren benutzt werden, die für die anfallenden Datenmengen nicht optimal sind. Dafür gibt es normalerweise nur drei Gründe:

  • Man weißt das DBMS dazu an: Es gibt mehrere Möglichkeiten das DBMS auf die falsche Fährte zu schicken. Entweder zwingt man es mittels QueryHints dazu oder gibt gleiche ganze ExecutionPlans vor.
  • Die Statistik ist falsch: der unwahrscheinlichste Fall, sei aber dennoch Aufgeführt. Ändert sich bei einer Tabelle mehr als 20%+500 Datensätze (Update, Delete, Insert) wird die Statistik der Tabelle neu erzeugt. Bei 10000 Datensätzen müssten sich also 2500 Datensätze ändern, bis es zu einem Update in der Statistik gibt. Im schlimmsten Fall muss sich nur ein Datensatz ändern um bei einem Folge-Join zu einer explodierenden Datenmenge zu sorgen. In-the-Wild ist mir ein solches „Szenario“ aber noch nie unter gekommen.
  • Parameter Sniffing/Spoofing + ExecutionPlan Caching: Für sich alleine genommen ist jede der beiden Technologien sinnvoll und hilfreich. In Kombination kann es jedoch zu erheblichen „Verklemmungen“ kommen. „Parameter Sniffing“ bezeichnet den Versuch des Query-Optimizers einen Execution Plan auf einen bestimmten Parameter-Tupel zu optimieren. Wird die Abfrage immer nur mit den gleichen Parametern ausgeführt oder nur einmal, beschleunigt das die Abfrage. Sorgt einer der Parameter z.B. dafür das eine große Tabelle auf wenige Datensätze reduziert wird, kann sich so die Lead-Tabelle ändern. Nachteilig wird das ganze wenn dieser Plan gecached wird. Bei der zweiten Ausführung der Abfrage wird der alte Execution Plan genutzt. Im schlimmsten Fall ist dieser falsch optimiert und führt zu unnötigen I/O – Operationen.

Gegen falsche Statistiken lässt sich recht einfach vorgehen. Man veranlasst einen Rebuild der entsprechenden Statistik oder erzeugt gleich alle Statistiken neu.

UPDATE STATISTICS  -- erneuert nur die Statistik der angegeben Tabelle

exec sp_updatestats -- baut alle Statistiken neu

Mit einem Statistik-Rebuild werden auch alle gecachten Execution-Pläne verworfen die von der Statistik abhängen.

Gegen Parameter-Sniffing hilft das leider nur temporär. Abfragen die mehrere „optimale“ Execution Pläne haben, sollten gesondert behandelt werden. Es gibt prinzipiell fünf Möglichkeiten das Problem endgültig zu beheben.

  • der „sicherste“ weg: Wenn eine Abfrage in unterschiedlichen Kontexten genutzt wird, dupliziert man die Abfrage und ändert die Signatur ein wenig. Somit werden mehrere Abfragen gestellt, die getrennt optimiert werden.
  • Kann man das Problem in eine StoredProcedure auslagern, kann man dort ein „ConditionalTree“ aufbauen. Also die Übergabeparameter analysieren und entsprechende Sub-StoredProcedures aufrufen. Die wiederum auf den speziellen Fall optimiert werden.
  • Man kann auch das Parameter Sniffing unterdrücken. Dazu muss man aber ebenfalls auf Stored Procedures zurückgreifen.
    CREATE PROCEDURE SelectSomething
    		@parameter1 int,
    		@parameter2 char
    DECLARE
    	@parameter1_save int,
    	@parameter2_save char
    AS
    	SET @parameter1_save = @parameter1
    	SET @parameter2_save = @parameter2
    	
    	SELECT * 
    	  FROM tabelle1 t1
    	  JOIN tabelle2 t2
    	 WHERE t1.s1 = @parameter1_save
    	   AND t2.s1 = @parameter2_save
    

    Die Parameterzuweisung kann vom Optimizer nicht vorhergesehen werden, deswegen wird der anschließende Select auf den allgemeinen Anwendungsfall optimiert.

  • Kann man den Anwendungsbereich eines SELECTs von vornherein einschränken oder eingrenzen, kann man QueryOptimizer anweisen auf diesen Bereich hin zu optimieren.
    SELECT * 
      FROM tabelle1 t1
      JOIN tabelle2 t2
     WHERE t1.s1 = @parameter1
       AND t2.s1 = @parameter2
    OPTION (OPTIMIZE FOR(@parameter1 = ‘somevalue’,@parameter2 = ‘somevalue’ ));
    
  • Es gibt fälle wo alle bisher genannten Lösungsvarianten nicht greifen, da die Abfragen nicht getrennt werden können und die einzelnen Abfrage-Ziele so unterschiedlich sind, dass eine allgemeine Optimierung keinen Vorteil bringt. In diesem Fall kann man das DBMS anweisen, den ExecutionPlan nicht zu cachen bzw keine gecachten Plane zu nutzen.

    SELECT * 
      FROM tabelle1 t1
      JOIN tabelle2 t2
     WHERE t1.s1 = @parameter1
       AND t2.s1 = @parameter2
    OPTION (RECOMPILE)
    

    In diesem Fall wird für diesen SELECT immer ein neuer ExecutionPlan erzeugt und nach der Ausführung sofort verworfen. So wird immer der „optimale“ ExecutionPlan genutzt. Dafür geht jetzt immer Zeit für das Erzeugen des Plans drauf.