{"id":706,"date":"2015-04-02T18:28:37","date_gmt":"2015-04-02T16:28:37","guid":{"rendered":"http:\/\/dbahire.com\/?p=706"},"modified":"2022-03-10T12:44:46","modified_gmt":"2022-03-10T11:44:46","slug":"dejad-de-usar-flush-privileges","status":"publish","type":"post","link":"https:\/\/jynus.com\/dbahire\/dejad-de-usar-flush-privileges\/","title":{"rendered":"Dejad de usar FLUSH PRIVILEGES"},"content":{"rendered":"<figure id=\"attachment_713\" aria-describedby=\"caption-attachment-713\" style=\"width: 240px\" class=\"wp-caption alignright\"><a href=\"\/wp-content\/uploads\/2015\/04\/liftarn-Mermaid.png\"><img loading=\"lazy\" decoding=\"async\" src=\"\/dbahire\/wp-content\/uploads\/2015\/04\/liftarn-Mermaid.png\" alt=\"Una sirena tiene las mismas probabilidades de arreglar tus problemas de permisos, la diferencia es que la gente contin\u00faa creyendo en el mito de FLUSH PRIVILEGES.\" class=\"size-full wp-image-713\" width=\"240\" height=\"217\"><\/a><figcaption id=\"caption-attachment-713\" class=\"wp-caption-text\">Una sirena tiene las mismas probabilidades de arreglar tus problemas de permisos, la diferencia es que la gente contin\u00faa creyendo en el mito de FLUSH PRIVILEGES.<\/figcaption><\/figure>\n<p>Cada vez que alguien escribe un tutorial o soluci\u00f3n a un problema relacionado con la creaci\u00f3n de una nueva cuentas de usuario o la provisi\u00f3n de diferentes privilegios veo la sugerencia de utilizar <code>FLUSH PRIVILEGES<\/code>. Por ejemplo, el primer post en <a href=\"https:\/\/reddit.com\/r\/mysql\">\/r\/mysql<\/a> en el momento de escribir estas l\u00edneas, <a href=\"http:\/\/kedar.nitty-witty.com\/blog\/solutions-mysql-error-1449-the-user-specified-as-a-definer-does-not-exist\">&#8220;MySQL:The user specified as a definer does not exist (error 1449)-Solutions&#8221;<\/a> es culpable m\u00faltiples veces de esto mismo (<strong>Actualizaci\u00f3n:<\/strong> el usuario ha tachado estas l\u00edneas, tras la publicaci\u00f3n de este art\u00edculo).<\/p>\n<p>No es mi intenci\u00f3n denunciar ese art\u00edculo, pero lo cierto es que he visto cometer ese error muchas, muchas veces. Incluso si os dirig\u00eds a la <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/grant.html\">p\u00e1gina del manual de referencia sobre el comando GRANT<\/a> ver\u00e9is al final -de un usuario ajeno a MySQL- usar <code>GRANT<\/code> seguido de un <code>FLUSH PRIVILEGES<\/code>.<\/p>\n<p>\u00bfPor qu\u00e9 deber\u00eda importarme? \u00bfEs <code>FLUSH PRIVILEGES<\/code> un problema? \u00bfPor qu\u00e9 lo hace todo el mundo? La raz\u00f3n de que ese comando exista es porque \u2014para mejorar el rendimiento\u2014 MySQL mantiene una copia en memoria de las tablas GRANT (de permisos de usuario). De esta manera, no necesita leerlas de disco en cada conexi\u00f3n, en cada cambio de la base de datos por defecto, y en cada consulta enviada al servidor. El comando mencionado anteriormente fuerza la recarga de esta cach\u00e9, ley\u00e9ndola directamente de disco (o de la cach\u00e9 del sistema de archivos), tal y como el manual de referencia de MySQL indica claramente (teniendo incluso su propia secci\u00f3n <em><a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/privilege-changes.html\">When Privilege Changes Take Effect<\/a><\/em> &#8220;Cu\u00e1ndo tienen efecto los cambios de privilegios&#8221;). Sin embargo, su ejecuci\u00f3n es totalmente innecesaria en la mayor\u00eda de los casos pr\u00e1cticos porque:<\/p>\n<blockquote><p><em>If you modify the grant tables indirectly using account-management statements such as <code>GRANT<\/code>, <code>REVOKE<\/code>, <code>SET PASSWORD<\/code>, or <code>RENAME USER<\/code>, the server notices these changes and loads the grant tables into memory again immediately.<\/em><\/p><\/blockquote>\n<p>que significa, m\u00e1s o menos:<\/p>\n<blockquote><p>Si modificas las tablas de privilegios de manera inderecta, usando sentencias de gesti\u00f3n de cuentas tales como <code>GRANT<\/code>, <code>REVOKE<\/code>, <code>SET PASSWORD<\/code> o <code>RENAME USER<\/code> el servidor tiene en cuenta estos cambios y carga las tablas en memoria de nuevo de manera inmediata.<\/p><\/blockquote>\n<p>La \u00fanica raz\u00f3n en la cual es necesario realizar la operaci\u00f3n de recarga manualmente es cuando:<\/p>\n<blockquote><p><em>you modify the grant tables directly using statements such as <code>INSERT<\/code>, <code>UPDATE<\/code>, or <code>DELETE<\/code><\/em> (se modifican las tablas de privilegios directamente usando sentencias como <code>INSERT<\/code>, <code>UPDATE<\/code> o <code>DELETE<\/code>)<\/p><\/blockquote>\n<p>Para la mayor\u00eda de operaciones, como crear un usuario, cambiar sus privilegios o contrase\u00f1a, es preferible usar las operaciones de alto nivel. No s\u00f3lo son m\u00e1s f\u00e1ciles de usar, tambi\u00e9n son en general compatibles con un mayor n\u00famero de versiones de MySQL y adem\u00e1s te evitar\u00e1n cometer errores (por supuesto, es necesario acordarse de configurar el modo sql <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/sql-mode.html#sqlmode_no_auto_create_user\">&#8220;<code>NO_AUTO_CREATE_USER<\/code>&#8220;<\/a>). Incluso normalmente funcionar\u00e1n sin problemas en un entorno hostil para MyISAM como es un cl\u00faster de Galera. Ciertamente, hay razones por las que puede ser necesario editar las tablas manualmente- como administradores, es posible que deseemos manipular de manera especial los privilegios o importar las tablas <code>mysql.*<\/code> de alg\u00fan otro lugar, por lo que en estos casos ejecutar <code>FLUSH PRIVILEGES<\/code> es obligatorio. Tened en cuenta que, como dice la p\u00e1gina del manual, en la mayor\u00eda de los casos (por ejemplo, privilegios globales) cambiar los permisos de un usuario s\u00f3lo afectar\u00e1 a nuevas conexiones y nunca a consultas que se est\u00e9n ejecutando ya que los privilegios se comprueban al inicio del procesado de la consulta -l\u00e9ase <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/privilege-changes.html\">el manual para los detalles<\/a>.<\/p>\n<p>As\u00ed, pues, \u00bfpor qu\u00e9 mi cruzada contra el abuso de <code>FLUSH PRIVILEGES<\/code>? Despu\u00e9s de todo, en el peor de los casos, \u00a1se recargar\u00e1n los mismos privilegios de nuevo! No es una cuesti\u00f3n de rendimiento. Aunque, en casos extremos bien podr\u00eda ser un problema. Echad un vistazo al siguiente <em>script<\/em>, que ejecuta 10 000 sentencias CREATE USER (esto s\u00f3lo se puede hacer en un \u00fanico hilo ya que las tablas grant todav\u00eda est\u00e1n en formato MyISAM, incluso en 5.7.6):<\/p>\n<pre lang=\"python\">def execute_test(port, drop, flush, thread):\n   db = mysql.connector.connect(host=\"localhost\", port=port, user=\"msandbox\", passwd=\"msandbox\", database=\"test\")\n   for i in range(0, TIMES):\n      cursor = db.cursor()\n      if (drop):\n         sql = \"DROP USER 'test-\" + `thread` + '-' + `i` + \"'@'localhost'\"\n      else:\n         sql = \"CREATE USER 'test-\" + `thread` + '-' + `i` + \"'@'localhost' IDENTIFIED BY '\" + DEFAULT_PASSWORD + \"'\"\n      cursor.execute(sql)\n      cursor.close()\n      db.commit()\n      if (flush):\n         cursor = db.cursor()\n         flush_sql = \"FLUSH PRIVILEGES\"\n         cursor.execute(flush_sql)\n         cursor.close()\n         db.commit()\n   db.close()\n<\/pre>\n<p>Las mediciones de tiempos de ambas ejecuciones son las siguientes:<\/p>\n<pre lang=\"bash\">$ time .\/test_flush.py\nNot flushing\nExecuting the command 10000 times\n\nreal    0m15.508s\nuser    0m0.827s\nsys     0m0.323s\n\n$ .\/test_flush.py -d\nNot flushing\nDropping users\nExecuting the command 10000 times\n\n$ time .\/test_flush.py -f\nFlushing after each create\nExecuting the command 10000 times\n\nreal    2m7.041s\nuser    0m2.482s\nsys     0m0.771s\n\n$ .\/test_flush.py -d\nNot flushing\nDropping users\nExecuting the command 10000 times<\/pre>\n<p>Podemos ver que <strong>usar <code>FLUSH PRIVILEGES<\/code> es 8 veces m\u00e1s lento que no utilizarlo<\/strong>. De nuevo, <strong>quiero enfatizar que el rendimiento no es el mayor de los problemas en esta situaci\u00f3n<\/strong>, ya que la mayor\u00eda de las personas lo ejecutar\u00edan tan s\u00f3lo una vez al final de cada bloque de comandos, por lo que no supondr\u00eda una gran sobrecarga. Incluso si hay una mayor cantidad de operaciones de lectura de disco, debemos asumir que todo <em>round trip<\/em> a la base de datos, y todo <em>commit<\/em> requiere ciertos recursos extra, por lo que esto mismo se puede extrapolar a cualquier comando. Adem\u00e1s, la concurrencia no suele ser un problema en la creaci\u00f3n de cuentas de usuario, ya que la tabla <code>mysql.user<\/code> no es normalmente (o no deber\u00eda) se muy din\u00e1mica.<\/p>\n<p><strong>El mayor problema contra el abuso de <code>FLUSH PRIVILEGES<\/code> es que la gente lo ejecuta sin comprender porqu\u00e9 lo hacen, y lo que realmente hace este comando<\/strong>. Cada vez que una persona tiene un problema con el sistema de privilegios de MySQL, el primer consejo dado es ejecutar dicha sentencia &#8220;por si acaso&#8221;. Si no os lo cre\u00e9is, echad un vistazo r\u00e1pido a respuestas en dba.stackexchange como <a href=\"http:\/\/dba.stackexchange.com\/questions\/36837\/mysql-user-created-via-command-line-cant-access-phpmyadmin\/36838#36838\">esta<\/a>, <a href=\"http:\/\/dba.stackexchange.com\/a\/65643\/30545\">esta<\/a> y <a href=\"http:\/\/dba.stackexchange.com\/questions\/13557\/grant-command-not-working-in-mysql-server\/13559#13559\">esta<\/a> (que he seleccionado entre las muchas existentes), cuyo usuario original no estaba alterando manualmente las tablas <code>mysql.*<\/code>. La problem\u00e1tica est\u00e1 en que la mayor parte de las veces el comando no hace absolutamente nada, y el problema subyace en la pobre comprensi\u00f3n del sistema de permisos de MySQL. Como dice el dicho en ingl\u00e9s- <strong>cuando tienes un martillo, todo problema parece un clavo<\/strong>. Los usuarios leen que esa es la manera adecuada de resolver problemas relacionados con permisos y pasan el &#8220;conocimiento&#8221; a otros usuarios, creando b\u00e1sicamente el equivalente a una mito urbano en el mundo MySQL.<\/p>\n<p>As\u00ed pues, la siguiente vez que encuentres un problema como un usuario que no puede entrar en la base de datos, o aplicarle privilegios, hay muchas otras fuentes de confusi\u00f3n, tales como: el uso de <code>old_passwords<\/code>, usar un m\u00e9todo de autenticaci\u00f3n distinto de las contrase\u00f1as nativas, no disponer de los permisos adecuados o de la propiedad <code>WITH GRANT OPTION<\/code> para transmitirlos, el servidor no detectando el usuario con el mismo nombre y host que en el que realmente est\u00e1, el uso de <code>skip-name-resolve<\/code> lo qual hace que se ignores las entradas dns, no esperar a una nueva conexi\u00f3n para que los cambios surtan efecto, &#8230; y muchas otras dificultades que van unidas a la autenticaci\u00f3n y autorizaci\u00f3n. El sistema de privilegios de MySQL no es precisamente obvio ni perfecto(<em>\u00a1Hola, otorgar permisos para bases de datos que no existen?<\/em>), pero invertir 5 minutos en leer <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/5.6\/en\/privilege-system.html\">el detallado manual sobre grants<\/a> puede ahorrar muchos quebraderos de cabeza en el futuro. <strong>TL;TR RTFM<\/strong><\/p>\n<p>Para aquellos que ya saben cu\u00e1ndo usar o no usar <code>FLUSH PRIVILEGES<\/code>, por favor, la siguiente vez que ve\u00e1is a alguien abusarlo, educar al usuario sobre mejores pr\u00e1cticas <strong>para que la gente no siga fundament\u00e1ndose en la magia y mitos urbanos para resolver problemas<\/strong>; id a reddit\/stackoverflow\/vuestra red social favorita\/etc. y votad positivamente las buenas pr\u00e1cticas\/comentad sobre las malas. Hoy podr\u00eda ser <code>FLUSH PRIVILEGES<\/code>, ma\u00f1ana puede que sea <em>&#8220;a\u00f1adir OPTIMIZE TABLE en un trabajo del cron cada 5 minutos para tus tablas InnoDB&#8221;<\/em> (y s\u00ed, esto \u00faltimo lo he encontrado en producci\u00f3n ah\u00ed fuera).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cada vez que alguien escribe un tutorial o soluci\u00f3n a un problema relacionado con la creaci\u00f3n de una nueva cuentas de usuario o la provisi\u00f3n de diferentes privilegios veo la sugerencia de utilizar FLUSH PRIVILEGES. Por ejemplo, el primer post<\/p>\n","protected":false},"author":1,"featured_media":721,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[85],"tags":[400,367,369,371,184,457,373,375,398,377,196,379,394,402,383,385,387,287,396],"class_list":["post-706","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-mysql-es","tag-conexion","tag-create-user-es","tag-flush-es","tag-grant-es","tag-myisam-es","tag-mysql-es","tag-mysql-user-es","tag-password-es","tag-permisos","tag-permissions-es","tag-primary","tag-privileges-es","tag-privilegios","tag-problema","tag-rename-user-es","tag-revoke-es","tag-set-es","tag-tabla","tag-usuario"],"_links":{"self":[{"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/posts\/706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/comments?post=706"}],"version-history":[{"count":19,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/posts\/706\/revisions"}],"predecessor-version":[{"id":1041,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/posts\/706\/revisions\/1041"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/media\/721"}],"wp:attachment":[{"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/media?parent=706"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/categories?post=706"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jynus.com\/dbahire\/wp-json\/wp\/v2\/tags?post=706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}