← Back to team overview

maria-developers team mailing list archive

Re: dfa33fb309d: MDEV-26664 Store UUIDs in a more efficient manner

 

Hi, Alexander!

On Oct 19, Alexander Barkov wrote:
> revision-id: dfa33fb309d (mariadb-10.6.1-84-gdfa33fb309d)
> parent(s): 5cfb31854a4
> author: Alexander Barkov
> committer: Alexander Barkov
> timestamp: 2021-10-19 13:27:19 +0400
> message:
> 
> MDEV-26664 Store UUIDs in a more efficient manner
> 
> UUID values
> 
>   llllllll-mmmm-Vhhh-vsss-nnnnnnnnnnnn
> 
> are now stored as
> 
>   nnnnnnnnnnnn-vsss-Vhhh-mmmm-llllllll
> 
> inside the record:
> 
> - the groups (segments separated by dash) are reordered right-to-left.
> - the bytes inside the groups are not reordered (stored as before,
>   in big-endian format).
> 
> This provides a better sorting order: the earlier UUID was generated,
> the higher it appears in the ORDER BY output.
> 
> Also, this change enables a good key prefix compression,
> because the constant part is now in the beginning, while
> the non-constant part (the timestamp) is in the end.
> 
> diff --git a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result
> index 6a08ffe8c23..6e2e1cc87ef 100644
> --- a/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result
> +++ b/plugin/type_uuid/mysql-test/type_uuid/type_uuid.result
> @@ -596,10 +596,10 @@ INSERT INTO t1 VALUES (CAST(CONCAT('2','0000000-0000-0000-0000-000000000003') AS
>  SELECT * FROM t1 ORDER BY a;
>  a
>  10000000-0000-0000-0000-000000000001
> -10000000-0000-0000-0000-000000000002
> -10000000-0000-0000-0000-000000000003
>  20000000-0000-0000-0000-000000000001
> +10000000-0000-0000-0000-000000000002
>  20000000-0000-0000-0000-000000000002
> +10000000-0000-0000-0000-000000000003
>  20000000-0000-0000-0000-000000000003

please, add a test for `ORDER BY CONCAT(a)` just to test the
lexicographical ordering of UUIDs. Or may be `ORDER BY HEX(a)` ?
Or `UNHEX` ? Whatever the recommended approach should be.

>  DROP TABLE t1;
>  #
> diff --git a/plugin/type_uuid/mysql-test/type_uuid/type_uuid_memory.result b/plugin/type_uuid/mysql-test/type_uuid/type_uuid_memory.result
> index 42bb74d4b01..eb7a51895b9 100644
> --- a/plugin/type_uuid/mysql-test/type_uuid/type_uuid_memory.result
> +++ b/plugin/type_uuid/mysql-test/type_uuid/type_uuid_memory.result
> @@ -25,7 +25,7 @@ a
>  00000000-0000-0000-0000-0000000000ff
>  EXPLAIN SELECT * FROM t1 WHERE a='00000000-0000-0000-0000-0000000000ff';
>  id	select_type	table	type	possible_keys	key	key_len	ref	rows	Extra
> -1	SIMPLE	t1	ref	a	a	17	const	2	Using where
> +1	SIMPLE	t1	ref	a	a	17	const	4	Using where

why?

> diff --git a/plugin/type_uuid/sql_type_uuid.h b/plugin/type_uuid/sql_type_uuid.h
> index 9076c874e98..be9fea8ebc9 100644
> --- a/plugin/type_uuid/sql_type_uuid.h
> +++ b/plugin/type_uuid/sql_type_uuid.h
> @@ -24,8 +24,162 @@ class UUID: public FixedBinTypeStorage<MY_UUID_SIZE, MY_UUID_STRING_LENGTH>
...
> +  // Compare two in-memory values
> +  static int cmp(const LEX_CSTRING &a, const LEX_CSTRING &b)
> +  {

this is way more LoC that I would've used.
but ok, it's your code and it's a plugin, so as you like
  
> diff --git a/sql/sql_string.h b/sql/sql_string.h
> index fe57c8153bb..795f80c3e08 100644
> --- a/sql/sql_string.h
> +++ b/sql/sql_string.h
> @@ -484,6 +484,11 @@ class Binary_string: public Sql_alloc
>      if (str.Alloced_length)
>        Alloced_length= (uint32) (str.Alloced_length - offset);
>    }
> +  LEX_CSTRING to_lex_cstring() const
> +  {
> +    LEX_CSTRING tmp= {Ptr, str_length};
> +    return tmp;
> +  }
>    inline LEX_CSTRING *get_value(LEX_CSTRING *res)

may be you could remove get_value()? In a separate commit.
the name is quite bad and the method makes rather little sense,
especially if there's to_lex_cstring() now.

>    {
>      res->str=    Ptr;
> diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h
> index c6e3d20bcfa..5141cb9fad4 100644
> --- a/sql/sql_type_fixedbin.h
> +++ b/sql/sql_type_fixedbin.h
> @@ -154,18 +160,13 @@ class FixedBinTypeBundle
>                                               FbtImpl::max_char_length()+1));
>        return false;
>      }
> -    int cmp(const char *str, size_t length) const
> -    {
> -      DBUG_ASSERT(length == sizeof(m_buffer));
> -      return memcmp(m_buffer, str, length);
> -    }
>      int cmp(const Binary_string &other) const

