10тыс пользователей * 1 млн строк по 200 байт = 2ТБ - максимальный размер базы - великовато для MySQL, но работать будет даже в лоб.
Раз строки повторяются, то нужно сделать словарь строк, и оперировать уже id строки.
Раз таблица пользователь-строки может оказаться очень длинной и ее изменение будет приносить большие задержки. То есть смысл резделить таблицу с информацией о строках пользователей на несколько таблиц (партиционирование), разделив весь пул пользователей по конкретным таблицам, чем больше таблиц - тем легче будет проходить обновления.
итого имеем такую структуру:
таблица users,
в которой id пользователя, некое внешнее описание пользователя, номер/имя таблицы с данными
таблица dict,
в которой храним уникальные строки и их id
пачка таблиц usersdata1...N,
в которых храним id пользователя и id строки, если у пользователя строки могут повторяться - то уникальный key id, чтобы хранить дубликаты строк и вытягивать их в порядке key id
чем больше N - тем веселее будут проходить изменения.
Выборка видится такой - ищем в users пользователя, берем его id и знание какую таблицу userdata надо опрашивать, затем выбрать из userdata список id строк, сразу подтягивая строки из dict.
Выбор таблицы можно делать, не храня данные о таблицах, например, по первым символам хеша "имени пользователя". Но такой принцип делает фиксированным количество таблиц userdata, это может оказаться не очень гибким способом для последующих изменений.
Потом такую базу можно перенести на raid из ssd, чтобы чтение происходило с минимальными задержками на чтение.
Если захочется еще повысить производительность, то userdataN можно размазать на разные хосты. При этом таблицы dict и users реплицировать между хостами средствами mysql.
Можно и миллионы пользователей так обслуживать, имя соответствующий парк серверов.