what is this one used for?

>      {
> -      return cmp(other.ptr(), other.length());
> +      return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring());
>      }
>      int cmp(const Fbt &other) const
>      {
> -      return memcmp(m_buffer, other.m_buffer, sizeof(m_buffer));
> +      return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring());
>      }
>    };
>  
> diff --git a/sql/sql_type_fixedbin_storage.h b/sql/sql_type_fixedbin_storage.h
> index 0e8ac81ab59..6e18335bd4c 100644
> --- a/sql/sql_type_fixedbin_storage.h
> +++ b/sql/sql_type_fixedbin_storage.h
> @@ -21,6 +21,38 @@
>    format and their own (variable size) canonical string representation.
>  
>    Examples are INET6 and UUID types.
> +
> +  The MariaDB server uses three binary representations of a data type:
> +
> +  1. In-memory binary representation (user visible)
> +    This representation:
> +    - can be used in INSERT..VALUES (X'AABBCC')
> +    - can be used in WHERE conditions: WHERE c1=X'AABBCC'
> +    - is returned by CAST(x AS BINARY(N))
> +    - is returned by Field::val_native() and Item::val_native()
> +
> +  2. In-record binary representation (user invisible)
> +    This representation:
> +    - is used in records (is pointed by Field::ptr)
> +    - must be comparable by memcmp()
> +
> +  3. Binlog binary (row) representation
> +    Usually, for string data types the binlog representation
> +    is based on the in-record representation with trailing byte compression:
> +    - trailing space compression for text string data types
> +    - trailing zero compression for binary string data types
> +
> +  We have to have separate in-memory and in-record representations
> +  because we use HA_KEYTYPE_BINARY for indexing. The engine API
> +  does not have a way to pass a comparison function as a parameter.
> +
> +  The default implementation below assumes that:
> +  - the in-memory and in-record representations are equal
> +  - the binlog representation is compatible with BINARY(N)
> +  This is OK for simple data types, like INET6.
> +
> +  Data type implementations that need different representations
> +  can override the default implementation (like e.g. UUID does).
>  */

thanks, this is very good

>  
>  /***********************************************************************/
> @@ -64,5 +137,37 @@ class FixedBinTypeStorage
>      return true;
>    }
>  
> +  static ulong KEY_pack_flags(uint column_nr)
> +  {
> +    /*
> +      Return zero by default. A particular data type can override
> +      this method return some flags, e.g. HA_PACK_KEY to enable
> +      key prefix compression.

what if you change it later, will it make old tables corrupted?

> +    */
> +    return 0;
> +  }

Regards,
Sergei
VP of MariaDB Server Engineering
and security@xxxxxxxxxxx


Follow ups